Bug 1615148 - Fix wrench on android. r=jrmuizel

This uses a patched version of glutin 0.28 which builds successfully
on Android. It has the caveat that the application is now responsible
to ensure we only create a GL context when the application has been
resumed and the window is valid. This patch does so by spinning an
event loop on startup until we receive a Resume event. This is a bit
of a hack, and will break if the app is minimised, but it is good
enough for wrench's use case.

Cargo-apk no longer supports specifying a separate target_sdk_version
and android_version, meaning we must use a target_sdk_version of
31. This means we no longer have permission to read from "/sdcard", so
wrench and its scripts have been updated to use the application's
"external data dir".

Finally, when running on CI we use a patched version of cargo-apk
which allows building with SDK version 31 and NDK r21d. We should be
able to switch to the upstream git version once we update to NDK r23.

Differential Revision: https://phabricator.services.mozilla.com/D144418
This commit is contained in:
Jamie Nicol 2022-04-27 15:31:56 +00:00
Родитель 0a0a0bb91d
Коммит 7792217d05
11 изменённых файлов: 114 добавлений и 93 удалений

31
gfx/wr/Cargo.lock сгенерированный
Просмотреть файл

@ -23,12 +23,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "android_glue"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407"
[[package]]
name = "ansi_term"
version = "0.11.0"
@ -146,9 +140,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.50"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
dependencies = [
"jobserver",
]
@ -793,10 +787,8 @@ dependencies = [
[[package]]
name = "glutin"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00ea9dbe544bc8a657c4c4a798c2d16cd01b549820e47657297549d28371f6d2"
source = "git+https://github.com/jamienicol/glutin?branch=wr#03285da9c14ec56296c2400c781d2c32b80d745a"
dependencies = [
"android_glue",
"cgl",
"cocoa",
"core-foundation 0.9.2",
@ -808,6 +800,7 @@ dependencies = [
"lazy_static",
"libloading",
"log",
"ndk-glue",
"objc",
"osmesa-sys",
"parking_lot",
@ -820,8 +813,7 @@ dependencies = [
[[package]]
name = "glutin_egl_sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2abb6aa55523480c4adc5a56bbaa249992e2dddb2fc63dc96e04a3355364c211"
source = "git+https://github.com/jamienicol/glutin?branch=wr#03285da9c14ec56296c2400c781d2c32b80d745a"
dependencies = [
"gl_generator 0.14.0",
"winapi",
@ -830,14 +822,12 @@ dependencies = [
[[package]]
name = "glutin_emscripten_sys"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1"
source = "git+https://github.com/jamienicol/glutin?branch=wr#03285da9c14ec56296c2400c781d2c32b80d745a"
[[package]]
name = "glutin_gles2_sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103"
source = "git+https://github.com/jamienicol/glutin?branch=wr#03285da9c14ec56296c2400c781d2c32b80d745a"
dependencies = [
"gl_generator 0.14.0",
"objc",
@ -846,8 +836,7 @@ dependencies = [
[[package]]
name = "glutin_glx_sys"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e393c8fc02b807459410429150e9c4faffdb312d59b8c038566173c81991351"
source = "git+https://github.com/jamienicol/glutin?branch=wr#03285da9c14ec56296c2400c781d2c32b80d745a"
dependencies = [
"gl_generator 0.14.0",
"x11-dl",
@ -856,8 +845,7 @@ dependencies = [
[[package]]
name = "glutin_wgl_sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696"
source = "git+https://github.com/jamienicol/glutin?branch=wr#03285da9c14ec56296c2400c781d2c32b80d745a"
dependencies = [
"gl_generator 0.14.0",
]
@ -2361,6 +2349,7 @@ dependencies = [
"image",
"log",
"mozangle",
"ndk-glue",
"osmesa-src",
"osmesa-sys",
"semver",

Просмотреть файл

@ -23,3 +23,5 @@ opt-level = 2
[patch.crates-io]
fog = { path = "fog" }
# use a patched version of glutin that works on android
glutin = { version = "0.28", git = "https://github.com/jamienicol/glutin", branch="wr" }

Просмотреть файл

@ -6,6 +6,11 @@ build = "build.rs"
license = "MPL-2.0"
edition = "2018"
# Required by cargo-apk to build for Android
[lib]
crate-type = ["lib", "cdylib"]
path = "src/main.rs"
[dependencies]
base64 = "0.13"
env_logger = { version = "0.5", optional = true }
@ -46,25 +51,23 @@ software = [ "swgl" ]
dwrote = "0.11"
mozangle = { version = "0.3.2", features = ["egl"] }
[target.'cfg(target_os = "android")'.dependencies]
ndk-glue = "0.5"
[target.'cfg(all(unix, not(target_os = "android")))'.dependencies]
font-loader = "0.11"
# Configuration information used when building wrench as an APK.
[package.metadata.android]
package_name = "org.mozilla.wrench"
label = "Wrench"
# keep it in sync with android-sdk-version in android-sdk.configure
android_version = 31
target_sdk_version = 18
min_sdk_version = 18
fullscreen = true
package = "org.mozilla.wrench"
build_targets = [ "armv7-linux-androideabi", "i686-linux-android" ]
opengles_version_major = 3
opengles_version_minor = 0
[package.metadata.android.application_attributes]
"android:hardwareAccelerated" = "true"
[package.metadata.android.activity_attributes]
"android:screenOrientation" = "unspecified"
"android:uiOptions" = "none"
[[package.metadata.android.permission]]
name = "android.permission.READ_EXTERNAL_STORAGE"
[package.metadata.android.sdk]
# keep it in sync with android-sdk-version in android-sdk.configure
target_sdk_version = 31
min_sdk_version = 18
[package.metadata.android.application]
label = "Wrench"

Просмотреть файл

@ -10,10 +10,11 @@ Follow the steps at https://github.com/rust-windowing/android-rs-glue#setting-up
- Install both the i686-linux-android and armv7-linux-androideabi rust
targets, as the APK will include native libraries with both architectures.
- Don't install currently published version of cargo-apk as it doesn't work with the
version of winit and glutin we are using.
Instead, install the git master version of our fork like so:
cargo install --git https://github.com/jamienicol/android-rs-glue cargo-apk
- Don't install currently published version of cargo-apk, as it is missing the following
required patch: https://github.com/rust-windowing/android-ndk-rs/pull/236
Instead, install the git master version like so:
cargo install --git https://github.com/rust-windowing/android-ndk-rs cargo-apk
- Consider adding ~/.mozbuild/android-sdk-linux/platform-tools to your path, for the adb commands below.
@ -22,23 +23,23 @@ Compiling and running:
Compile wrench:
cd wrench
export ANDROID_HOME=$HOME/.mozbuild/android-sdk-linux # exact path may vary
export NDK_HOME=$HOME/.mozbuild/android-ndk-r17b # exact path may vary
export ANDROID_SDK_ROOT=$HOME/.mozbuild/android-sdk-linux # exact path may vary
export ANDROID_NDK_ROOT=$HOME/.mozbuild/android-ndk-r21d # exact path may vary
cargo apk build
Install the APK:
adb install -r ../target/android-artifacts/debug/apk/wrench.apk
adb install -r ../target/debug/apk/wrench.apk
Set command line arguments and env vars for wrench:
adb shell
mkdir /sdcard/wrench
echo "load reftests/aa/rounded-rects.yaml" >/sdcard/wrench/args
echo "env: WRENCH_REFTEST_CONDITION_EMULATOR=1" >>/sdcard/wrench/args # If you're using the emulator
echo "env: WRENCH_REFTEST_CONDITION_DEVICE=1" >>/sdcard/wrench/args # If you're using a device
mkdir /storage/emulated/0/Android/data/org.mozilla.wrench/files/wrench
echo "load reftests/aa/rounded-rects.yaml" >/storage/emulated/0/Android/data/org.mozilla.wrench/files/wrench/args
echo "env: WRENCH_REFTEST_CONDITION_EMULATOR=1" >>/storage/emulated/0/Android/data/org.mozilla.wrench/files/wrench/args # If you're using the emulator
echo "env: WRENCH_REFTEST_CONDITION_DEVICE=1" >>/storage/emulated/0/Android/data/org.mozilla.wrench/files/wrench/args # If you're using a device
exit
Push reftests (if you need these files for what you're doing):
adb push reftests /sdcard/wrench/
adb push reftests /storage/emulated/0/Android/data/org.mozilla.wrench/files/wrench/
Run the application:
adb shell am start -n org.mozilla.wrench/android.app.NativeActivity
@ -49,11 +50,11 @@ Release mode:
Building in release does work as well. Use the following steps to compile wrench:
cd wrench
export ANDROID_HOME=$HOME/.mozbuild/android-sdk-linux # exact path may vary
export NDK_HOME=$HOME/.mozbuild/android-ndk # exact path may vary
export ANDROID_SDK_ROOT=$HOME/.mozbuild/android-sdk-linux # exact path may vary
export ANDROID_NDK_ROOT=$HOME/.mozbuild/android-ndk-r21d # exact path may vary
cargo apk build --release
Now the APK at ../target/android-artifacts/release/apk/wrench.apk
Now the APK at ../target/release/apk/wrench.apk
should be signed and installable (you may need to uninstall the debug APK first if you
have that installed).
@ -66,7 +67,7 @@ Running reftests like a boss (on a local emulator):
This will automatically do the following:
- Download the blessed android AVDs from taskcluster
- Start the emulator (using your ~/.mozbuild/android-sdk-linux emulator binaries)
- Install the debug APK (from gfx/wr/wrench/target/android-artifacts/debug/apk/wrench.apk)
- Install the debug APK (from gfx/wr/wrench/target/debug/apk/wrench.apk)
- Copy the reftests to the sdcard
- Write an args file to the sdcard
- Run wrench
@ -80,7 +81,7 @@ Running reftests like a boss (on a local emulator):
If you want to use a release APK (runs much faster), build it as per the "Release mode"
instructions above and set the WRENCH_APK env var to point to the APK:
to point to it:
export WRENCH_APK=gfx/wr/target/android-artifacts/release/apk/wrench.apk
export WRENCH_APK=gfx/wr/target/release/apk/wrench.apk
./mach python testing/mozharness/scripts/android_wrench.py --config testing/mozharness/configs/android/wrench.py
Running reftests like a boss (on a local device):

Просмотреть файл

@ -32,7 +32,6 @@ use gleam::gl::Gl;
use crate::perf::PerfHarness;
use crate::rawtest::RawtestHarness;
use crate::reftest::{ReftestHarness, ReftestOptions};
use std::fs;
#[cfg(feature = "headless")]
use std::ffi::CString;
#[cfg(feature = "headless")]
@ -328,7 +327,8 @@ fn make_window(
let wrapper = if let Some(events_loop) = events_loop {
let context_builder = glutin::ContextBuilder::new()
.with_gl(gl_request)
.with_vsync(vsync);
// Glutin can fail to create a context on Android if vsync is not set
.with_vsync(vsync || cfg!(target_os = "android"));
let window_builder = winit::window::WindowBuilder::new()
.with_title("WRench")
@ -489,11 +489,18 @@ fn reftest<'a>(
rx: Receiver<NotifierEvent>
) -> usize {
let dim = window.get_inner_size();
let base_manifest = if cfg!(target_os = "android") {
Path::new("/sdcard/wrench/reftests/reftest.list")
} else {
Path::new("reftests/reftest.list")
#[cfg(target_os = "android")]
let base_manifest = {
let mut list_path = PathBuf::new();
list_path.push(ndk_glue::native_activity().external_data_path().to_str().unwrap());
list_path.push("wrench");
list_path.push("reftests");
list_path.push("reftest.list");
list_path
};
#[cfg(not(target_os = "android"))]
let base_manifest = Path::new("reftests/reftest.list").to_owned();
let specific_reftest = subargs.value_of("REFTEST").map(Path::new);
let mut reftest_options = ReftestOptions::default();
if let Some(allow_max_diff) = subargs.value_of("fuzz_tolerance") {
@ -501,12 +508,13 @@ fn reftest<'a>(
reftest_options.allow_num_differences = dim.width as usize * dim.height as usize;
}
let num_failures = ReftestHarness::new(&mut wrench, window, &rx)
.run(base_manifest, specific_reftest, &reftest_options);
.run(&base_manifest, specific_reftest, &reftest_options);
wrench.shut_down(rx);
num_failures
}
fn main() {
#[cfg_attr(target_os = "android", ndk_glue::main)]
pub fn main() {
#[cfg(feature = "env_logger")]
env_logger::init();
@ -525,16 +533,22 @@ fn main() {
let clap = clap::App::from_yaml(args_yaml)
.setting(clap::AppSettings::ArgRequiredElseHelp);
// On android devices, attempt to read command line arguments
// from a text file located at /sdcard/wrench/args.
let args = if cfg!(target_os = "android") {
// On android devices, attempt to read command line arguments from a text
// file located at <external_data_dir>/wrench/args.
#[cfg(target_os = "android")]
let args = {
// get full backtraces by default because it's hard to request
// externally on android
std::env::set_var("RUST_BACKTRACE", "full");
let mut args = vec!["wrench".to_string()];
if let Ok(wrench_args) = fs::read_to_string("/sdcard/wrench/args") {
let mut args_path = PathBuf::new();
args_path.push(ndk_glue::native_activity().external_data_path().to_str().unwrap());
args_path.push("wrench");
args_path.push("args");
if let Ok(wrench_args) = std::fs::read_to_string(&args_path) {
for line in wrench_args.lines() {
if let Some(envvar) = line.strip_prefix("env: ") {
if let Some((lhs, rhs)) = envvar.split_once('=') {
@ -552,10 +566,11 @@ fn main() {
}
clap.get_matches_from(&args)
} else {
clap.get_matches()
};
#[cfg(not(target_os = "android"))]
let args = clap.get_matches();
// handle some global arguments
let res_path = args.value_of("shaders").map(PathBuf::from);
let size = args.value_of("size")
@ -622,6 +637,21 @@ fn main() {
let software = args.is_present("software");
// On Android we can only create an OpenGL context when we have a
// native_window handle, so wait here until we are resumed and have a
// handle. If the app gets minimized this will no longer be valid, but
// that's okay for wrench's usage.
#[cfg(target_os = "android")]
{
events_loop.as_mut().unwrap().run_return(|event, _elwt, control_flow| {
if let winit::event::Event::Resumed = event {
if ndk_glue::native_window().is_some() {
*control_flow = winit::event_loop::ControlFlow::Exit;
}
}
});
}
let mut window = make_window(
size,
args.is_present("vsync"),

Просмотреть файл

@ -338,12 +338,12 @@ llvm-mingw:
repo: https://github.com/mstorsjo/llvm-mingw
revision: 9f8e5cebd6dbbb7546e8917e6c59284699a48d26
android-rs-glue:
description: android-rs-glue source code
android-ndk-rs:
description: android-ndk-rs source code
fetch:
type: git
repo: https://github.com/jamienicol/android-rs-glue
revision: 86d9cb4db7bf7a8052e6765d9f8caae6009107cb
repo: https://github.com/jamienicol/android-ndk-rs
revision: 595f4e14a78371e3ab59b12f7bd8131f2ec5b4a1
clang-5.0:
description: clang 5.0.2 source code

Просмотреть файл

@ -177,7 +177,7 @@ wrench-deps:
toolchain-artifact: public/build/wrench-deps.tar.zst
fetches:
fetch:
- android-rs-glue
- android-ndk-rs
toolchain:
- linux64-rust # whatever m-c is built with

Просмотреть файл

@ -247,7 +247,7 @@ jobs:
artifacts:
- type: file
name: public/build/wrench-debug.apk
path: /builds/worker/checkouts/gecko/gfx/wr/target/android-artifacts/debug/apk/wrench.apk
path: /builds/worker/checkouts/gecko/gfx/wr/target/debug/apk/wrench.apk
- type: file
name: public/build/reftests.tar.gz
path: /builds/worker/checkouts/gecko/gfx/wr/wrench/reftests.tar.gz
@ -290,7 +290,7 @@ jobs:
artifacts:
- type: file
name: public/build/wrench-release.apk
path: /builds/worker/checkouts/gecko/gfx/wr/target/android-artifacts/release/apk/wrench.apk
path: /builds/worker/checkouts/gecko/gfx/wr/target/release/apk/wrench.apk
- type: file
name: public/build/reftests.tar.gz
path: /builds/worker/checkouts/gecko/gfx/wr/wrench/reftests.tar.gz

Просмотреть файл

@ -12,17 +12,13 @@ pushd "${GECKO_PATH}/gfx/wr/wrench"
# this script (webrender-wrench-android-build).
export PATH="${PATH}:${MOZ_FETCHES_DIR}/rustc/bin"
export PATH="${PATH}:${JAVA_HOME}/bin"
export ANDROID_HOME="${MOZ_FETCHES_DIR}/android-sdk-linux"
export NDK_HOME="${MOZ_FETCHES_DIR}/android-ndk"
export ANDROID_SDK_ROOT="${MOZ_FETCHES_DIR}/android-sdk-linux"
export ANDROID_NDK_ROOT="${MOZ_FETCHES_DIR}/android-ndk"
# `cargo apk build` fails whilst attempting to sign the output apk,
# unless the `.android` directory exists.
# See https://github.com/rust-windowing/android-rs-glue/issues/252
mkdir /builds/worker/.android
if [ "$MODE" == "debug" ]; then
../cargo-apk/bin/cargo-apk build --frozen --verbose
../cargo-apk/bin/cargo-apk apk build --frozen --verbose
elif [ "$MODE" == "release" ]; then
../cargo-apk/bin/cargo-apk build --frozen --verbose --release
../cargo-apk/bin/cargo-apk apk build --frozen --verbose --release
else
echo "Unknown mode '${MODE}'; must be 'debug' or 'release'"
exit 1

Просмотреть файл

@ -17,11 +17,10 @@ mkdir wrench-deps
mv vendor .cargo wrench-deps/
mkdir wrench-deps/cargo-apk
# Until there's a version of cargo-apk published on crates.io that has
# https://github.com/rust-windowing/android-rs-glue/pull/223, we need to use
# an unpublished version.
# That version of cargo-apk needs to be built with an older version of rust.
# See bug 1722702 comment 4.
PATH=$MOZ_FETCHES_DIR/rustc-1.47/rustc/bin:$PATH cargo install --path $MOZ_FETCHES_DIR/android-rs-glue/cargo-apk --root wrench-deps/cargo-apk cargo-apk
# https://github.com/rust-windowing/android-ndk-rs/pull/236, we need to use
# an unpublished version. Additionally, until we update the NDK version used
# in gecko we must use our own patched version. See bug 1615148.
cargo install --path $MOZ_FETCHES_DIR/android-ndk-rs/cargo-apk --root wrench-deps/cargo-apk cargo-apk
ci-scripts/install-meson.sh
mv meson wrench-deps/meson

Просмотреть файл

@ -81,7 +81,7 @@ class AndroidWrench(TestingMixin, BaseScript, MozbaseMixin, AndroidMixin):
abs_dirs["abs_blob_upload_dir"] = os.path.join(abs_dirs["abs_work_dir"], "logs")
abs_dirs["abs_apk_path"] = os.environ.get(
"WRENCH_APK", "gfx/wr/target/android-artifacts/debug/apk/wrench.apk"
"WRENCH_APK", "gfx/wr/target/debug/apk/wrench.apk"
)
abs_dirs["abs_reftests_path"] = os.environ.get(
"WRENCH_REFTESTS", "gfx/wr/wrench/reftests"
@ -157,11 +157,12 @@ class AndroidWrench(TestingMixin, BaseScript, MozbaseMixin, AndroidMixin):
# Note that we hard-code /sdcard/wrench as the path here, rather than
# using something like self.device.test_root, because it needs to be
# kept in sync with the path hard-coded inside the wrench source code.
self.device.rm("/sdcard/wrench", recursive=True, force=True)
self.device.mkdir("/sdcard/wrench", parents=True)
wrench_root = "/storage/emulated/0/Android/data/org.mozilla.wrench/files/wrench"
self.device.rm(wrench_root, recursive=True, force=True)
self.device.mkdir(wrench_root, parents=True)
if test_mode == TestMode.REFTEST:
self.device.push(
self.query_abs_dirs()["abs_reftests_path"], "/sdcard/wrench/reftests"
self.query_abs_dirs()["abs_reftests_path"], wrench_root + "/reftests"
)
args_file = os.path.join(self.query_abs_dirs()["abs_work_dir"], "wrench_args")
with open(args_file, "w") as argfile:
@ -177,7 +178,7 @@ class AndroidWrench(TestingMixin, BaseScript, MozbaseMixin, AndroidMixin):
argfile.write("--precache test_shaders")
elif test_mode == TestMode.REFTEST:
argfile.write("reftest")
self.device.push(args_file, "/sdcard/wrench/args")
self.device.push(args_file, wrench_root + "/args")
def run_tests(self, timeout):
self.timed_screenshots(None)
@ -196,7 +197,7 @@ class AndroidWrench(TestingMixin, BaseScript, MozbaseMixin, AndroidMixin):
"""Wrench will dump the test output to logcat, but for convenience we
want it to show up in the main log. So we scrape it out of the logcat
and dump it to our own log. Note that all output from wrench goes
through the cargo-apk glue stuff, which uses the RustAndroidGlueStdouterr
through the cargo-apk glue stuff, which uses the RustStdoutStderr
tag on the output. Also it limits the line length to 512 bytes
(including the null terminator). For reftest unexpected-fail output
this means that the base64 image dump gets wrapped over multiple
@ -205,7 +206,7 @@ class AndroidWrench(TestingMixin, BaseScript, MozbaseMixin, AndroidMixin):
with open(self.logcat_path(), "r", encoding="utf-8") as f:
self.info("=== scraped logcat output ===")
tag = "RustAndroidGlueStdouterr: "
tag = "RustStdoutStderr: "
long_line = None
for line in f:
tag_index = line.find(tag)