mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 14:18:31 +09:00
[Feat] (trace-replay): add Linux retrace CI
This commit is contained in:
@@ -0,0 +1,80 @@
|
|||||||
|
name: Retrace APK
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
- Feat/Backend-Direct-GLES
|
||||||
|
- Feat/Backend-Direct-Vulkan
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Set artifact metadata
|
||||||
|
run: |
|
||||||
|
echo "date_today=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV"
|
||||||
|
echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: zulu
|
||||||
|
java-version: '17'
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
with:
|
||||||
|
gradle-version: 8.10.2
|
||||||
|
|
||||||
|
- name: Setup Android SDK
|
||||||
|
uses: android-actions/setup-android@v3
|
||||||
|
|
||||||
|
- name: Install Android NDK
|
||||||
|
run: |
|
||||||
|
sdkmanager "ndk;27.3.13750724"
|
||||||
|
echo "ndk.dir=$ANDROID_HOME/ndk/27.3.13750724" >> android-plugin/local.properties
|
||||||
|
|
||||||
|
- name: Update glslang external sources
|
||||||
|
working-directory: 3rdparty/glslang
|
||||||
|
run: python update_glslang_sources.py
|
||||||
|
|
||||||
|
- name: Build retrace APK
|
||||||
|
run: |
|
||||||
|
gradle --no-daemon -p android-plugin :app:assembleEsprytTraceRelease
|
||||||
|
gradle --no-daemon -p android-plugin :app:assembleMagmaTraceRelease
|
||||||
|
env:
|
||||||
|
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
||||||
|
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
|
||||||
|
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Verify signed APK
|
||||||
|
run: |
|
||||||
|
APKSIGNER="$(find "$ANDROID_HOME/build-tools" -name apksigner -type f | sort -V | tail -n 1)"
|
||||||
|
mapfile -t APKS < <(find android-plugin/app/build/outputs/apk -type f -path '*/release/*.apk' | sort)
|
||||||
|
if [ "${#APKS[@]}" -eq 0 ]; then
|
||||||
|
echo "::error::No release APKs found under android-plugin/app/build/outputs/apk"
|
||||||
|
find android-plugin/app/build/outputs/apk -type f -print || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for APK in "${APKS[@]}"; do
|
||||||
|
if [[ "$APK" == *-unsigned.apk ]]; then
|
||||||
|
echo "::error::Unsigned release APK produced: $APK"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
"$APKSIGNER" verify --verbose "$APK"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Upload retrace APK
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: MobileGL-retrace-apk-${{ env.date_today }}-${{ env.short_sha }}
|
||||||
|
path: android-plugin/app/build/outputs/apk/**/release/*.apk
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
name: Retrace
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
- Feat/Backend-Direct-GLES
|
||||||
|
- Feat/Backend-Direct-Vulkan
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
retrace:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TEST_ROOT: ${{github.workspace}}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Set Swap Space
|
||||||
|
uses: pierotofy/set-swap-space@master
|
||||||
|
with:
|
||||||
|
swap-size-gb: 32
|
||||||
|
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Get CMake
|
||||||
|
uses: lukka/get-cmake@latest
|
||||||
|
|
||||||
|
- name: Prepare Vulkan SDK
|
||||||
|
uses: humbletim/setup-vulkan-sdk@v1.2.1
|
||||||
|
with:
|
||||||
|
vulkan-query-version: 1.4.304.1
|
||||||
|
vulkan-components: Vulkan-Headers, Vulkan-Loader
|
||||||
|
vulkan-use-cache: true
|
||||||
|
|
||||||
|
- name: Update glslang external sources
|
||||||
|
working-directory: ${{env.TEST_ROOT}}/3rdparty/glslang
|
||||||
|
run: python update_glslang_sources.py
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y clang-20 clang++-20 lld-20 libc++-20-dev libc++abi-20-dev libvulkan-dev libegl1-mesa-dev libgles2-mesa-dev libgl1-mesa-dri mesa-vulkan-drivers
|
||||||
|
|
||||||
|
- name: Show installed toolchain
|
||||||
|
run: |
|
||||||
|
clang-20 --version
|
||||||
|
clang++-20 --version
|
||||||
|
ld.lld-20 --version || ld.lld --version || true
|
||||||
|
dpkg -l 'libc++*' 'libegl*' 'libgles*' 'mesa*' || true
|
||||||
|
|
||||||
|
- name: Configure CMake
|
||||||
|
working-directory: ${{env.TEST_ROOT}}
|
||||||
|
run: |
|
||||||
|
if [ "${{ secrets.ACTIONS_STEP_DEBUG }}" == "true" ]; then
|
||||||
|
cmake -S . -B build-retrace -G Ninja -DCMAKE_C_COMPILER=clang-20 -DCMAKE_CXX_COMPILER=clang++-20 -DCMAKE_BUILD_TYPE=Debug -DMOBILEGL_BUILD_TEST=OFF -DMOBILEGL_BUILD_BENCHMARK=OFF -DMOBILEGL_BUILD_TRACE_REPLAY=ON -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||||
|
else
|
||||||
|
cmake -S . -B build-retrace -G Ninja -DCMAKE_C_COMPILER=clang-20 -DCMAKE_CXX_COMPILER=clang++-20 -DCMAKE_BUILD_TYPE=Release -DMOBILEGL_BUILD_TEST=OFF -DMOBILEGL_BUILD_BENCHMARK=OFF -DMOBILEGL_BUILD_TRACE_REPLAY=ON -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build trace replay
|
||||||
|
working-directory: ${{env.TEST_ROOT}}/build-retrace
|
||||||
|
run: cmake --build . --target mobilegl_trace_replay
|
||||||
|
|
||||||
|
- name: OpenRA retrace
|
||||||
|
working-directory: ${{env.TEST_ROOT}}/build-retrace
|
||||||
|
run: ctest -V -R MobileGLTraceReplay.OpenRA.DirectGLES
|
||||||
|
|
||||||
|
- name: Upload OpenRA actual
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: OpenRA-retrace-actual
|
||||||
|
path: |
|
||||||
|
build-retrace/tools/trace_replay/openra/output/actual.png
|
||||||
|
build-retrace/tools/trace_replay/openra/output/result.json
|
||||||
|
build-retrace/tools/trace_replay/openra/output/retrace.log
|
||||||
|
if-no-files-found: warn
|
||||||
@@ -6,6 +6,7 @@ option(MOBILEGL_BUILD_TEST "Build MobileGL tests"
|
|||||||
option(MOBILEGL_BUILD_BENCHMARK "Build MobileGL benchmarks" ON )
|
option(MOBILEGL_BUILD_BENCHMARK "Build MobileGL benchmarks" ON )
|
||||||
option(MOBILEGL_FORCE_RELEASE_OPT "Enable Release optimization flags in Debug build" ON )
|
option(MOBILEGL_FORCE_RELEASE_OPT "Enable Release optimization flags in Debug build" ON )
|
||||||
option(MOBILEGL_ENABLE_TRACY "Enable tracy for profiling" OFF)
|
option(MOBILEGL_ENABLE_TRACY "Enable tracy for profiling" OFF)
|
||||||
|
option(MOBILEGL_BUILD_TRACE_REPLAY "Build Linux apitrace replay runner" OFF)
|
||||||
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
set(MOBILEGL_BUILD_TEST OFF CACHE BOOL "Build MobileGL tests" FORCE)
|
set(MOBILEGL_BUILD_TEST OFF CACHE BOOL "Build MobileGL tests" FORCE)
|
||||||
@@ -396,4 +397,9 @@ if (NOT ANDROID)
|
|||||||
if (MOBILEGL_BUILD_BENCHMARK)
|
if (MOBILEGL_BUILD_BENCHMARK)
|
||||||
add_subdirectory(MobileGL/MG_Benchmark)
|
add_subdirectory(MobileGL/MG_Benchmark)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (MOBILEGL_BUILD_TRACE_REPLAY)
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(tools/trace_replay)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -170,7 +170,9 @@ add_library(retrace_common STATIC
|
|||||||
"${APITRACE_ROOT}/retrace/state_writer_ubjson.cpp"
|
"${APITRACE_ROOT}/retrace/state_writer_ubjson.cpp"
|
||||||
"${APITRACE_ROOT}/retrace/ws.cpp")
|
"${APITRACE_ROOT}/retrace/ws.cpp")
|
||||||
target_compile_definitions(retrace_common PUBLIC RETRACE)
|
target_compile_definitions(retrace_common PUBLIC RETRACE)
|
||||||
target_compile_definitions(retrace_common PRIVATE exit=mobilegl_apitrace_exit)
|
target_compile_definitions(retrace_common PRIVATE
|
||||||
|
exit=mobilegl_apitrace_exit
|
||||||
|
main=mobilegl_apitrace_main)
|
||||||
target_compile_options(retrace_common PRIVATE "-include${CMAKE_CURRENT_LIST_DIR}/apitrace_exit.hpp")
|
target_compile_options(retrace_common PRIVATE "-include${CMAKE_CURRENT_LIST_DIR}/apitrace_exit.hpp")
|
||||||
target_include_directories(retrace_common PUBLIC
|
target_include_directories(retrace_common PUBLIC
|
||||||
"${APITRACE_GENERATED_DIR}"
|
"${APITRACE_GENERATED_DIR}"
|
||||||
@@ -216,7 +218,9 @@ target_include_directories(glretrace_common PUBLIC
|
|||||||
"${APITRACE_ROOT}/dispatch"
|
"${APITRACE_ROOT}/dispatch"
|
||||||
"${APITRACE_ROOT}/helpers"
|
"${APITRACE_ROOT}/helpers"
|
||||||
"${APITRACE_ROOT}/retrace")
|
"${APITRACE_ROOT}/retrace")
|
||||||
target_compile_definitions(glretrace_common PRIVATE exit=mobilegl_apitrace_exit)
|
target_compile_definitions(glretrace_common PRIVATE
|
||||||
|
exit=mobilegl_apitrace_exit
|
||||||
|
main=mobilegl_apitrace_main)
|
||||||
target_compile_options(glretrace_common PRIVATE "-include${CMAKE_CURRENT_LIST_DIR}/apitrace_exit.hpp")
|
target_compile_options(glretrace_common PRIVATE "-include${CMAKE_CURRENT_LIST_DIR}/apitrace_exit.hpp")
|
||||||
target_link_libraries(glretrace_common PUBLIC retrace_common glhelpers glproc)
|
target_link_libraries(glretrace_common PUBLIC retrace_common glhelpers glproc)
|
||||||
|
|
||||||
@@ -225,6 +229,8 @@ add_library(trace_replay_runner SHARED
|
|||||||
trace_replay_jni.cpp)
|
trace_replay_jni.cpp)
|
||||||
|
|
||||||
target_compile_features(trace_replay_runner PRIVATE cxx_std_17)
|
target_compile_features(trace_replay_runner PRIVATE cxx_std_17)
|
||||||
|
target_compile_definitions(trace_replay_runner PRIVATE
|
||||||
|
MOBILEGL_APITRACE_RETRACE_MAIN=mobilegl_apitrace_main)
|
||||||
|
|
||||||
target_include_directories(trace_replay_runner PRIVATE
|
target_include_directories(trace_replay_runner PRIVATE
|
||||||
"${APITRACE_ROOT}/lib/image")
|
"${APITRACE_ROOT}/lib/image")
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef exit
|
||||||
|
#pragma push_macro("exit")
|
||||||
|
#undef exit
|
||||||
|
#define MOBILEGL_TRACE_RESTORE_EXIT_MACRO
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifdef MOBILEGL_TRACE_RESTORE_EXIT_MACRO
|
||||||
|
#pragma pop_macro("exit")
|
||||||
|
#undef MOBILEGL_TRACE_RESTORE_EXIT_MACRO
|
||||||
|
#endif
|
||||||
|
|
||||||
struct MobileGLRetraceExit {
|
struct MobileGLRetraceExit {
|
||||||
int status;
|
int status;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,7 +22,11 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
extern "C" int main(int argc, char** argv);
|
#ifndef MOBILEGL_APITRACE_RETRACE_MAIN
|
||||||
|
#define MOBILEGL_APITRACE_RETRACE_MAIN main
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C" int MOBILEGL_APITRACE_RETRACE_MAIN(int argc, char** argv);
|
||||||
|
|
||||||
namespace mobilegl_trace {
|
namespace mobilegl_trace {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -108,6 +112,7 @@ std::string JsonEscape(const std::string& value) {
|
|||||||
|
|
||||||
bool LoadMobileGL(const Request& request, std::string& error) {
|
bool LoadMobileGL(const Request& request, std::string& error) {
|
||||||
setenv("MOBILEGL_BACKEND_TYPE", request.backend.c_str(), 1);
|
setenv("MOBILEGL_BACKEND_TYPE", request.backend.c_str(), 1);
|
||||||
|
setenv("MOBILEGL_TRACE_LIBRARY", request.mobileGlLibrary.c_str(), 1);
|
||||||
|
|
||||||
void* handle = dlopen(request.mobileGlLibrary.c_str(), RTLD_NOW | RTLD_GLOBAL);
|
void* handle = dlopen(request.mobileGlLibrary.c_str(), RTLD_NOW | RTLD_GLOBAL);
|
||||||
if (handle == nullptr) {
|
if (handle == nullptr) {
|
||||||
@@ -366,7 +371,7 @@ int RunRetraceMain(const Request& request) {
|
|||||||
tracePath.data(),
|
tracePath.data(),
|
||||||
nullptr,
|
nullptr,
|
||||||
};
|
};
|
||||||
return main(10, argv);
|
return MOBILEGL_APITRACE_RETRACE_MAIN(10, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RunRetrace(const Request& request, Result& result) {
|
bool RunRetrace(const Request& request, Result& result) {
|
||||||
|
|||||||
@@ -0,0 +1,270 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.22.1)
|
||||||
|
|
||||||
|
if(NOT UNIX OR APPLE OR ANDROID)
|
||||||
|
message(STATUS "MobileGL trace replay runner is only enabled on Linux")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(MOBILEGL_TRACE_ROOT "${CMAKE_CURRENT_LIST_DIR}")
|
||||||
|
set(MOBILEGL_TRACE_SHARED_CPP_DIR "${CMAKE_SOURCE_DIR}/android-plugin/app/src/trace/cpp")
|
||||||
|
set(APITRACE_ROOT "${CMAKE_SOURCE_DIR}/android-plugin/third_party/apitrace")
|
||||||
|
set(APITRACE_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/apitrace")
|
||||||
|
set(APITRACE_VERSION "mobilegl-trace")
|
||||||
|
|
||||||
|
find_package(Python3 REQUIRED)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
include("${APITRACE_ROOT}/cmake/ConvenienceLibrary.cmake")
|
||||||
|
|
||||||
|
function(target_optimize TARGET)
|
||||||
|
target_compile_definitions(${TARGET} PRIVATE -DNDEBUG)
|
||||||
|
target_compile_options(${TARGET} PRIVATE -O3)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(include_with_scope)
|
||||||
|
include(${ARGV})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
|
||||||
|
set(ENABLE_STATIC_SNAPPY ON CACHE BOOL "" FORCE)
|
||||||
|
set(DOC_INSTALL_DIR "doc" CACHE PATH "" FORCE)
|
||||||
|
set(HAVE_X86 OFF CACHE BOOL "" FORCE)
|
||||||
|
set(ZLIB_FOUND OFF CACHE BOOL "" FORCE)
|
||||||
|
set(PNG_FOUND OFF CACHE BOOL "" FORCE)
|
||||||
|
set(Snappy_FOUND OFF CACHE BOOL "" FORCE)
|
||||||
|
set(BROTLIDEC_FOUND OFF CACHE BOOL "" FORCE)
|
||||||
|
set(BROTLIENC_FOUND OFF CACHE BOOL "" FORCE)
|
||||||
|
set(ZSTD_FOUND OFF CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
if(NOT TARGET check)
|
||||||
|
add_custom_target(check)
|
||||||
|
endif()
|
||||||
|
add_subdirectory("${APITRACE_ROOT}/thirdparty" "${APITRACE_BINARY_DIR}/thirdparty")
|
||||||
|
|
||||||
|
set(APITRACE_GENERATED_DIR "${APITRACE_BINARY_DIR}/generated")
|
||||||
|
file(MAKE_DIRECTORY "${APITRACE_GENERATED_DIR}")
|
||||||
|
configure_file("${APITRACE_ROOT}/version.h.in" "${APITRACE_GENERATED_DIR}/version.h" @ONLY)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT
|
||||||
|
"${APITRACE_GENERATED_DIR}/glproc.hpp"
|
||||||
|
"${APITRACE_GENERATED_DIR}/glproc.cpp"
|
||||||
|
COMMAND ${Python3_EXECUTABLE}
|
||||||
|
"${APITRACE_ROOT}/dispatch/glproc.py"
|
||||||
|
"${APITRACE_GENERATED_DIR}/glproc.hpp"
|
||||||
|
"${APITRACE_GENERATED_DIR}/glproc.cpp"
|
||||||
|
DEPENDS
|
||||||
|
"${APITRACE_ROOT}/dispatch/glproc.py"
|
||||||
|
"${APITRACE_ROOT}/dispatch/dispatch.py"
|
||||||
|
"${APITRACE_ROOT}/specs/wglapi.py"
|
||||||
|
"${APITRACE_ROOT}/specs/glxapi.py"
|
||||||
|
"${APITRACE_ROOT}/specs/cglapi.py"
|
||||||
|
"${APITRACE_ROOT}/specs/eglapi.py"
|
||||||
|
"${APITRACE_ROOT}/specs/glapi.py"
|
||||||
|
"${APITRACE_ROOT}/specs/gltypes.py"
|
||||||
|
"${APITRACE_ROOT}/specs/stdapi.py")
|
||||||
|
|
||||||
|
add_library(mobilegl_trace_apitrace_os STATIC
|
||||||
|
"${APITRACE_ROOT}/lib/os/os_backtrace.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/os/os_crtdbg.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/os/os_posix.cpp")
|
||||||
|
target_include_directories(mobilegl_trace_apitrace_os PUBLIC
|
||||||
|
"${APITRACE_ROOT}/compat"
|
||||||
|
"${APITRACE_ROOT}/thirdparty"
|
||||||
|
"${APITRACE_ROOT}/lib/os"
|
||||||
|
"${APITRACE_ROOT}/lib/trace")
|
||||||
|
target_link_libraries(mobilegl_trace_apitrace_os PUBLIC Threads::Threads)
|
||||||
|
|
||||||
|
add_library(mobilegl_trace_highlight STATIC "${APITRACE_ROOT}/lib/highlight/highlight.cpp")
|
||||||
|
target_include_directories(mobilegl_trace_highlight PUBLIC "${APITRACE_ROOT}/lib/highlight")
|
||||||
|
|
||||||
|
add_library(mobilegl_trace_guids STATIC "${APITRACE_ROOT}/lib/guids/guids.cpp")
|
||||||
|
target_include_directories(mobilegl_trace_guids PUBLIC
|
||||||
|
"${APITRACE_ROOT}/lib/guids"
|
||||||
|
"${APITRACE_ROOT}/lib/os")
|
||||||
|
|
||||||
|
add_library(mobilegl_trace_image STATIC
|
||||||
|
"${APITRACE_ROOT}/lib/image/image_bmp.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/image/image_png.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/image/image_pnm.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/image/image_raw.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/image/image_md5.cpp")
|
||||||
|
target_include_directories(mobilegl_trace_image PUBLIC "${APITRACE_ROOT}/lib/image")
|
||||||
|
target_link_libraries(mobilegl_trace_image PUBLIC md5 PNG::PNG)
|
||||||
|
|
||||||
|
add_library(mobilegl_trace_common STATIC
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_callset.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_dump.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_fast_callset.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_file.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_file_read.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_file_zlib.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_file_brotli.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_file_snappy.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_file_zstd.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_file_zstd_seekable.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_model.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_option.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_ostream_snappy.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_ostream_zlib.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_ostream_zstd.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_parser.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_parser_flags.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_parser_loop.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_profiler.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_writer.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_writer_local.cpp"
|
||||||
|
"${APITRACE_ROOT}/lib/trace/trace_writer_model.cpp")
|
||||||
|
target_include_directories(mobilegl_trace_common PUBLIC
|
||||||
|
"${APITRACE_ROOT}/compat"
|
||||||
|
"${APITRACE_ROOT}/thirdparty"
|
||||||
|
"${APITRACE_ROOT}/lib/guids"
|
||||||
|
"${APITRACE_ROOT}/lib/highlight"
|
||||||
|
"${APITRACE_ROOT}/lib/os"
|
||||||
|
"${APITRACE_ROOT}/lib/trace"
|
||||||
|
"${APITRACE_ROOT}/lib/ubjson")
|
||||||
|
target_link_libraries(mobilegl_trace_common PUBLIC
|
||||||
|
mobilegl_trace_guids
|
||||||
|
mobilegl_trace_highlight
|
||||||
|
mobilegl_trace_apitrace_os
|
||||||
|
Snappy::snappy
|
||||||
|
ZLIB::ZLIB
|
||||||
|
PkgConfig::BROTLIDEC
|
||||||
|
PkgConfig::ZSTD
|
||||||
|
zstd_seekable)
|
||||||
|
|
||||||
|
add_library(mobilegl_trace_glproc STATIC
|
||||||
|
"${APITRACE_GENERATED_DIR}/glproc.cpp"
|
||||||
|
"${MOBILEGL_TRACE_ROOT}/apitrace_glproc_mobilegl.cpp")
|
||||||
|
target_include_directories(mobilegl_trace_glproc PUBLIC
|
||||||
|
"${APITRACE_GENERATED_DIR}"
|
||||||
|
"${APITRACE_ROOT}/dispatch"
|
||||||
|
"${APITRACE_ROOT}/lib/os"
|
||||||
|
"${APITRACE_ROOT}/thirdparty/khronos")
|
||||||
|
target_link_libraries(mobilegl_trace_glproc PUBLIC mobilegl_trace_apitrace_os dl)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${Python3_EXECUTABLE} "${APITRACE_ROOT}/retrace/glretrace.py"
|
||||||
|
OUTPUT_FILE "${APITRACE_GENERATED_DIR}/glretrace_gl.cpp"
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${Python3_EXECUTABLE} "${APITRACE_ROOT}/retrace/glstate_params.py"
|
||||||
|
OUTPUT_FILE "${APITRACE_GENERATED_DIR}/glstate_params.cpp"
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY)
|
||||||
|
|
||||||
|
add_library(mobilegl_trace_glhelpers STATIC
|
||||||
|
"${APITRACE_ROOT}/helpers/glfeatures.cpp"
|
||||||
|
"${APITRACE_ROOT}/helpers/eglsize.cpp")
|
||||||
|
target_include_directories(mobilegl_trace_glhelpers PUBLIC
|
||||||
|
"${APITRACE_GENERATED_DIR}"
|
||||||
|
"${APITRACE_ROOT}/dispatch"
|
||||||
|
"${APITRACE_ROOT}/helpers"
|
||||||
|
"${APITRACE_ROOT}/lib/os"
|
||||||
|
"${APITRACE_ROOT}/thirdparty/khronos")
|
||||||
|
target_link_libraries(mobilegl_trace_glhelpers PUBLIC mobilegl_trace_glproc mobilegl_trace_apitrace_os)
|
||||||
|
|
||||||
|
add_library(mobilegl_trace_retrace_common STATIC
|
||||||
|
"${APITRACE_ROOT}/retrace/json.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/process_name.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/retrace.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/retrace_main.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/retrace_stdc.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/retrace_swizzle.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/state_writer.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/state_writer_json.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/state_writer_ubjson.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/ws.cpp")
|
||||||
|
target_compile_definitions(mobilegl_trace_retrace_common PUBLIC RETRACE)
|
||||||
|
target_compile_definitions(mobilegl_trace_retrace_common PRIVATE
|
||||||
|
exit=mobilegl_apitrace_exit
|
||||||
|
main=mobilegl_apitrace_main)
|
||||||
|
target_compile_options(mobilegl_trace_retrace_common PRIVATE "-include${MOBILEGL_TRACE_SHARED_CPP_DIR}/apitrace_exit.hpp")
|
||||||
|
target_include_directories(mobilegl_trace_retrace_common PUBLIC
|
||||||
|
"${APITRACE_GENERATED_DIR}"
|
||||||
|
"${APITRACE_ROOT}/compat"
|
||||||
|
"${APITRACE_ROOT}/dispatch"
|
||||||
|
"${APITRACE_ROOT}/helpers"
|
||||||
|
"${APITRACE_ROOT}/lib/highlight"
|
||||||
|
"${APITRACE_ROOT}/lib/image"
|
||||||
|
"${APITRACE_ROOT}/lib/os"
|
||||||
|
"${APITRACE_ROOT}/lib/trace"
|
||||||
|
"${APITRACE_ROOT}/lib/ubjson"
|
||||||
|
"${APITRACE_ROOT}/retrace"
|
||||||
|
"${APITRACE_ROOT}/thirdparty"
|
||||||
|
"${APITRACE_ROOT}/thirdparty/khronos")
|
||||||
|
target_link_libraries(mobilegl_trace_retrace_common PUBLIC mobilegl_trace_image mobilegl_trace_common)
|
||||||
|
|
||||||
|
add_library(mobilegl_trace_glretrace_common STATIC
|
||||||
|
"${APITRACE_GENERATED_DIR}/glretrace_gl.cpp"
|
||||||
|
"${APITRACE_GENERATED_DIR}/glstate_params.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glretrace_cgl.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glretrace_egl.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glretrace_glx.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glretrace_main.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glretrace_wgl.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glretrace_wgl_font_bitmaps.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glretrace_wgl_font_outlines.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glretrace_ws.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glstate.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glstate_formats.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glstate_images.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glstate_shaders.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/glws.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/metric_backend_amd_perfmon.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/metric_backend_intel_perfquery.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/metric_backend_opengl.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/metric_helper.cpp"
|
||||||
|
"${APITRACE_ROOT}/retrace/metric_writer.cpp"
|
||||||
|
"${MOBILEGL_TRACE_ROOT}/apitrace_glws_egl.cpp")
|
||||||
|
target_include_directories(mobilegl_trace_glretrace_common PUBLIC
|
||||||
|
"${APITRACE_GENERATED_DIR}"
|
||||||
|
"${APITRACE_ROOT}/dispatch"
|
||||||
|
"${APITRACE_ROOT}/helpers"
|
||||||
|
"${APITRACE_ROOT}/retrace")
|
||||||
|
target_compile_definitions(mobilegl_trace_glretrace_common PRIVATE
|
||||||
|
exit=mobilegl_apitrace_exit
|
||||||
|
main=mobilegl_apitrace_main)
|
||||||
|
target_compile_options(mobilegl_trace_glretrace_common PRIVATE "-include${MOBILEGL_TRACE_SHARED_CPP_DIR}/apitrace_exit.hpp")
|
||||||
|
target_link_libraries(mobilegl_trace_glretrace_common PUBLIC
|
||||||
|
mobilegl_trace_retrace_common
|
||||||
|
mobilegl_trace_glhelpers
|
||||||
|
mobilegl_trace_glproc)
|
||||||
|
|
||||||
|
add_executable(mobilegl_trace_replay
|
||||||
|
"${MOBILEGL_TRACE_SHARED_CPP_DIR}/trace_replay_core.cpp"
|
||||||
|
"${MOBILEGL_TRACE_ROOT}/trace_replay_cli.cpp")
|
||||||
|
target_compile_features(mobilegl_trace_replay PRIVATE cxx_std_17)
|
||||||
|
target_compile_definitions(mobilegl_trace_replay PRIVATE
|
||||||
|
MOBILEGL_APITRACE_RETRACE_MAIN=mobilegl_apitrace_main)
|
||||||
|
target_include_directories(mobilegl_trace_replay PRIVATE
|
||||||
|
"${MOBILEGL_TRACE_SHARED_CPP_DIR}"
|
||||||
|
"${APITRACE_ROOT}/lib/image")
|
||||||
|
target_link_libraries(mobilegl_trace_replay PRIVATE
|
||||||
|
"-Wl,--start-group"
|
||||||
|
mobilegl_trace_glretrace_common
|
||||||
|
mobilegl_trace_retrace_common
|
||||||
|
mobilegl_trace_image
|
||||||
|
mobilegl_trace_common
|
||||||
|
mobilegl_trace_glhelpers
|
||||||
|
mobilegl_trace_glproc
|
||||||
|
mobilegl_trace_apitrace_os
|
||||||
|
"-Wl,--end-group"
|
||||||
|
dl)
|
||||||
|
add_dependencies(mobilegl_trace_replay MobileGL)
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
add_test(
|
||||||
|
NAME MobileGLTraceReplay.OpenRA.DirectGLES
|
||||||
|
COMMAND "${CMAKE_COMMAND}"
|
||||||
|
-DTRACE_REPLAY_EXE=$<TARGET_FILE:mobilegl_trace_replay>
|
||||||
|
-DMOBILEGL_LIBRARY=$<TARGET_FILE:MobileGL>
|
||||||
|
-DOPENRA_TRACE_ARCHIVE=${MOBILEGL_TRACE_ROOT}/fixtures/openra.tgz
|
||||||
|
-DOPENRA_GOLDEN=${MOBILEGL_TRACE_ROOT}/fixtures/openra.0000031249.png
|
||||||
|
-DOPENRA_OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}/openra
|
||||||
|
-P ${MOBILEGL_TRACE_ROOT}/run_openra_replay.cmake)
|
||||||
|
set_tests_properties(MobileGLTraceReplay.OpenRA.DirectGLES PROPERTIES
|
||||||
|
ENVIRONMENT "EGL_PLATFORM=surfaceless;LIBGL_ALWAYS_SOFTWARE=1;MESA_GL_VERSION_OVERRIDE=3.3;MESA_GLSL_VERSION_OVERRIDE=330")
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# MobileGL trace replay
|
||||||
|
|
||||||
|
This directory builds a Linux command line replay runner for apitrace files. It is meant for CI coverage of MobileGL
|
||||||
|
without Android, APK packaging, FCL, or plugin runtime dependencies.
|
||||||
|
|
||||||
|
Build from the MobileGL repository root:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake -S . -B build-test -G Ninja \
|
||||||
|
-DMOBILEGL_BUILD_TEST=ON \
|
||||||
|
-DMOBILEGL_BUILD_BENCHMARK=OFF \
|
||||||
|
-DMOBILEGL_BUILD_TRACE_REPLAY=ON
|
||||||
|
cmake --build build-test
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the OpenRA fixture test:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ctest --test-dir build-test -V -R MobileGLTraceReplay.OpenRA.DirectGLES
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the CLI directly:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
build-test/tools/trace_replay/mobilegl_trace_replay \
|
||||||
|
--trace openra.trace \
|
||||||
|
--golden openra.0000031249.png \
|
||||||
|
--output out/openra \
|
||||||
|
--backend DirectGLES \
|
||||||
|
--mobilegl-library build-test/libMobileGL.so \
|
||||||
|
--target-call 31249 \
|
||||||
|
--width 640 \
|
||||||
|
--height 480 \
|
||||||
|
--crop-x 1 \
|
||||||
|
--crop-y 1 \
|
||||||
|
--crop-width 638 \
|
||||||
|
--crop-height 478 \
|
||||||
|
--tolerance 20 \
|
||||||
|
--fuzz-percent 20
|
||||||
|
```
|
||||||
|
|
||||||
|
The Linux runner uses an EGL pbuffer shim and loads MobileGL by path with `dlopen`. The current CI test covers the
|
||||||
|
OpenRA core-profile trace through the `DirectGLES` backend. `DirectVulkan` is still available as a CLI argument, but it
|
||||||
|
requires a native window/swapchain path and is not part of the headless CI test yet.
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
#include "glproc.hpp"
|
||||||
|
|
||||||
|
#include <GLES3/gl3.h>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
void *_libGlHandle = nullptr;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void *LookupSymbol(const char *procName);
|
||||||
|
|
||||||
|
void *GetMobileGlHandle() {
|
||||||
|
if (_libGlHandle != nullptr) {
|
||||||
|
return _libGlHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *library = std::getenv("MOBILEGL_TRACE_LIBRARY");
|
||||||
|
if (library != nullptr && library[0] != '\0') {
|
||||||
|
_libGlHandle = dlopen(library, RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD);
|
||||||
|
if (_libGlHandle == nullptr) {
|
||||||
|
_libGlHandle = dlopen(library, RTLD_NOW | RTLD_GLOBAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_libGlHandle == nullptr) {
|
||||||
|
_libGlHandle = dlopen("libMobileGL.so", RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD);
|
||||||
|
}
|
||||||
|
if (_libGlHandle == nullptr) {
|
||||||
|
_libGlHandle = dlopen("libMobileGL.so", RTLD_NOW | RTLD_GLOBAL);
|
||||||
|
}
|
||||||
|
return _libGlHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
using GlReadBuffer = void (*)(GLenum mode);
|
||||||
|
using GlReadPixels = void (*)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
|
||||||
|
void *pixels);
|
||||||
|
using GlGetIntegerv = void (*)(GLenum pname, GLint *data);
|
||||||
|
using GlGetError = GLenum (*)();
|
||||||
|
GlReadBuffer gRealGlReadBuffer = nullptr;
|
||||||
|
GlReadPixels gRealGlReadPixels = nullptr;
|
||||||
|
GlGetIntegerv gRealGlGetIntegerv = nullptr;
|
||||||
|
GlGetError gRealGlGetError = nullptr;
|
||||||
|
int gSuppressInvalidEnumCount = 0;
|
||||||
|
|
||||||
|
void MobileGLTraceReadBuffer(GLenum mode) {
|
||||||
|
if (mode == GL_BACK) {
|
||||||
|
++gSuppressInvalidEnumCount;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gRealGlReadBuffer == nullptr) {
|
||||||
|
gRealGlReadBuffer = reinterpret_cast<GlReadBuffer>(LookupSymbol("glReadBuffer"));
|
||||||
|
}
|
||||||
|
if (gRealGlReadBuffer != nullptr) {
|
||||||
|
gRealGlReadBuffer(mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MobileGLTraceReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
|
||||||
|
void *pixels) {
|
||||||
|
if (gRealGlReadPixels == nullptr) {
|
||||||
|
gRealGlReadPixels = reinterpret_cast<GlReadPixels>(LookupSymbol("glReadPixels"));
|
||||||
|
}
|
||||||
|
if (gRealGlReadPixels != nullptr) {
|
||||||
|
gRealGlReadPixels(x, y, width, height, format, type, pixels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MobileGLTraceGetIntegerv(GLenum pname, GLint *data) {
|
||||||
|
if (pname == GL_READ_BUFFER) {
|
||||||
|
if (data != nullptr) {
|
||||||
|
*data = GL_BACK;
|
||||||
|
}
|
||||||
|
++gSuppressInvalidEnumCount;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gRealGlGetIntegerv == nullptr) {
|
||||||
|
gRealGlGetIntegerv = reinterpret_cast<GlGetIntegerv>(LookupSymbol("glGetIntegerv"));
|
||||||
|
}
|
||||||
|
if (gRealGlGetIntegerv != nullptr) {
|
||||||
|
gRealGlGetIntegerv(pname, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum MobileGLTraceGetError() {
|
||||||
|
if (gRealGlGetError == nullptr) {
|
||||||
|
gRealGlGetError = reinterpret_cast<GlGetError>(LookupSymbol("glGetError"));
|
||||||
|
}
|
||||||
|
if (gRealGlGetError == nullptr) {
|
||||||
|
return GL_NO_ERROR;
|
||||||
|
}
|
||||||
|
GLenum error = gRealGlGetError();
|
||||||
|
if (error == GL_INVALID_ENUM && gSuppressInvalidEnumCount > 0) {
|
||||||
|
--gSuppressInvalidEnumCount;
|
||||||
|
return GL_NO_ERROR;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *LookupSymbol(const char *procName) {
|
||||||
|
void *mobileGl = GetMobileGlHandle();
|
||||||
|
if (mobileGl != nullptr) {
|
||||||
|
void *proc = dlsym(mobileGl, procName);
|
||||||
|
if (proc != nullptr) {
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dlsym(RTLD_DEFAULT, procName);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void *_getPublicProcAddress(const char *procName) {
|
||||||
|
if (strcmp(procName, "glReadPixels") == 0) {
|
||||||
|
return reinterpret_cast<void *>(&MobileGLTraceReadPixels);
|
||||||
|
}
|
||||||
|
if (strcmp(procName, "glReadBuffer") == 0) {
|
||||||
|
return reinterpret_cast<void *>(&MobileGLTraceReadBuffer);
|
||||||
|
}
|
||||||
|
if (strcmp(procName, "glGetIntegerv") == 0) {
|
||||||
|
return reinterpret_cast<void *>(&MobileGLTraceGetIntegerv);
|
||||||
|
}
|
||||||
|
if (strcmp(procName, "glGetError") == 0) {
|
||||||
|
return reinterpret_cast<void *>(&MobileGLTraceGetError);
|
||||||
|
}
|
||||||
|
return LookupSymbol(procName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *_getPrivateProcAddress(const char *procName) {
|
||||||
|
void *proc = LookupSymbol(procName);
|
||||||
|
if (proc != nullptr) {
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_eglGetProcAddress != nullptr) {
|
||||||
|
return reinterpret_cast<void *>(_eglGetProcAddress(procName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
@@ -0,0 +1,373 @@
|
|||||||
|
#include "glws.hpp"
|
||||||
|
#include "retrace.hpp"
|
||||||
|
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <EGL/eglext.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using PfnEglBindApi = EGLBoolean (*)(EGLenum);
|
||||||
|
using PfnEglChooseConfig = EGLBoolean (*)(EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *);
|
||||||
|
using PfnEglCreateContext = EGLContext (*)(EGLDisplay, EGLConfig, EGLContext, const EGLint *);
|
||||||
|
using PfnEglCreatePbufferSurface = EGLSurface (*)(EGLDisplay, EGLConfig, const EGLint *);
|
||||||
|
using PfnEglDestroyContext = EGLBoolean (*)(EGLDisplay, EGLContext);
|
||||||
|
using PfnEglDestroySurface = EGLBoolean (*)(EGLDisplay, EGLSurface);
|
||||||
|
using PfnEglGetConfigAttrib = EGLBoolean (*)(EGLDisplay, EGLConfig, EGLint, EGLint *);
|
||||||
|
using PfnEglGetDisplay = EGLDisplay (*)(EGLNativeDisplayType);
|
||||||
|
using PfnEglGetError = EGLint (*)();
|
||||||
|
using PfnEglInitialize = EGLBoolean (*)(EGLDisplay, EGLint *, EGLint *);
|
||||||
|
using PfnEglMakeCurrent = EGLBoolean (*)(EGLDisplay, EGLSurface, EGLSurface, EGLContext);
|
||||||
|
using PfnEglQueryString = const char *(*)(EGLDisplay, EGLint);
|
||||||
|
using PfnEglSwapBuffers = EGLBoolean (*)(EGLDisplay, EGLSurface);
|
||||||
|
using PfnEglTerminate = EGLBoolean (*)(EGLDisplay);
|
||||||
|
|
||||||
|
struct EglFns {
|
||||||
|
PfnEglBindApi bindApi = nullptr;
|
||||||
|
PfnEglChooseConfig chooseConfig = nullptr;
|
||||||
|
PfnEglCreateContext createContext = nullptr;
|
||||||
|
PfnEglCreatePbufferSurface createPbufferSurface = nullptr;
|
||||||
|
PfnEglDestroyContext destroyContext = nullptr;
|
||||||
|
PfnEglDestroySurface destroySurface = nullptr;
|
||||||
|
PfnEglGetConfigAttrib getConfigAttrib = nullptr;
|
||||||
|
PfnEglGetDisplay getDisplay = nullptr;
|
||||||
|
PfnEglGetError getError = nullptr;
|
||||||
|
PfnEglInitialize initialize = nullptr;
|
||||||
|
PfnEglMakeCurrent makeCurrent = nullptr;
|
||||||
|
PfnEglQueryString queryString = nullptr;
|
||||||
|
PfnEglSwapBuffers swapBuffers = nullptr;
|
||||||
|
PfnEglTerminate terminate = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
EglFns gEgl;
|
||||||
|
EGLDisplay gDisplay = EGL_NO_DISPLAY;
|
||||||
|
void *gMobileGl = nullptr;
|
||||||
|
const glws::Drawable *gCurrentDrawable = nullptr;
|
||||||
|
EGLContext gCurrentContext = EGL_NO_CONTEXT;
|
||||||
|
int gRequestedWidth = 0;
|
||||||
|
int gRequestedHeight = 0;
|
||||||
|
|
||||||
|
int ResolveWidth(int width) {
|
||||||
|
if (gRequestedWidth > 0) {
|
||||||
|
return gRequestedWidth;
|
||||||
|
}
|
||||||
|
return width > 0 ? width : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ResolveHeight(int height) {
|
||||||
|
if (gRequestedHeight > 0) {
|
||||||
|
return gRequestedHeight;
|
||||||
|
}
|
||||||
|
return height > 0 ? height : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *Lookup(const char *name) {
|
||||||
|
if (gMobileGl == nullptr) {
|
||||||
|
const char *library = std::getenv("MOBILEGL_TRACE_LIBRARY");
|
||||||
|
if (library != nullptr && library[0] != '\0') {
|
||||||
|
gMobileGl = dlopen(library, RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD);
|
||||||
|
if (gMobileGl == nullptr) {
|
||||||
|
gMobileGl = dlopen(library, RTLD_NOW | RTLD_GLOBAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (gMobileGl == nullptr) {
|
||||||
|
gMobileGl = dlopen("libMobileGL.so", RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD);
|
||||||
|
}
|
||||||
|
if (gMobileGl == nullptr) {
|
||||||
|
gMobileGl = dlopen("libMobileGL.so", RTLD_NOW | RTLD_GLOBAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (gMobileGl != nullptr) {
|
||||||
|
void *symbol = dlsym(gMobileGl, name);
|
||||||
|
if (symbol != nullptr) {
|
||||||
|
return symbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dlsym(RTLD_DEFAULT, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Load(T &slot, const char *name) {
|
||||||
|
slot = reinterpret_cast<T>(Lookup(name));
|
||||||
|
if (slot == nullptr) {
|
||||||
|
std::cerr << "error: failed to resolve " << name << " from MobileGL\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoadEgl() {
|
||||||
|
return Load(gEgl.bindApi, "eglBindAPI") &&
|
||||||
|
Load(gEgl.chooseConfig, "eglChooseConfig") &&
|
||||||
|
Load(gEgl.createContext, "eglCreateContext") &&
|
||||||
|
Load(gEgl.createPbufferSurface, "eglCreatePbufferSurface") &&
|
||||||
|
Load(gEgl.destroyContext, "eglDestroyContext") &&
|
||||||
|
Load(gEgl.destroySurface, "eglDestroySurface") &&
|
||||||
|
Load(gEgl.getConfigAttrib, "eglGetConfigAttrib") &&
|
||||||
|
Load(gEgl.getDisplay, "eglGetDisplay") &&
|
||||||
|
Load(gEgl.getError, "eglGetError") &&
|
||||||
|
Load(gEgl.initialize, "eglInitialize") &&
|
||||||
|
Load(gEgl.makeCurrent, "eglMakeCurrent") &&
|
||||||
|
Load(gEgl.queryString, "eglQueryString") &&
|
||||||
|
Load(gEgl.swapBuffers, "eglSwapBuffers") &&
|
||||||
|
Load(gEgl.terminate, "eglTerminate");
|
||||||
|
}
|
||||||
|
|
||||||
|
class EglVisual final : public glws::Visual {
|
||||||
|
public:
|
||||||
|
EGLConfig config = nullptr;
|
||||||
|
EGLenum api = EGL_OPENGL_API;
|
||||||
|
|
||||||
|
explicit EglVisual(glws::Profile prof) : Visual(prof) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EglDrawable final : public glws::Drawable {
|
||||||
|
public:
|
||||||
|
EGLSurface surface = EGL_NO_SURFACE;
|
||||||
|
|
||||||
|
EglDrawable(const EglVisual *visual, int width, int height, bool pbuffer)
|
||||||
|
: Drawable(visual, width, height, pbuffer) {
|
||||||
|
createSurface();
|
||||||
|
}
|
||||||
|
|
||||||
|
~EglDrawable() override {
|
||||||
|
destroySurface();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize(int w, int h) override {
|
||||||
|
w = ResolveWidth(w);
|
||||||
|
h = ResolveHeight(h);
|
||||||
|
if (w == width && h == height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
destroySurface();
|
||||||
|
width = w;
|
||||||
|
height = h;
|
||||||
|
createSurface();
|
||||||
|
if (gCurrentDrawable == this && gCurrentContext != EGL_NO_CONTEXT) {
|
||||||
|
gEgl.makeCurrent(gDisplay, surface, surface, gCurrentContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void show() override {
|
||||||
|
visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swapBuffers() override {
|
||||||
|
if (surface == EGL_NO_SURFACE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char callNo[32];
|
||||||
|
const char *overrideCallNo = getenv("MOBILEGL_TRACE_CURRENT_CALL_OVERRIDE");
|
||||||
|
if (overrideCallNo != nullptr && overrideCallNo[0] != '\0') {
|
||||||
|
snprintf(callNo, sizeof(callNo), "%s", overrideCallNo);
|
||||||
|
} else {
|
||||||
|
snprintf(callNo, sizeof(callNo), "%u", retrace::callNo);
|
||||||
|
}
|
||||||
|
setenv("MOBILEGL_PRESENT_CURRENT_CALL", callNo, 1);
|
||||||
|
gEgl.swapBuffers(gDisplay, surface);
|
||||||
|
unsetenv("MOBILEGL_PRESENT_CURRENT_CALL");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createSurface() {
|
||||||
|
const EGLint attribs[] = {
|
||||||
|
EGL_WIDTH, ResolveWidth(width),
|
||||||
|
EGL_HEIGHT, ResolveHeight(height),
|
||||||
|
EGL_NONE,
|
||||||
|
};
|
||||||
|
surface = gEgl.createPbufferSurface(gDisplay, static_cast<const EglVisual *>(visual)->config, attribs);
|
||||||
|
if (surface == EGL_NO_SURFACE) {
|
||||||
|
std::cerr << "error: EGL pbuffer creation failed: 0x" << std::hex
|
||||||
|
<< gEgl.getError() << std::dec << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroySurface() {
|
||||||
|
if (surface != EGL_NO_SURFACE) {
|
||||||
|
gEgl.destroySurface(gDisplay, surface);
|
||||||
|
surface = EGL_NO_SURFACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EglContext final : public glws::Context {
|
||||||
|
public:
|
||||||
|
EGLContext context = EGL_NO_CONTEXT;
|
||||||
|
|
||||||
|
EglContext(const EglVisual *visual, glws::Context *shareContext)
|
||||||
|
: Context(visual) {
|
||||||
|
const EGLContext share = shareContext == nullptr ? EGL_NO_CONTEXT : static_cast<EglContext *>(shareContext)->context;
|
||||||
|
if (!gEgl.bindApi(visual->api)) {
|
||||||
|
std::cerr << "error: eglBindAPI failed: 0x" << std::hex << gEgl.getError()
|
||||||
|
<< std::dec << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLint attribs[9];
|
||||||
|
int index = 0;
|
||||||
|
if (visual->api == EGL_OPENGL_ES_API) {
|
||||||
|
attribs[index++] = EGL_CONTEXT_CLIENT_VERSION;
|
||||||
|
attribs[index++] = static_cast<EGLint>(std::max<unsigned>(profile.major, 2));
|
||||||
|
} else {
|
||||||
|
attribs[index++] = EGL_CONTEXT_MAJOR_VERSION_KHR;
|
||||||
|
attribs[index++] = profile.major >= 3 ? profile.major : 3;
|
||||||
|
attribs[index++] = EGL_CONTEXT_MINOR_VERSION_KHR;
|
||||||
|
attribs[index++] = profile.major >= 3 ? profile.minor : 3;
|
||||||
|
attribs[index++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
|
||||||
|
attribs[index++] = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR;
|
||||||
|
}
|
||||||
|
attribs[index++] = EGL_NONE;
|
||||||
|
|
||||||
|
context = gEgl.createContext(gDisplay, visual->config, share, attribs);
|
||||||
|
if (context == EGL_NO_CONTEXT && visual->api == EGL_OPENGL_API) {
|
||||||
|
const EGLint fallbackAttribs[] = {EGL_NONE};
|
||||||
|
context = gEgl.createContext(gDisplay, visual->config, share, fallbackAttribs);
|
||||||
|
}
|
||||||
|
if (context == EGL_NO_CONTEXT) {
|
||||||
|
std::cerr << "error: eglCreateContext failed: 0x" << std::hex << gEgl.getError()
|
||||||
|
<< std::dec << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~EglContext() override {
|
||||||
|
if (context != EGL_NO_CONTEXT) {
|
||||||
|
gEgl.destroyContext(gDisplay, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const EglDrawable *AsEglDrawable(const glws::Drawable *drawable) {
|
||||||
|
return static_cast<const EglDrawable *>(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
const EglContext *AsEglContext(const glws::Context *context) {
|
||||||
|
return static_cast<const EglContext *>(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace glws {
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
if (gDisplay != EGL_NO_DISPLAY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!LoadEgl()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gDisplay = gEgl.getDisplay(EGL_DEFAULT_DISPLAY);
|
||||||
|
if (gDisplay == EGL_NO_DISPLAY) {
|
||||||
|
std::cerr << "error: eglGetDisplay failed: 0x" << std::hex << gEgl.getError()
|
||||||
|
<< std::dec << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EGLint major = 0;
|
||||||
|
EGLint minor = 0;
|
||||||
|
if (!gEgl.initialize(gDisplay, &major, &minor)) {
|
||||||
|
std::cerr << "error: eglInitialize failed: 0x" << std::hex << gEgl.getError()
|
||||||
|
<< std::dec << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup() {
|
||||||
|
if (gDisplay != EGL_NO_DISPLAY) {
|
||||||
|
gEgl.makeCurrent(gDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
|
gEgl.terminate(gDisplay);
|
||||||
|
gDisplay = EGL_NO_DISPLAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Visual *createVisual(bool doubleBuffer, unsigned samples, Profile profile) {
|
||||||
|
auto *visual = new EglVisual(profile);
|
||||||
|
visual->doubleBuffer = doubleBuffer;
|
||||||
|
visual->api = profile.api == glfeatures::API_GLES ? EGL_OPENGL_ES_API : EGL_OPENGL_API;
|
||||||
|
|
||||||
|
if (!gEgl.bindApi(visual->api)) {
|
||||||
|
delete visual;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EGLint attribs[] = {
|
||||||
|
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
||||||
|
EGL_RENDERABLE_TYPE, visual->api == EGL_OPENGL_ES_API ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_BIT,
|
||||||
|
EGL_RED_SIZE, 8,
|
||||||
|
EGL_GREEN_SIZE, 8,
|
||||||
|
EGL_BLUE_SIZE, 8,
|
||||||
|
EGL_ALPHA_SIZE, 8,
|
||||||
|
EGL_DEPTH_SIZE, 24,
|
||||||
|
EGL_STENCIL_SIZE, 8,
|
||||||
|
EGL_SAMPLE_BUFFERS, samples > 1 ? 1 : 0,
|
||||||
|
EGL_SAMPLES, samples > 1 ? static_cast<EGLint>(samples) : 0,
|
||||||
|
EGL_NONE,
|
||||||
|
};
|
||||||
|
EGLint count = 0;
|
||||||
|
if (!gEgl.chooseConfig(gDisplay, attribs, &visual->config, 1, &count) || count < 1) {
|
||||||
|
delete visual;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return visual;
|
||||||
|
}
|
||||||
|
|
||||||
|
Drawable *createDrawable(const Visual *visual, int width, int height, const pbuffer_info *pbInfo) {
|
||||||
|
return new EglDrawable(static_cast<const EglVisual *>(visual), ResolveWidth(width),
|
||||||
|
ResolveHeight(height), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Context *createContext(const Visual *visual, Context *shareContext, bool) {
|
||||||
|
return new EglContext(static_cast<const EglVisual *>(visual), shareContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool makeCurrentInternal(Drawable *drawable, Drawable *readable, Context *context) {
|
||||||
|
EGLSurface drawSurface = drawable == nullptr ? EGL_NO_SURFACE : AsEglDrawable(drawable)->surface;
|
||||||
|
EGLSurface readSurface = readable == nullptr ? drawSurface : AsEglDrawable(readable)->surface;
|
||||||
|
EGLContext eglContext = context == nullptr ? EGL_NO_CONTEXT : AsEglContext(context)->context;
|
||||||
|
if (drawSurface == EGL_NO_SURFACE && readSurface == EGL_NO_SURFACE && eglContext == EGL_NO_CONTEXT) {
|
||||||
|
gEgl.makeCurrent(gDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
|
gCurrentDrawable = nullptr;
|
||||||
|
gCurrentContext = EGL_NO_CONTEXT;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (gEgl.makeCurrent(gDisplay, drawSurface, readSurface, eglContext) != EGL_TRUE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gCurrentDrawable = drawable;
|
||||||
|
gCurrentContext = eglContext;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool processEvents() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bindTexImage(Drawable *, int) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool releaseTexImage(Drawable *, int) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setPbufferAttrib(Drawable *, const int *) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace glws
|
||||||
|
|
||||||
|
extern "C" bool mobilegl_trace_get_drawable_bounds(int *width, int *height) {
|
||||||
|
if (gCurrentDrawable == nullptr || width == nullptr || height == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*width = gCurrentDrawable->width;
|
||||||
|
*height = gCurrentDrawable->height;
|
||||||
|
return *width > 0 && *height > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void mobilegl_trace_set_requested_size(int width, int height) {
|
||||||
|
gRequestedWidth = width > 0 ? width : 0;
|
||||||
|
gRequestedHeight = height > 0 ? height : 0;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 394 KiB |
Binary file not shown.
@@ -0,0 +1,63 @@
|
|||||||
|
foreach(required TRACE_REPLAY_EXE MOBILEGL_LIBRARY OPENRA_TRACE_ARCHIVE OPENRA_GOLDEN OPENRA_OUTPUT_DIR)
|
||||||
|
if(NOT DEFINED ${required} OR "${${required}}" STREQUAL "")
|
||||||
|
message(FATAL_ERROR "${required} is required")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(EXISTS "${OPENRA_OUTPUT_DIR}")
|
||||||
|
file(REMOVE_RECURSE "${OPENRA_OUTPUT_DIR}")
|
||||||
|
endif()
|
||||||
|
file(MAKE_DIRECTORY "${OPENRA_OUTPUT_DIR}/input")
|
||||||
|
file(MAKE_DIRECTORY "${OPENRA_OUTPUT_DIR}/output")
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E tar xzf "${OPENRA_TRACE_ARCHIVE}"
|
||||||
|
WORKING_DIRECTORY "${OPENRA_OUTPUT_DIR}/input"
|
||||||
|
RESULT_VARIABLE extract_result
|
||||||
|
OUTPUT_VARIABLE extract_stdout
|
||||||
|
ERROR_VARIABLE extract_stderr)
|
||||||
|
if(NOT extract_result EQUAL 0)
|
||||||
|
message(STATUS "${extract_stdout}")
|
||||||
|
message(STATUS "${extract_stderr}")
|
||||||
|
message(FATAL_ERROR "failed to extract ${OPENRA_TRACE_ARCHIVE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(openra_trace "${OPENRA_OUTPUT_DIR}/input/openra.trace")
|
||||||
|
if(NOT EXISTS "${openra_trace}")
|
||||||
|
message(FATAL_ERROR "extracted OpenRA trace was not found at ${openra_trace}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${TRACE_REPLAY_EXE}"
|
||||||
|
--trace "${openra_trace}"
|
||||||
|
--golden "${OPENRA_GOLDEN}"
|
||||||
|
--output "${OPENRA_OUTPUT_DIR}/output"
|
||||||
|
--backend DirectGLES
|
||||||
|
--mobilegl-library "${MOBILEGL_LIBRARY}"
|
||||||
|
--target-call 31249
|
||||||
|
--width 640
|
||||||
|
--height 480
|
||||||
|
--tolerance 20
|
||||||
|
--crop-x 1
|
||||||
|
--crop-y 1
|
||||||
|
--crop-width 638
|
||||||
|
--crop-height 478
|
||||||
|
--fuzz-percent 20
|
||||||
|
RESULT_VARIABLE replay_result
|
||||||
|
OUTPUT_VARIABLE replay_stdout
|
||||||
|
ERROR_VARIABLE replay_stderr)
|
||||||
|
|
||||||
|
message(STATUS "${replay_stdout}")
|
||||||
|
message(STATUS "${replay_stderr}")
|
||||||
|
|
||||||
|
set(result_json "${OPENRA_OUTPUT_DIR}/output/result.json")
|
||||||
|
if(EXISTS "${result_json}")
|
||||||
|
file(READ "${result_json}" result_contents)
|
||||||
|
message(STATUS "${result_contents}")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "trace replay did not write ${result_json}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT replay_result EQUAL 0)
|
||||||
|
message(FATAL_ERROR "OpenRA trace replay failed with status ${replay_result}")
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
#include "trace_replay_core.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern "C" void mobilegl_trace_set_requested_size(int width, int height);
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void PrintUsage(const char *argv0) {
|
||||||
|
std::cerr
|
||||||
|
<< "Usage: " << argv0 << " --trace PATH --target-call N --output DIR [options]\n"
|
||||||
|
<< "\n"
|
||||||
|
<< "Options:\n"
|
||||||
|
<< " --golden PATH Golden PNG to compare against\n"
|
||||||
|
<< " --backend NAME DirectGLES or DirectVulkan (default: DirectGLES)\n"
|
||||||
|
<< " --mobilegl-library PATH libMobileGL.so path (default: libMobileGL.so)\n"
|
||||||
|
<< " --width N Replay surface width override\n"
|
||||||
|
<< " --height N Replay surface height override\n"
|
||||||
|
<< " --tolerance N Allowed mismatch pixels\n"
|
||||||
|
<< " --fuzz-percent N Per-channel fuzz threshold, 0-100 (default: 20)\n"
|
||||||
|
<< " --crop-x N Compare crop x\n"
|
||||||
|
<< " --crop-y N Compare crop y\n"
|
||||||
|
<< " --crop-width N Compare crop width\n"
|
||||||
|
<< " --crop-height N Compare crop height\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadValue(int argc, char **argv, int &index, std::string &out) {
|
||||||
|
if (index + 1 >= argc) {
|
||||||
|
std::cerr << "Missing value for " << argv[index] << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out = argv[++index];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadInt(int argc, char **argv, int &index, int &out) {
|
||||||
|
std::string value;
|
||||||
|
if (!ReadValue(argc, argv, index, value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out = std::atoi(value.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadLongLong(int argc, char **argv, int &index, long long &out) {
|
||||||
|
std::string value;
|
||||||
|
if (!ReadValue(argc, argv, index, value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out = std::strtoll(value.c_str(), nullptr, 10);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseArgs(int argc, char **argv, mobilegl_trace::Request &request) {
|
||||||
|
request.backend = "DirectGLES";
|
||||||
|
request.fuzzPercent = 20;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
const std::string arg = argv[i];
|
||||||
|
if (arg == "--trace") {
|
||||||
|
if (!ReadValue(argc, argv, i, request.tracePath)) return false;
|
||||||
|
} else if (arg == "--golden") {
|
||||||
|
if (!ReadValue(argc, argv, i, request.goldenPath)) return false;
|
||||||
|
} else if (arg == "--output") {
|
||||||
|
if (!ReadValue(argc, argv, i, request.outputDir)) return false;
|
||||||
|
} else if (arg == "--backend") {
|
||||||
|
if (!ReadValue(argc, argv, i, request.backend)) return false;
|
||||||
|
} else if (arg == "--mobilegl-library") {
|
||||||
|
if (!ReadValue(argc, argv, i, request.mobileGlLibrary)) return false;
|
||||||
|
} else if (arg == "--target-frame") {
|
||||||
|
if (!ReadInt(argc, argv, i, request.targetFrame)) return false;
|
||||||
|
} else if (arg == "--target-call") {
|
||||||
|
if (!ReadLongLong(argc, argv, i, request.targetCall)) return false;
|
||||||
|
} else if (arg == "--width") {
|
||||||
|
if (!ReadInt(argc, argv, i, request.width)) return false;
|
||||||
|
} else if (arg == "--height") {
|
||||||
|
if (!ReadInt(argc, argv, i, request.height)) return false;
|
||||||
|
} else if (arg == "--tolerance") {
|
||||||
|
if (!ReadInt(argc, argv, i, request.tolerance)) return false;
|
||||||
|
} else if (arg == "--crop-x") {
|
||||||
|
if (!ReadInt(argc, argv, i, request.cropX)) return false;
|
||||||
|
} else if (arg == "--crop-y") {
|
||||||
|
if (!ReadInt(argc, argv, i, request.cropY)) return false;
|
||||||
|
} else if (arg == "--crop-width") {
|
||||||
|
if (!ReadInt(argc, argv, i, request.cropWidth)) return false;
|
||||||
|
} else if (arg == "--crop-height") {
|
||||||
|
if (!ReadInt(argc, argv, i, request.cropHeight)) return false;
|
||||||
|
} else if (arg == "--fuzz-percent") {
|
||||||
|
if (!ReadInt(argc, argv, i, request.fuzzPercent)) return false;
|
||||||
|
} else if (arg == "--help" || arg == "-h") {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Unknown argument: " << arg << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.tracePath.empty()) {
|
||||||
|
std::cerr << "--trace is required\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (request.outputDir.empty()) {
|
||||||
|
std::cerr << "--output is required\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (request.targetCall < 0) {
|
||||||
|
std::cerr << "--target-call is required\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
mobilegl_trace::Request request;
|
||||||
|
if (!ParseArgs(argc, argv, request)) {
|
||||||
|
PrintUsage(argv[0]);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
mobilegl_trace_set_requested_size(request.width, request.height);
|
||||||
|
mobilegl_trace::Result result = mobilegl_trace::RunTraceReplay(request);
|
||||||
|
mobilegl_trace_set_requested_size(0, 0);
|
||||||
|
|
||||||
|
if (!result.resultPath.empty()) {
|
||||||
|
mobilegl_trace::WriteResultJson(request, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << result.message << "\n"
|
||||||
|
<< "result: " << result.resultPath << "\n"
|
||||||
|
<< "actual: " << result.actualPath << "\n";
|
||||||
|
return result.passed ? 0 : result.statusCode;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user