diff --git a/.github/workflows/apk.yml b/.github/workflows/apk.yml index 1fbb7d09..5159c64b 100644 --- a/.github/workflows/apk.yml +++ b/.github/workflows/apk.yml @@ -78,12 +78,15 @@ jobs: - name: Download ANGLE x86_64 libraries run: | angle_dir="android-plugin/app/src/trace/jniLibs/x86_64" - angle_base="https://raw.githubusercontent.com/FCL-Team/FoldCraftLauncher/main/FCLauncher/src/main/jniLibs/x86_64" + angle_commit="f2a3d510dffd8f6540a52e1a7d0c5787d151075b" + angle_base="https://raw.githubusercontent.com/FCL-Team/FoldCraftLauncher/${angle_commit}/FCLauncher/src/main/jniLibs/x86_64" mkdir -p "${angle_dir}" curl -L --fail --retry 3 -o "${angle_dir}/libEGL_angle.so" "${angle_base}/libEGL_angle.so" curl -L --fail --retry 3 -o "${angle_dir}/libGLESv2_angle.so" "${angle_base}/libGLESv2_angle.so" test -s "${angle_dir}/libEGL_angle.so" test -s "${angle_dir}/libGLESv2_angle.so" + echo "c41828768d089899fa058ec0bee711a91be88347f29bdb935223da6be1149c40 ${angle_dir}/libEGL_angle.so" | sha256sum -c - + echo "e4f820d99f94365c66df868c7740fef142fe5c0cd7c941790a9e30638857ca4d ${angle_dir}/libGLESv2_angle.so" | sha256sum -c - - name: Build retrace APKs run: gradle --no-daemon -p android-plugin :app:assembleEsprytTraceRelease :app:assembleMagmaTraceRelease -Pmobilegl.abis=all -Pmobilegl.debuggableRelease=true -Pmobilegl.logLevel=MOBILEGL_LOG_LEVEL_INFO --parallel --max-workers "$(nproc)" @@ -322,6 +325,19 @@ jobs: MOBILEGL_VULKAN_R11G11B10F_FALLBACK: ${{ matrix.backend.name == 'DirectVulkan' && '1' || '0' }} run: | apk_file="$(find android-retrace-apks -name '${{ matrix.backend.apk }}' -print -quit)" + extra_retrace_args=() + if [ "${{ matrix.backend.name }}" = "DirectGLES" ] && [ "${{ matrix.case.name }}" = "minecraft-1.21.4-fabric-iris-bliss-in-world" ]; then + angle_override_dir="${RUNNER_TEMP}/mobilegl-angle-bliss-x86_64" + angle_override_commit="bdcc96ac11c79001018ae4375eb73cb54a9f682f" + angle_override_base="https://raw.githubusercontent.com/FCL-Team/FoldCraftLauncher/${angle_override_commit}/FCLauncher/src/main/jniLibs/x86_64" + mkdir -p "${angle_override_dir}" + curl -L --fail --retry 3 -o "${angle_override_dir}/libEGL_angle.so" "${angle_override_base}/libEGL_angle.so" + curl -L --fail --retry 3 -o "${angle_override_dir}/libGLESv2_angle.so" "${angle_override_base}/libGLESv2_angle.so" + echo "d0f4298ccc770cc801fc52e21733521646161e8a4adb3bd0052d9a1b57ee0ca8 ${angle_override_dir}/libEGL_angle.so" | sha256sum -c - + echo "66fdc867e552192d553d59095ea2e3cef4829de65c356f1fd826027b1905972e ${angle_override_dir}/libGLESv2_angle.so" | sha256sum -c - + extra_retrace_args+=(--angle-library-dir "${angle_override_dir}") + extra_retrace_args+=(--avoid-angle-llvmpipe-sampler-mipmap-min-filter) + fi timeout "$(( ${{ matrix.case.timeout_seconds }} + 300 ))" sh android-plugin/trace-replay-ci.sh \ --apk-file "${apk_file}" \ --package "${{ matrix.backend.package }}" \ @@ -341,7 +357,8 @@ jobs: --crop-y "${{ matrix.case.crop_y }}" \ --crop-width "${{ matrix.case.crop_width }}" \ --crop-height "${{ matrix.case.crop_height }}" \ - --timeout-seconds "${{ matrix.case.timeout_seconds }}" + --timeout-seconds "${{ matrix.case.timeout_seconds }}" \ + "${extra_retrace_args[@]}" - name: Collect retrace summary inputs if: always() diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index f24dcd10..401e3877 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -25,20 +25,28 @@ #include #include #include +#include +#include namespace MobileGL::MG_Backend::DirectGLES { constexpr Bool PREFER_MAP_BUFFER_RANGE_FOR_BUFFER_SYNC = false; constexpr const char* BASE_INSTANCE_UNIFORM_NAME = "mg_BaseInstance"; - static Bool ShouldAvoidMipmapMinFilterOnAngleLlvmpipe() { + static Bool IsAngleLlvmpipeRenderer() { return g_GLESCapabilities.GLESRendererString.find("ANGLE") != String::npos && g_GLESCapabilities.GLESRendererString.find("llvmpipe") != String::npos; } - static GLenum ResolveBackendMinFilter(const SamplerParameters& samplerParams) { + static Bool ShouldAvoidSamplerMipmapMinFilterOnAngleLlvmpipe() { + const char* value = std::getenv("MOBILEGL_ANGLE_LLVMPIPE_AVOID_SAMPLER_MIPMAP_MIN_FILTER"); + return value != nullptr && std::strcmp(value, "1") == 0 && IsAngleLlvmpipeRenderer(); + } + + static GLenum ResolveBackendMinFilter(const SamplerParameters& samplerParams, + Bool avoidMipmapMinFilter) { GLenum filter = MG_Util::ConvertSamplerFilterModeToGLEnum(samplerParams.minFilter, samplerParams.mipmapMode); - if (!ShouldAvoidMipmapMinFilterOnAngleLlvmpipe()) { + if (!avoidMipmapMinFilter) { return filter; } switch (filter) { @@ -1214,7 +1222,7 @@ namespace MobileGL::MG_Backend::DirectGLES { if (m_cacheSamplerParameters.minFilter != samplerParams.minFilter || m_cacheSamplerParameters.mipmapMode != samplerParams.mipmapMode) { g_GLESFuncs.glTexParameteri(target, GL_TEXTURE_MIN_FILTER, - (GLint)ResolveBackendMinFilter(samplerParams)); + (GLint)ResolveBackendMinFilter(samplerParams, IsAngleLlvmpipeRenderer())); m_cacheSamplerParameters.minFilter = samplerParams.minFilter; m_cacheSamplerParameters.mipmapMode = samplerParams.mipmapMode; } @@ -1962,7 +1970,9 @@ namespace MobileGL::MG_Backend::DirectGLES { if (m_cacheSamplerParameters.minFilter != samplerParams.minFilter || m_cacheSamplerParameters.mipmapMode != samplerParams.mipmapMode) { g_GLESFuncs.glSamplerParameteri(m_backendSamplerId, GL_TEXTURE_MIN_FILTER, - (GLint)ResolveBackendMinFilter(samplerParams)); + (GLint)ResolveBackendMinFilter( + samplerParams, + ShouldAvoidSamplerMipmapMinFilterOnAngleLlvmpipe())); m_cacheSamplerParameters.minFilter = samplerParams.minFilter; m_cacheSamplerParameters.mipmapMode = samplerParams.mipmapMode; } diff --git a/android-plugin/app/src/trace/cpp/trace_replay_core.cpp b/android-plugin/app/src/trace/cpp/trace_replay_core.cpp index cff73f03..2dee477c 100644 --- a/android-plugin/app/src/trace/cpp/trace_replay_core.cpp +++ b/android-plugin/app/src/trace/cpp/trace_replay_core.cpp @@ -149,6 +149,11 @@ bool LoadMobileGL(const Request& request, std::string& error) { unsetenv("MOBILEGL_RETRACE_USE_ANGLE"); unsetenv("MOBILEGL_RETRACE_ANGLE_DIR"); } + if (request.avoidAngleLlvmpipeSamplerMipmapMinFilter) { + setenv("MOBILEGL_ANGLE_LLVMPIPE_AVOID_SAMPLER_MIPMAP_MIN_FILTER", "1", 1); + } else { + unsetenv("MOBILEGL_ANGLE_LLVMPIPE_AVOID_SAMPLER_MIPMAP_MIN_FILTER"); + } void* handle = dlopen(request.mobileGlLibrary.c_str(), RTLD_NOW | RTLD_GLOBAL); if (handle == nullptr) { @@ -870,6 +875,8 @@ bool WriteResultJson(const Request& request, const Result& result) { file << " \"ssimThreshold\": " << request.ssimThreshold << ",\n"; file << " \"useAngle\": " << (UseAngleForRequest(request) ? "true" : "false") << ",\n"; file << " \"usePbuffer\": " << (request.usePbuffer ? "true" : "false") << ",\n"; + file << " \"avoidAngleLlvmpipeSamplerMipmapMinFilter\": " + << (request.avoidAngleLlvmpipeSamplerMipmapMinFilter ? "true" : "false") << ",\n"; file << " \"holdMs\": " << request.holdMs << ",\n"; file << " \"mismatchPixels\": " << result.mismatchPixels << "\n"; file << "}\n"; diff --git a/android-plugin/app/src/trace/cpp/trace_replay_core.hpp b/android-plugin/app/src/trace/cpp/trace_replay_core.hpp index 9f259e1c..64930731 100644 --- a/android-plugin/app/src/trace/cpp/trace_replay_core.hpp +++ b/android-plugin/app/src/trace/cpp/trace_replay_core.hpp @@ -35,6 +35,7 @@ struct Request { double ssimThreshold = 0.99; bool useAngle = false; bool usePbuffer = true; + bool avoidAngleLlvmpipeSamplerMipmapMinFilter = false; int holdMs = 0; }; diff --git a/android-plugin/app/src/trace/cpp/trace_replay_jni.cpp b/android-plugin/app/src/trace/cpp/trace_replay_jni.cpp index d7ea89a2..791ce121 100644 --- a/android-plugin/app/src/trace/cpp/trace_replay_jni.cpp +++ b/android-plugin/app/src/trace/cpp/trace_replay_jni.cpp @@ -100,7 +100,8 @@ Java_top_mobilegl_plugin_trace_TraceReplayActivity_nativeRunTraceReplay(JNIEnv* jint cropHeight, jstring angleLibraryDir, jboolean useAngle, - jboolean usePbuffer) { + jboolean usePbuffer, + jboolean avoidAngleLlvmpipeSamplerMipmapMinFilter) { mobilegl_trace::Request request; request.tracePath = ToString(env, tracePath); request.goldenPath = ToString(env, goldenPath); @@ -123,6 +124,8 @@ Java_top_mobilegl_plugin_trace_TraceReplayActivity_nativeRunTraceReplay(JNIEnv* request.cropHeight = cropHeight; request.useAngle = useAngle == JNI_TRUE; request.usePbuffer = usePbuffer == JNI_TRUE; + request.avoidAngleLlvmpipeSamplerMipmapMinFilter = + avoidAngleLlvmpipeSamplerMipmapMinFilter == JNI_TRUE; ScopedTraceReplayState replayState; mobilegl_trace_set_requested_size(request.width, request.height); diff --git a/android-plugin/app/src/trace/java/top/mobilegl/plugin/trace/TraceReplayActivity.java b/android-plugin/app/src/trace/java/top/mobilegl/plugin/trace/TraceReplayActivity.java index b033a811..6c521e35 100644 --- a/android-plugin/app/src/trace/java/top/mobilegl/plugin/trace/TraceReplayActivity.java +++ b/android-plugin/app/src/trace/java/top/mobilegl/plugin/trace/TraceReplayActivity.java @@ -113,7 +113,8 @@ public final class TraceReplayActivity extends Activity { request.cropHeight, request.angleLibraryDir, request.useAngle, - request.usePbuffer + request.usePbuffer, + request.avoidAngleLlvmpipeSamplerMipmapMinFilter ); Log.i(TAG, result.toString()); TraceReplayResult finalResult = result; @@ -142,7 +143,8 @@ public final class TraceReplayActivity extends Activity { int cropHeight, String angleLibraryDir, boolean useAngle, - boolean usePbuffer + boolean usePbuffer, + boolean avoidAngleLlvmpipeSamplerMipmapMinFilter ); private static final class TraceReplayRequest { @@ -164,6 +166,7 @@ public final class TraceReplayActivity extends Activity { final String angleLibraryDir; final boolean useAngle; final boolean usePbuffer; + final boolean avoidAngleLlvmpipeSamplerMipmapMinFilter; private TraceReplayRequest( String tracePath, @@ -183,7 +186,8 @@ public final class TraceReplayActivity extends Activity { int cropHeight, String angleLibraryDir, boolean useAngle, - boolean usePbuffer + boolean usePbuffer, + boolean avoidAngleLlvmpipeSamplerMipmapMinFilter ) { this.tracePath = tracePath; this.goldenPath = goldenPath; @@ -203,6 +207,7 @@ public final class TraceReplayActivity extends Activity { this.angleLibraryDir = angleLibraryDir; this.useAngle = useAngle; this.usePbuffer = usePbuffer; + this.avoidAngleLlvmpipeSamplerMipmapMinFilter = avoidAngleLlvmpipeSamplerMipmapMinFilter; } static TraceReplayRequest from(Intent intent, File filesDir, String nativeLibraryDir, String defaultBackend) { @@ -226,7 +231,8 @@ public final class TraceReplayActivity extends Activity { intent.getIntExtra("crop_height", 0), readString(intent, "angle_library_dir", nativeLibraryDir), intent.getBooleanExtra("use_angle", false), - intent.getBooleanExtra("use_pbuffer", false) + intent.getBooleanExtra("use_pbuffer", false), + intent.getBooleanExtra("avoid_angle_llvmpipe_sampler_mipmap_min_filter", false) ); } diff --git a/android-plugin/trace-replay-ci.sh b/android-plugin/trace-replay-ci.sh index a3ded194..f9604305 100644 --- a/android-plugin/trace-replay-ci.sh +++ b/android-plugin/trace-replay-ci.sh @@ -18,6 +18,7 @@ Usage: --trace-file FILE_IN_ARCHIVE \ --golden FILE \ [--alternate-golden FILE] \ + [--angle-library-dir DIR] \ --target-call N \ --width N \ --height N \ @@ -27,12 +28,15 @@ Usage: --crop-width N \ --crop-height N \ [--use-pbuffer] \ + [--avoid-angle-llvmpipe-sampler-mipmap-min-filter] \ --timeout-seconds N Set MOBILEGL_RETRACE_USE_ANGLE=1 to run DirectGLES replay with packaged ANGLE instead of the device system GLES driver. Set MOBILEGL_RETRACE_USE_PBUFFER=1 or pass --use-pbuffer to run DirectGLES against an offscreen EGL pbuffer instead of the Activity surface. +Pass --avoid-angle-llvmpipe-sampler-mipmap-min-filter for DirectGLES traces that +need ANGLE llvmpipe sampler mipmap filters downgraded to avoid driver stalls. EOF } @@ -79,6 +83,7 @@ trace_archive="" trace_file="" golden_path="" alternate_golden_path="" +angle_library_dir="" target_call="" width="" height="" @@ -88,6 +93,7 @@ crop_y="" crop_width="" crop_height="" use_pbuffer=0 +avoid_angle_llvmpipe_sampler_mipmap_min_filter=0 timeout_seconds="" while [ "$#" -gt 0 ]; do @@ -110,6 +116,7 @@ while [ "$#" -gt 0 ]; do shift 2 fi ;; + --angle-library-dir) angle_library_dir="$(next_arg "$@")"; shift 2 ;; --target-call) target_call="$(next_arg "$@")"; shift 2 ;; --width) width="$(next_arg "$@")"; shift 2 ;; --height) height="$(next_arg "$@")"; shift 2 ;; @@ -119,6 +126,10 @@ while [ "$#" -gt 0 ]; do --crop-width) crop_width="$(next_arg "$@")"; shift 2 ;; --crop-height) crop_height="$(next_arg "$@")"; shift 2 ;; --use-pbuffer) use_pbuffer=1; shift 1 ;; + --avoid-angle-llvmpipe-sampler-mipmap-min-filter) + avoid_angle_llvmpipe_sampler_mipmap_min_filter=1 + shift 1 + ;; --timeout-seconds) timeout_seconds="$(next_arg "$@")"; shift 2 ;; -h|--help) usage; exit 0 ;; *) die "unknown argument: $1" ;; @@ -150,6 +161,10 @@ test -f "${golden_path}" || die "golden image does not exist: ${golden_path}" if [ -n "${alternate_golden_path}" ]; then test -f "${alternate_golden_path}" || die "alternate golden image does not exist: ${alternate_golden_path}" fi +if [ -n "${angle_library_dir}" ]; then + test -f "${angle_library_dir}/libEGL_angle.so" || die "ANGLE override is missing libEGL_angle.so: ${angle_library_dir}" + test -f "${angle_library_dir}/libGLESv2_angle.so" || die "ANGLE override is missing libGLESv2_angle.so: ${angle_library_dir}" +fi safe_case="$(printf '%s' "${case_name}" | sed 's/[^A-Za-z0-9._-]/_/g')" app_dir="/data/user/0/${package_name}/files/trace-replay" @@ -188,10 +203,18 @@ prepare_fixture() { if [ -n "${alternate_golden_path}" ]; then adb_device_path push "$(host_path_for_adb "${alternate_golden_path}")" "/data/local/tmp/mobilegl-${safe_case}.alternate-golden.png" fi + if [ -n "${angle_library_dir}" ]; then + adb_device_path push "$(host_path_for_adb "${angle_library_dir}/libEGL_angle.so")" "/data/local/tmp/mobilegl-${safe_case}.libEGL_angle.so" + adb_device_path push "$(host_path_for_adb "${angle_library_dir}/libGLESv2_angle.so")" "/data/local/tmp/mobilegl-${safe_case}.libGLESv2_angle.so" + fi adb_device_path shell chmod 0644 "/data/local/tmp/mobilegl-${safe_case}.trace" "/data/local/tmp/mobilegl-${safe_case}.golden.png" if [ -n "${alternate_golden_path}" ]; then adb_device_path shell chmod 0644 "/data/local/tmp/mobilegl-${safe_case}.alternate-golden.png" fi + if [ -n "${angle_library_dir}" ]; then + adb_device_path shell chmod 0644 "/data/local/tmp/mobilegl-${safe_case}.libEGL_angle.so" \ + "/data/local/tmp/mobilegl-${safe_case}.libGLESv2_angle.so" + fi } copy_fixture_to_app() { @@ -206,6 +229,13 @@ copy_fixture_to_app() { if [ -n "${alternate_golden_path}" ]; then adb_device_path shell run-as "${package_name}" cp "${alternate_golden_tmp}" "${app_dir}/input/alternate-golden.png" fi + if [ -n "${angle_library_dir}" ]; then + adb_device_path shell run-as "${package_name}" mkdir -p "${app_dir}/angle" + adb_device_path shell run-as "${package_name}" cp "/data/local/tmp/mobilegl-${safe_case}.libEGL_angle.so" \ + "${app_dir}/angle/libEGL_angle.so" + adb_device_path shell run-as "${package_name}" cp "/data/local/tmp/mobilegl-${safe_case}.libGLESv2_angle.so" \ + "${app_dir}/angle/libGLESv2_angle.so" + fi } run_retrace() { @@ -236,10 +266,16 @@ run_retrace() { fi if [ "${use_angle}" -eq 1 ]; then set -- "$@" --ez use_angle true + if [ -n "${angle_library_dir}" ]; then + set -- "$@" --es angle_library_dir "${app_dir}/angle" + fi fi if [ "${use_pbuffer}" -eq 1 ] && [ "${backend}" = "DirectGLES" ]; then set -- "$@" --ez use_pbuffer true fi + if [ "${avoid_angle_llvmpipe_sampler_mipmap_min_filter}" -eq 1 ] && [ "${backend}" = "DirectGLES" ]; then + set -- "$@" --ez avoid_angle_llvmpipe_sampler_mipmap_min_filter true + fi set -- "$@" \ --es output_dir "${app_dir}/output" \ --es diff_path "${app_dir}/output/${safe_case}-diff.png" \