diff --git a/include/logger.hpp b/include/logger.hpp index 82d90410..e021a685 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -2,6 +2,10 @@ #include #include +#ifdef __ANDROID__ +#include +#endif + namespace Log { // Our logger class template @@ -12,7 +16,11 @@ namespace Log { std::va_list args; va_start(args, fmt); +#ifdef __ANDROID__ + __android_log_vprint(ANDROID_LOG_DEFAULT, "Panda3DS", fmt, args); +#else std::vprintf(fmt, args); +#endif va_end(args); } }; @@ -81,4 +89,4 @@ namespace Log { #else #define MAKE_LOG_FUNCTION(functionName, logger) MAKE_LOG_FUNCTION_USER(functionName, logger) #endif -} +} \ No newline at end of file diff --git a/src/jni_driver.cpp b/src/jni_driver.cpp index 6eeb727a..d962f23e 100644 --- a/src/jni_driver.cpp +++ b/src/jni_driver.cpp @@ -35,6 +35,13 @@ JNIEnv* jniEnv() { extern "C" { +#define MAKE_SETTING(functionName, type, settingName) \ +AlberFunction(void, functionName) (JNIEnv* env, jobject obj, type value) { emulator->getConfig().settingName = value; } + +MAKE_SETTING(setShaderJitEnabled, jboolean, shaderJitEnabled) + +#undef MAKE_SETTING + AlberFunction(void, Setup)(JNIEnv* env, jobject obj) { env->GetJavaVM(&jvm); } AlberFunction(void, Pause)(JNIEnv* env, jobject obj) { emulator->pause(); } AlberFunction(void, Resume)(JNIEnv* env, jobject obj) { emulator->resume(); } diff --git a/src/pandroid/app/src/main/AndroidManifest.xml b/src/pandroid/app/src/main/AndroidManifest.xml index b6276493..c66d37af 100644 --- a/src/pandroid/app/src/main/AndroidManifest.xml +++ b/src/pandroid/app/src/main/AndroidManifest.xml @@ -48,5 +48,7 @@ + + diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/AlberDriver.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/AlberDriver.java index 5cff703c..00b7842b 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/AlberDriver.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/AlberDriver.java @@ -22,5 +22,7 @@ public class AlberDriver { public static native void LoadLuaScript(String script); public static native byte[] GetSmdh(); + public static native void setShaderJitEnabled(boolean enable); + static { System.loadLibrary("Alber"); } } \ No newline at end of file diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java index aced6faa..f7050e99 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java @@ -21,6 +21,7 @@ import com.panda3ds.pandroid.input.InputMap; import com.panda3ds.pandroid.utils.Constants; import com.panda3ds.pandroid.view.PandaGlSurfaceView; import com.panda3ds.pandroid.view.PandaLayoutController; +import com.panda3ds.pandroid.view.utils.PerformanceView; public class GameActivity extends BaseActivity { private final DrawerFragment drawerFragment = new DrawerFragment(); @@ -56,6 +57,11 @@ public class GameActivity extends BaseActivity { ((CheckBox) findViewById(R.id.hide_screen_controller)).setChecked(GlobalConfig.get(GlobalConfig.KEY_SCREEN_GAMEPAD_VISIBLE)); getSupportFragmentManager().beginTransaction().replace(R.id.drawer_fragment, drawerFragment).commitNow(); + + if (GlobalConfig.get(GlobalConfig.KEY_SHOW_PERFORMANCE_OVERLAY)) { + PerformanceView view = new PerformanceView(this); + ((FrameLayout) findViewById(R.id.panda_gl_frame)).addView(view, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + } } @Override diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/PandroidApplication.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/PandroidApplication.java index 02fbbbcc..b0cdc935 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/PandroidApplication.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/PandroidApplication.java @@ -2,11 +2,13 @@ package com.panda3ds.pandroid.app; import android.app.Application; import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; import com.panda3ds.pandroid.AlberDriver; import com.panda3ds.pandroid.R; +import com.panda3ds.pandroid.app.services.LoggerService; import com.panda3ds.pandroid.data.config.GlobalConfig; import com.panda3ds.pandroid.input.InputMap; import com.panda3ds.pandroid.utils.GameUtils; @@ -24,6 +26,10 @@ public class PandroidApplication extends Application { GameUtils.initialize(); InputMap.initialize(); AlberDriver.Setup(); + + if (GlobalConfig.get(GlobalConfig.KEY_LOGGER_SERVICE)) { + startService(new Intent(this, LoggerService.class)); + } } public static int getThemeId() { diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/base/BasePreferenceFragment.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/base/BasePreferenceFragment.java index 3cd28f4b..9482df1d 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/base/BasePreferenceFragment.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/base/BasePreferenceFragment.java @@ -1,8 +1,13 @@ package com.panda3ds.pandroid.app.base; import android.annotation.SuppressLint; + +import androidx.annotation.StringRes; +import androidx.appcompat.app.AppCompatActivity; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.SwitchPreference; + import com.panda3ds.pandroid.lang.Function; @@ -15,4 +20,8 @@ public abstract class BasePreferenceFragment extends PreferenceFragmentCompat { return false; }); } + + protected void setActivityTitle(@StringRes int titleId) { + ((AppCompatActivity) requireActivity()).getSupportActionBar().setTitle(titleId); + } } diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/main/SettingsFragment.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/main/SettingsFragment.java index b3bebf8f..bfe33a2b 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/main/SettingsFragment.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/main/SettingsFragment.java @@ -8,6 +8,7 @@ import com.panda3ds.pandroid.R; import com.panda3ds.pandroid.app.PreferenceActivity; import com.panda3ds.pandroid.app.base.BasePreferenceFragment; import com.panda3ds.pandroid.app.preferences.AppearancePreferences; +import com.panda3ds.pandroid.app.preferences.DeveloperPreferences; import com.panda3ds.pandroid.app.preferences.InputPreferences; public class SettingsFragment extends BasePreferenceFragment { @@ -16,5 +17,6 @@ public class SettingsFragment extends BasePreferenceFragment { setPreferencesFromResource(R.xml.start_preferences, rootKey); setItemClick("input", (item) -> PreferenceActivity.launch(requireContext(), InputPreferences.class)); setItemClick("appearance", (item)-> PreferenceActivity.launch(requireContext(), AppearancePreferences.class)); + setItemClick("developer", (item)-> PreferenceActivity.launch(requireContext(), DeveloperPreferences.class)); } } diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/preferences/DeveloperPreferences.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/preferences/DeveloperPreferences.java new file mode 100644 index 00000000..f131f0a0 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/preferences/DeveloperPreferences.java @@ -0,0 +1,49 @@ +package com.panda3ds.pandroid.app.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.preference.SwitchPreference; + +import com.panda3ds.pandroid.R; +import com.panda3ds.pandroid.app.PandroidApplication; +import com.panda3ds.pandroid.app.base.BasePreferenceFragment; +import com.panda3ds.pandroid.app.services.LoggerService; +import com.panda3ds.pandroid.data.config.GlobalConfig; + +public class DeveloperPreferences extends BasePreferenceFragment { + @Override + public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) { + setPreferencesFromResource(R.xml.developer_preferences, rootKey); + setActivityTitle(R.string.developer_options); + + setItemClick("performanceMonitor", pref -> GlobalConfig.set(GlobalConfig.KEY_SHOW_PERFORMANCE_OVERLAY, ((SwitchPreference) pref).isChecked())); + setItemClick("shaderJit", pref -> GlobalConfig.set(GlobalConfig.KEY_SHADER_JIT, ((SwitchPreference) pref).isChecked())); + setItemClick("loggerService", pref -> { + boolean checked = ((SwitchPreference) pref).isChecked(); + Context ctx = PandroidApplication.getAppContext(); + if (checked) { + ctx.startService(new Intent(ctx, LoggerService.class)); + } else { + ctx.stopService(new Intent(ctx, LoggerService.class)); + } + GlobalConfig.set(GlobalConfig.KEY_LOGGER_SERVICE, checked); + }); + + refresh(); + } + + @Override + public void onResume() { + super.onResume(); + refresh(); + } + + private void refresh() { + ((SwitchPreference) findPreference("performanceMonitor")).setChecked(GlobalConfig.get(GlobalConfig.KEY_SHOW_PERFORMANCE_OVERLAY)); + ((SwitchPreference) findPreference("loggerService")).setChecked(GlobalConfig.get(GlobalConfig.KEY_LOGGER_SERVICE)); + ((SwitchPreference) findPreference("shaderJit")).setChecked(GlobalConfig.get(GlobalConfig.KEY_SHADER_JIT)); + } +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/services/LoggerService.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/services/LoggerService.java new file mode 100644 index 00000000..e44f3503 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/services/LoggerService.java @@ -0,0 +1,104 @@ +package com.panda3ds.pandroid.app.services; + +import android.app.Service; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.os.Build; +import android.os.IBinder; +import android.util.Log; + +import androidx.annotation.Nullable; + +import com.panda3ds.pandroid.lang.PipeStreamTask; +import com.panda3ds.pandroid.lang.Task; +import com.panda3ds.pandroid.utils.Constants; +import com.panda3ds.pandroid.utils.FileUtils; + +import java.io.OutputStream; +import java.util.Arrays; + +public class LoggerService extends Service { + private static final long MAX_LOG_SIZE = 1024 * 1024 * 4; // 4MB + + private PipeStreamTask errorTask; + private PipeStreamTask outputTask; + private Process logcat; + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + try { + Runtime.getRuntime().exec(new String[]{"logcat", "-c"}).waitFor(); + logcat = Runtime.getRuntime().exec(new String[]{"logcat"}); + String logPath = getExternalMediaDirs()[0].getAbsolutePath(); + FileUtils.createDir(logPath, "logs"); + logPath = logPath + "/logs"; + + if (FileUtils.exists(logPath + "/last.txt")) { + FileUtils.delete(logPath + "/last.txt"); + } + + if (FileUtils.exists(logPath + "/current.txt")) { + FileUtils.rename(logPath + "/current.txt", "last.txt"); + } + + OutputStream stream = FileUtils.getOutputStream(logPath + "/current.txt"); + errorTask = new PipeStreamTask(logcat.getErrorStream(), stream, MAX_LOG_SIZE); + outputTask = new PipeStreamTask(logcat.getInputStream(), stream, MAX_LOG_SIZE); + + errorTask.start(); + outputTask.start(); + + Log.i(Constants.LOG_TAG, "Started logger service"); + logDeviceInfo(); + } catch (Exception e) { + stopSelf(); + Log.e(Constants.LOG_TAG, "Failed to start logger service"); + } + } + + private void logDeviceInfo() { + Log.i(Constants.LOG_TAG, "----------------------"); + Log.i(Constants.LOG_TAG, "Android SDK: " + Build.VERSION.SDK_INT); + Log.i(Constants.LOG_TAG, "Device: " + Build.DEVICE); + Log.i(Constants.LOG_TAG, "Model: " + Build.MANUFACTURER + " " + Build.MODEL); + Log.i(Constants.LOG_TAG, "ABIs: " + Arrays.toString(Build.SUPPORTED_ABIS)); + try { + PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0); + Log.i(Constants.LOG_TAG, ""); + Log.i(Constants.LOG_TAG, "Package: " + info.packageName); + Log.i(Constants.LOG_TAG, "Install location: " + info.installLocation); + Log.i(Constants.LOG_TAG, "App version: " + info.versionName + " (" + info.versionCode + ")"); + } catch (Exception e) { + Log.e(Constants.LOG_TAG, "Error obtaining package info: " + e); + } + Log.i(Constants.LOG_TAG, "----------------------"); + } + + @Override + public void onTaskRemoved(Intent rootIntent) { + stopSelf(); + //This is a time for app save save log file + try { + Thread.sleep(1000); + } catch (Exception e) {} + super.onTaskRemoved(rootIntent); + } + + @Override + public void onDestroy() { + Log.i(Constants.LOG_TAG, "Logger service terminating"); + errorTask.close(); + outputTask.close(); + try { + logcat.destroy(); + } catch (Throwable t) {} + super.onDestroy(); + } +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/data/config/GlobalConfig.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/data/config/GlobalConfig.java index d6dbe3b8..bff1f9e0 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/data/config/GlobalConfig.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/data/config/GlobalConfig.java @@ -5,7 +5,6 @@ import com.panda3ds.pandroid.data.GsonConfigParser; import com.panda3ds.pandroid.utils.Constants; import java.io.Serializable; -import java.util.HashMap; import java.util.Map; public class GlobalConfig { @@ -19,6 +18,9 @@ public class GlobalConfig { public static DataModel data; + public static final Key KEY_SHADER_JIT = new Key<>("emu.shader_jit", false); + public static final Key KEY_SHOW_PERFORMANCE_OVERLAY = new Key<>("dev.performanceOverlay", false); + public static final Key KEY_LOGGER_SERVICE = new Key<>("dev.loggerService", false); public static final Key KEY_APP_THEME = new Key<>("app.theme", THEME_ANDROID); public static final Key KEY_SCREEN_GAMEPAD_VISIBLE = new Key<>("app.screen_gamepad.visible", true); diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/PipeStreamTask.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/PipeStreamTask.java new file mode 100644 index 00000000..e4bbda98 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/PipeStreamTask.java @@ -0,0 +1,40 @@ +package com.panda3ds.pandroid.lang; + +import java.io.InputStream; +import java.io.OutputStream; + +public class PipeStreamTask extends Task { + private final InputStream input; + private final OutputStream output; + private final long limit; + private long size; + + public PipeStreamTask(InputStream input, OutputStream output, long limit) { + this.input = input; + this.output = output; + this.limit = limit; + } + + @Override + public void run() { + super.run(); + int data; + try { + while ((data = input.read()) != -1) { + output.write(data); + if (++size > limit) { + break; + } + } + } catch (Exception e) {} + close(); + } + + public void close() { + try { + output.flush(); + output.close(); + input.close(); + } catch (Exception e) {} + } +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/Task.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/Task.java index 7745883d..8de344b4 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/Task.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/lang/Task.java @@ -5,6 +5,8 @@ public class Task extends Thread { super(runnable); } + protected Task() {} + public void runSync() { start(); waitFinish(); diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/FileUtils.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/FileUtils.java index 45faf5a4..1746f1c9 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/FileUtils.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/FileUtils.java @@ -70,6 +70,25 @@ public class FileUtils { return parseFile(path).exists(); } + public static void rename(String path, String newName){ + parseFile(path).renameTo(newName); + } + + public static void delete(String path) { + DocumentFile file = parseFile(path); + + if (file.exists()) { + if (file.isDirectory()) { + String[] children = listFiles(path); + for (String child : children) { + delete(path + "/" + child); + } + } + + file.delete(); + } + } + public static boolean createDir(String path, String name) { DocumentFile folder = parseFile(path); if (folder.findFile(name) != null) { diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/PerformanceMonitor.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/PerformanceMonitor.java new file mode 100644 index 00000000..23adbf13 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/utils/PerformanceMonitor.java @@ -0,0 +1,64 @@ +package com.panda3ds.pandroid.utils; + +import android.app.ActivityManager; +import android.content.Context; +import android.os.Debug; +import android.os.Process; + +import com.panda3ds.pandroid.app.PandroidApplication; +import com.panda3ds.pandroid.data.config.GlobalConfig; + +public class PerformanceMonitor { + private static int fps = 1; + private static String backend = ""; + private static int frames = 0; + private static long lastUpdate = 0; + private static long totalMemory = 1; + private static long availableMemory = 0; + + public static void initialize(String backendName) { + fps = 1; + backend = backendName; + } + + public static void runFrame() { + if (GlobalConfig.get(GlobalConfig.KEY_SHOW_PERFORMANCE_OVERLAY)) { + frames++; + if (System.currentTimeMillis() - lastUpdate > 1000) { + lastUpdate = System.currentTimeMillis(); + fps = frames; + frames = 0; + try { + Context ctx = PandroidApplication.getAppContext(); + ActivityManager manager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE); + ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo(); + manager.getMemoryInfo(info); + totalMemory = info.totalMem; + availableMemory = info.availMem; + } catch (Exception e) {} + } + } + } + + public static long getUsedMemory() { + return Math.max(1, totalMemory - availableMemory); + } + + public static long getTotalMemory() { + return totalMemory; + } + + public static long getAvailableMemory() { + return availableMemory; + } + + public static int getFps() { + return fps; + } + + public static String getBackend() { + return backend; + } + + public static void destroy() {} +} diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlRenderer.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlRenderer.java index 52e609a3..c39b36b3 100644 --- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlRenderer.java +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/PandaGlRenderer.java @@ -7,8 +7,10 @@ import android.graphics.Rect; import android.opengl.GLSurfaceView; import android.util.Log; import com.panda3ds.pandroid.AlberDriver; +import com.panda3ds.pandroid.data.config.GlobalConfig; import com.panda3ds.pandroid.utils.Constants; import com.panda3ds.pandroid.utils.GameUtils; +import com.panda3ds.pandroid.utils.PerformanceMonitor; import com.panda3ds.pandroid.view.renderer.ConsoleRenderer; import com.panda3ds.pandroid.view.renderer.layout.ConsoleLayout; import com.panda3ds.pandroid.view.renderer.layout.DefaultScreenLayout; @@ -38,9 +40,12 @@ public class PandaGlRenderer implements GLSurfaceView.Renderer, ConsoleRenderer if (screenTexture != 0) { glDeleteTextures(1, new int[] {screenTexture}, 0); } - if (screenFbo != 0) { + + if (screenFbo != 0) { glDeleteFramebuffers(1, new int[] {screenFbo}, 0); } + + PerformanceMonitor.destroy(); super.finalize(); } @@ -78,6 +83,7 @@ public class PandaGlRenderer implements GLSurfaceView.Renderer, ConsoleRenderer glBindFramebuffer(GL_FRAMEBUFFER, 0); AlberDriver.Initialize(); + AlberDriver.setShaderJitEnabled(GlobalConfig.get(GlobalConfig.KEY_SHADER_JIT)); AlberDriver.LoadRom(romPath); // Load the SMDH @@ -92,6 +98,8 @@ public class PandaGlRenderer implements GLSurfaceView.Renderer, ConsoleRenderer GameUtils.removeGame(game); GameUtils.addGame(GameMetadata.applySMDH(game, smdh)); } + + PerformanceMonitor.initialize(getBackendName()); } public void onDrawFrame(GL10 unused) { @@ -114,6 +122,8 @@ public class PandaGlRenderer implements GLSurfaceView.Renderer, ConsoleRenderer screenHeight - bottomScreen.bottom, GL_COLOR_BUFFER_BIT, GL_LINEAR ); } + + PerformanceMonitor.runFrame(); } public void onSurfaceChanged(GL10 unused, int width, int height) { diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/utils/PerformanceView.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/utils/PerformanceView.java new file mode 100644 index 00000000..e4d7be15 --- /dev/null +++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/view/utils/PerformanceView.java @@ -0,0 +1,64 @@ +package com.panda3ds.pandroid.view.utils; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.text.Html; +import android.util.AttributeSet; +import android.util.TypedValue; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatTextView; + +import com.panda3ds.pandroid.data.config.GlobalConfig; +import com.panda3ds.pandroid.utils.PerformanceMonitor; + +public class PerformanceView extends AppCompatTextView { + private boolean running = false; + + public PerformanceView(@NonNull Context context) { + this(context, null); + } + + public PerformanceView(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs,0); + } + + public PerformanceView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()); + setPadding(padding,padding,padding,padding); + setTextColor(Color.WHITE); + setShadowLayer(padding,0,0,Color.BLACK); + } + + public void refresh(){ + running = isShown(); + if (!running) { + return; + } + + String debug = ""; + + // Calculate total memory in MB and the current memory usage + int memoryTotalMb = (int) Math.round(PerformanceMonitor.getTotalMemory() / (1024.0 * 1024.0)); + int memoryUsageMb = (int) Math.round(PerformanceMonitor.getUsedMemory() / (1024.0 * 1024.0)); + + debug += "FPS: " + PerformanceMonitor.getFps() + "
"; + debug += "RAM: " + Math.round(((float) memoryUsageMb / memoryTotalMb) * 100) + "% (" + memoryUsageMb + "MB/" + memoryTotalMb + "MB)
"; + debug += "BACKEND: " + PerformanceMonitor.getBackend() + (GlobalConfig.get(GlobalConfig.KEY_SHADER_JIT) ? " + JIT" : "") + "
"; + setText(Html.fromHtml(debug, Html.FROM_HTML_MODE_COMPACT)); + postDelayed(this::refresh, 250); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (!running) { + refresh(); + } + } +} diff --git a/src/pandroid/app/src/main/res/values-pt-rBR/strings.xml b/src/pandroid/app/src/main/res/values-pt-rBR/strings.xml index 065b9e4f..1198d66b 100644 --- a/src/pandroid/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/pandroid/app/src/main/res/values-pt-rBR/strings.xml @@ -45,4 +45,13 @@ Abrir arquivo Criar novo Executando \"%s\" ... - \ No newline at end of file + Opções de desenvolvedor + Depuração, mostrar fps, etc. + Monitor de desempenho + Mostrar um overlay com fps, memoria, etc. + Depuração + Grave os registros para um arquivo. + Shader Jit + Usar recompilador de shaders. + Gráficos + diff --git a/src/pandroid/app/src/main/res/values/strings.xml b/src/pandroid/app/src/main/res/values/strings.xml index e0de62e1..4c64439c 100644 --- a/src/pandroid/app/src/main/res/values/strings.xml +++ b/src/pandroid/app/src/main/res/values/strings.xml @@ -46,4 +46,13 @@ Open file Create new Running \"%s\" ... + Developer options + Logger, FPS Counter, etc. + Performance monitor + Show overlay with fps, memory, etc. + Logger + Store application logs to file. + Shader JIT + Use shader recompiler. + Graphics diff --git a/src/pandroid/app/src/main/res/xml/developer_preferences.xml b/src/pandroid/app/src/main/res/xml/developer_preferences.xml new file mode 100644 index 00000000..96ce8906 --- /dev/null +++ b/src/pandroid/app/src/main/res/xml/developer_preferences.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/pandroid/app/src/main/res/xml/start_preferences.xml b/src/pandroid/app/src/main/res/xml/start_preferences.xml index 878aa920..5eeb1954 100644 --- a/src/pandroid/app/src/main/res/xml/start_preferences.xml +++ b/src/pandroid/app/src/main/res/xml/start_preferences.xml @@ -23,4 +23,11 @@ app:summary="@string/pref_appearance_summary" app:layout="@layout/preference_start_item"/> + + \ No newline at end of file