From f02bfa9dc1e965d104e7df690a033ee4b0e60008 Mon Sep 17 00:00:00 2001 From: Fredia Huya-Kouadio Date: Sat, 8 Jun 2024 13:01:35 -0700 Subject: [PATCH] Add macro benchmarking support for the Android editor --- platform/android/java/editor/build.gradle | 9 +++ .../java/editor/macrobenchmark/.gitignore | 1 + .../java/editor/macrobenchmark/build.gradle | 55 ++++++++++++++++++ .../src/main/AndroidManifest.xml | 1 + .../editor/macrobenchmark/EditorBenchmarks.kt | 57 +++++++++++++++++++ .../org/godotengine/editor/BaseGodotEditor.kt | 8 ++- .../lib/src/org/godotengine/godot/Godot.kt | 1 + .../godotengine/godot/utils/BenchmarkUtils.kt | 6 +- platform/android/java/settings.gradle | 2 + 9 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 platform/android/java/editor/macrobenchmark/.gitignore create mode 100644 platform/android/java/editor/macrobenchmark/build.gradle create mode 100644 platform/android/java/editor/macrobenchmark/src/main/AndroidManifest.xml create mode 100644 platform/android/java/editor/macrobenchmark/src/main/java/org/godotengine/editor/macrobenchmark/EditorBenchmarks.kt diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle index bfc123c8cd8a..340c712c84e3 100644 --- a/platform/android/java/editor/build.gradle +++ b/platform/android/java/editor/build.gradle @@ -123,6 +123,14 @@ android { applicationIdSuffix ".dev" manifestPlaceholders += [editorBuildSuffix: " (dev)"] } + benchmark { + initWith release + applicationIdSuffix ".benchmark" + manifestPlaceholders += [editorBuildSuffix: " (benchmark)"] + signingConfig signingConfigs.debug + matchingFallbacks = ['release'] + debuggable false + } debug { initWith release @@ -186,6 +194,7 @@ dependencies { implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion" implementation "androidx.constraintlayout:constraintlayout:2.1.4" implementation "org.bouncycastle:bcprov-jdk15to18:1.78" + implementation "androidx.profileinstaller:profileinstaller:1.4.1" // Meta dependencies horizonosImplementation "org.godotengine:godot-openxr-vendors-meta:$versions.openxrVendorsVersion" diff --git a/platform/android/java/editor/macrobenchmark/.gitignore b/platform/android/java/editor/macrobenchmark/.gitignore new file mode 100644 index 000000000000..42afabfd2abe --- /dev/null +++ b/platform/android/java/editor/macrobenchmark/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/platform/android/java/editor/macrobenchmark/build.gradle b/platform/android/java/editor/macrobenchmark/build.gradle new file mode 100644 index 000000000000..adfdf7b41613 --- /dev/null +++ b/platform/android/java/editor/macrobenchmark/build.gradle @@ -0,0 +1,55 @@ +plugins { + id 'com.android.test' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'org.godotengine.editor.macrobenchmark' + compileSdk versions.compileSdk + + compileOptions { + sourceCompatibility versions.javaVersion + targetCompatibility versions.javaVersion + } + + kotlinOptions { + jvmTarget = versions.javaVersion + } + + defaultConfig { + minSdk 24 + targetSdk versions.targetSdk + missingDimensionStrategy 'products', 'editor' + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = 'EMULATOR' + } + + buildTypes { + // This benchmark buildType is used for benchmarking, and should function like your + // release build (for example, with minification on). It's signed with a debug key + // for easy local/CI testing. + benchmark { + debuggable = true + signingConfig = debug.signingConfig + matchingFallbacks = ["release"] + } + } + + targetProjectPath = ":editor" + experimentalProperties["android.experimental.self-instrumenting"] = true +} + +dependencies { + implementation 'androidx.test:rules:1.5.0' + implementation 'androidx.test.ext:junit:1.1.5' + implementation 'androidx.test.espresso:espresso-core:3.5.1' + implementation 'androidx.test.uiautomator:uiautomator:2.3.0' + implementation 'androidx.benchmark:benchmark-macro-junit4:1.2.4' +} + +androidComponents { + beforeVariants(selector().all()) { + enable = buildType == "benchmark" + } +} diff --git a/platform/android/java/editor/macrobenchmark/src/main/AndroidManifest.xml b/platform/android/java/editor/macrobenchmark/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..227314eeb7de --- /dev/null +++ b/platform/android/java/editor/macrobenchmark/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/platform/android/java/editor/macrobenchmark/src/main/java/org/godotengine/editor/macrobenchmark/EditorBenchmarks.kt b/platform/android/java/editor/macrobenchmark/src/main/java/org/godotengine/editor/macrobenchmark/EditorBenchmarks.kt new file mode 100644 index 000000000000..5fbe6e253551 --- /dev/null +++ b/platform/android/java/editor/macrobenchmark/src/main/java/org/godotengine/editor/macrobenchmark/EditorBenchmarks.kt @@ -0,0 +1,57 @@ +package org.godotengine.editor.macrobenchmark + +import android.Manifest +import androidx.benchmark.macro.ExperimentalMetricApi +import androidx.benchmark.macro.MemoryUsageMetric +import androidx.benchmark.macro.StartupMode +import androidx.benchmark.macro.StartupTimingMetric +import androidx.benchmark.macro.junit4.MacrobenchmarkRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.GrantPermissionRule +import androidx.test.uiautomator.By +import androidx.test.uiautomator.StaleObjectException +import androidx.test.uiautomator.Until +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Set of editor macro benchmarks. + * + * Before running, switch the editor's active build variant to 'benchmark' + */ +@RunWith(AndroidJUnit4::class) +class EditorBenchmarks { + + companion object { + const val PACKAGE_NAME = "org.godotengine.editor.v4.benchmark" + } + + @get:Rule val benchmarkRule = MacrobenchmarkRule() + @get:Rule val grantPermissionRule = GrantPermissionRule.grant( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE) + + /** + * Navigates to the device's home screen, and launches the Project Manager. + */ + @OptIn(ExperimentalMetricApi::class) + @Test + fun startupProjectManager() = benchmarkRule.measureRepeated( + packageName = PACKAGE_NAME, + metrics = listOf( + StartupTimingMetric(), + MemoryUsageMetric(MemoryUsageMetric.Mode.Max), + ), + iterations = 5, + startupMode = StartupMode.COLD + ) { + pressHome() + startActivityAndWait() + + try { + val editorLoadingIndicator = device.findObject(By.res(PACKAGE_NAME, "editor_loading_indicator")) + editorLoadingIndicator.wait(Until.gone(By.res(PACKAGE_NAME, "editor_loading_indicator")), 5_000) + } catch (ignored: StaleObjectException) {} + } +} diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt index 13ce53ebbbcd..4da950abccc5 100644 --- a/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt +++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt @@ -222,9 +222,11 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER } - // We exclude certain permissions from the set we request at startup, as they'll be - // requested on demand based on use cases. - PermissionsUtil.requestManifestPermissions(this, getExcludedPermissions()) + if (BuildConfig.BUILD_TYPE != "benchmark") { + // We exclude certain permissions from the set we request at startup, as they'll be + // requested on demand based on use cases. + PermissionsUtil.requestManifestPermissions(this, getExcludedPermissions()) + } editorMessageDispatcher.parseStartIntent(packageManager, intent) diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index 52e90bbada3c..cef7ac3a65fd 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -749,6 +749,7 @@ class Godot(private val context: Context) { magnetometerEnabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_magnetometer"))) runOnUiThread { + getActivity()?.reportFullyDrawn() registerSensorsIfNeeded() enableImmersiveMode(useImmersive.get(), true) } diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt b/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt index 738f27e877c2..8d56f6dcbead 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt @@ -64,7 +64,7 @@ private val benchmarkTracker = Collections.synchronizedMap(LinkedHashMap