diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bbbbbc9d..7b3fa7b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -267,20 +267,20 @@ jobs: working-directory: build-retrace run: | set +e - ctest -V -R MobileGLTraceReplay.OpenRA.DirectGLES + ctest -V -R 'MobileGLTraceReplay\..*\.DirectGLES' status=$? - mkdir -p tools/trace_replay/openra - echo "${status}" > tools/trace_replay/openra/DirectGLES.status + mkdir -p tools/trace_replay/status + echo "${status}" > tools/trace_replay/status/DirectGLES.status exit 0 - name: Retrace and validate Magma (DirectVulkan) working-directory: build-retrace run: | set +e - ctest -V -R MobileGLTraceReplay.OpenRA.DirectVulkan + ctest -V -R 'MobileGLTraceReplay\..*\.DirectVulkan' status=$? - mkdir -p tools/trace_replay/openra - echo "${status}" > tools/trace_replay/openra/DirectVulkan.status + mkdir -p tools/trace_replay/status + echo "${status}" > tools/trace_replay/status/DirectVulkan.status exit 0 - name: Upload actual image @@ -289,20 +289,17 @@ jobs: with: name: retrace-result path: | - build-retrace/tools/trace_replay/openra/actual-images/openra-DirectGLES-actual.png - build-retrace/tools/trace_replay/openra/actual-images/openra-DirectVulkan-actual.png - build-retrace/tools/trace_replay/openra/actual-images/*.json - build-retrace/tools/trace_replay/openra/actual-images/*.log - build-retrace/tools/trace_replay/openra/*.status - build-retrace/tools/trace_replay/openra/*/output/** + build-retrace/tools/trace_replay/*/actual-images/** + build-retrace/tools/trace_replay/*/*/output/** + build-retrace/tools/trace_replay/status/*.status if-no-files-found: warn - name: Check retrace results if: always() working-directory: build-retrace run: | - direct_gles_status="$(cat tools/trace_replay/openra/DirectGLES.status 2>/dev/null || echo 1)" - direct_vulkan_status="$(cat tools/trace_replay/openra/DirectVulkan.status 2>/dev/null || echo 1)" + direct_gles_status="$(cat tools/trace_replay/status/DirectGLES.status 2>/dev/null || echo 1)" + direct_vulkan_status="$(cat tools/trace_replay/status/DirectVulkan.status 2>/dev/null || echo 1)" echo "DirectGLES status: ${direct_gles_status}" echo "DirectVulkan status: ${direct_vulkan_status}" if [ "${direct_gles_status}" -ne 0 ] || [ "${direct_vulkan_status}" -ne 0 ]; then diff --git a/.gitignore b/.gitignore index d240ff91..a53f2a64 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ MobileGL/MG*/cmake-build* /android-plugin/.gradle /android-plugin/build /android-plugin/app/build +tools/trace_replay/work/ diff --git a/tools/trace_replay/CMakeLists.txt b/tools/trace_replay/CMakeLists.txt index 81a2ffc3..9e75eba4 100644 --- a/tools/trace_replay/CMakeLists.txt +++ b/tools/trace_replay/CMakeLists.txt @@ -264,26 +264,104 @@ else() endif() enable_testing() -function(add_openra_trace_replay_test BACKEND) +function(add_trace_replay_test CASE_NAME BACKEND) + set(oneValueArgs + TRACE_ARCHIVE + TRACE_FILE + GOLDEN + TARGET_CALL + WIDTH + HEIGHT + TOLERANCE + CROP_X + CROP_Y + CROP_WIDTH + CROP_HEIGHT + FUZZ_PERCENT) + cmake_parse_arguments(TRACE_CASE "" "${oneValueArgs}" "" ${ARGN}) + foreach(required TRACE_ARCHIVE GOLDEN TARGET_CALL WIDTH HEIGHT) + if(NOT TRACE_CASE_${required}) + message(FATAL_ERROR "${required} is required for ${CASE_NAME}") + endif() + endforeach() + if(NOT TRACE_CASE_TRACE_FILE) + set(TRACE_CASE_TRACE_FILE trace.trace) + endif() + if(NOT TRACE_CASE_TOLERANCE) + set(TRACE_CASE_TOLERANCE 0) + endif() + if(NOT TRACE_CASE_CROP_X) + set(TRACE_CASE_CROP_X 0) + endif() + if(NOT TRACE_CASE_CROP_Y) + set(TRACE_CASE_CROP_Y 0) + endif() + if(NOT TRACE_CASE_CROP_WIDTH) + set(TRACE_CASE_CROP_WIDTH 0) + endif() + if(NOT TRACE_CASE_CROP_HEIGHT) + set(TRACE_CASE_CROP_HEIGHT 0) + endif() + if(NOT TRACE_CASE_FUZZ_PERCENT) + set(TRACE_CASE_FUZZ_PERCENT 20) + endif() + add_test( - NAME MobileGLTraceReplay.OpenRA.${BACKEND} + NAME MobileGLTraceReplay.${CASE_NAME}.${BACKEND} COMMAND "${CMAKE_COMMAND}" -DTRACE_REPLAY_EXE=$ -DMOBILEGL_LIBRARY=${mobilegl_trace_replay_mobilegl_library} - -DOPENRA_TRACE_ARCHIVE=${MOBILEGL_TRACE_ROOT}/fixtures/openra.tgz - -DOPENRA_GOLDEN=${MOBILEGL_TRACE_ROOT}/fixtures/openra.0000031249.png - -DOPENRA_BACKEND=${BACKEND} - -DOPENRA_OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}/openra/${BACKEND} - -DOPENRA_ARTIFACT_DIR=${CMAKE_CURRENT_BINARY_DIR}/openra/actual-images - -P ${MOBILEGL_TRACE_ROOT}/run_openra_replay.cmake) + -DTRACE_CASE_NAME=${CASE_NAME} + -DTRACE_ARCHIVE=${TRACE_CASE_TRACE_ARCHIVE} + -DTRACE_FILE=${TRACE_CASE_TRACE_FILE} + -DTRACE_GOLDEN=${TRACE_CASE_GOLDEN} + -DTRACE_BACKEND=${BACKEND} + -DTRACE_TARGET_CALL=${TRACE_CASE_TARGET_CALL} + -DTRACE_WIDTH=${TRACE_CASE_WIDTH} + -DTRACE_HEIGHT=${TRACE_CASE_HEIGHT} + -DTRACE_TOLERANCE=${TRACE_CASE_TOLERANCE} + -DTRACE_CROP_X=${TRACE_CASE_CROP_X} + -DTRACE_CROP_Y=${TRACE_CASE_CROP_Y} + -DTRACE_CROP_WIDTH=${TRACE_CASE_CROP_WIDTH} + -DTRACE_CROP_HEIGHT=${TRACE_CASE_CROP_HEIGHT} + -DTRACE_FUZZ_PERCENT=${TRACE_CASE_FUZZ_PERCENT} + -DTRACE_OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}/${CASE_NAME}/${BACKEND} + -DTRACE_ARTIFACT_DIR=${CMAKE_CURRENT_BINARY_DIR}/${CASE_NAME}/actual-images + -P ${MOBILEGL_TRACE_ROOT}/run_trace_case.cmake) if(BACKEND STREQUAL "DirectGLES") - set_tests_properties(MobileGLTraceReplay.OpenRA.${BACKEND} PROPERTIES + set_tests_properties(MobileGLTraceReplay.${CASE_NAME}.${BACKEND} PROPERTIES ENVIRONMENT "EGL_PLATFORM=surfaceless;LIBGL_ALWAYS_SOFTWARE=1;MESA_GL_VERSION_OVERRIDE=3.3;MESA_GLSL_VERSION_OVERRIDE=330") else() - set_tests_properties(MobileGLTraceReplay.OpenRA.${BACKEND} PROPERTIES + set_tests_properties(MobileGLTraceReplay.${CASE_NAME}.${BACKEND} PROPERTIES ENVIRONMENT "LIBGL_ALWAYS_SOFTWARE=1;MESA_GL_VERSION_OVERRIDE=3.3;MESA_GLSL_VERSION_OVERRIDE=330") endif() endfunction() -add_openra_trace_replay_test(DirectGLES) -add_openra_trace_replay_test(DirectVulkan) +function(add_trace_replay_test_for_backends CASE_NAME) + add_trace_replay_test(${CASE_NAME} DirectGLES ${ARGN}) + add_trace_replay_test(${CASE_NAME} DirectVulkan ${ARGN}) +endfunction() + +add_trace_replay_test_for_backends(OpenRA + TRACE_ARCHIVE ${MOBILEGL_TRACE_ROOT}/fixtures/openra.tgz + TRACE_FILE openra.trace + GOLDEN ${MOBILEGL_TRACE_ROOT}/fixtures/openra.0000031249.png + TARGET_CALL 31249 + WIDTH 640 + HEIGHT 480 + TOLERANCE 20 + CROP_X 1 + CROP_Y 1 + CROP_WIDTH 638 + CROP_HEIGHT 478 + FUZZ_PERCENT 20) + +add_trace_replay_test_for_backends(minecraft-1.21.4-startup + TRACE_ARCHIVE ${MOBILEGL_TRACE_ROOT}/fixtures/minecraft-1.21.4-startup.tgz + TRACE_FILE trace.trace + GOLDEN ${MOBILEGL_TRACE_ROOT}/fixtures/minecraft-1.21.4-startup.0000092195.png + TARGET_CALL 92195 + WIDTH 854 + HEIGHT 480 + TOLERANCE 20 + FUZZ_PERCENT 20) diff --git a/tools/trace_replay/README.md b/tools/trace_replay/README.md index fd0ff226..6432f518 100644 --- a/tools/trace_replay/README.md +++ b/tools/trace_replay/README.md @@ -3,9 +3,12 @@ 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. -The bundled `openra` fixture comes from GL4ES' apitrace corpus. See -[GL4ES_TRACES.md](GL4ES_TRACES.md) for the current inventory and the traces that -were rejected because they rely on compatibility/fixed-function OpenGL. +The bundled fixtures cover: + +- `OpenRA`: sourced from GL4ES' apitrace corpus. See [GL4ES_TRACES.md](GL4ES_TRACES.md) for the current inventory and + traces rejected because they rely on compatibility/fixed-function OpenGL. +- `minecraft-1.21.4-startup`: captured from Minecraft 1.21.4's Mojang Studios startup screen. The fixture is trimmed + to call 92195, immediately after the startup screen draw into Minecraft's offscreen framebuffer. Build from the MobileGL repository root: @@ -17,10 +20,10 @@ cmake -S . -B build-test -G Ninja \ cmake --build build-test ``` -Run the OpenRA fixture test: +Run the fixture tests: ```sh -ctest --test-dir build-test -V -R MobileGLTraceReplay.OpenRA +ctest --test-dir build-test -V -R 'MobileGLTraceReplay\.' ``` Run the CLI directly: @@ -43,5 +46,8 @@ build-test/tools/trace_replay/mobilegl_trace_replay \ --fuzz-percent 20 ``` +The Minecraft startup fixture uses `tools/trace_replay/fixtures/minecraft-1.21.4-startup.tgz`, golden image +`minecraft-1.21.4-startup.0000092195.png`, `--target-call 92195`, `--width 854`, and `--height 480`. + The Linux runner loads MobileGL by path with `dlopen`. It uses an EGL pbuffer for `DirectGLES` and maps the pbuffer path to `VK_EXT_headless_surface` for `DirectVulkan`, so CI does not need an X11 or Wayland window. diff --git a/tools/trace_replay/fixtures/minecraft-1.21.4-startup.0000092195.png b/tools/trace_replay/fixtures/minecraft-1.21.4-startup.0000092195.png new file mode 100644 index 00000000..f00da5ba Binary files /dev/null and b/tools/trace_replay/fixtures/minecraft-1.21.4-startup.0000092195.png differ diff --git a/tools/trace_replay/fixtures/minecraft-1.21.4-startup.tgz b/tools/trace_replay/fixtures/minecraft-1.21.4-startup.tgz new file mode 100644 index 00000000..e533edbf Binary files /dev/null and b/tools/trace_replay/fixtures/minecraft-1.21.4-startup.tgz differ diff --git a/tools/trace_replay/run_trace_case.cmake b/tools/trace_replay/run_trace_case.cmake new file mode 100644 index 00000000..80261fd7 --- /dev/null +++ b/tools/trace_replay/run_trace_case.cmake @@ -0,0 +1,116 @@ +foreach(required TRACE_REPLAY_EXE MOBILEGL_LIBRARY TRACE_ARCHIVE TRACE_FILE TRACE_GOLDEN TRACE_OUTPUT_DIR TRACE_BACKEND TRACE_CASE_NAME TRACE_TARGET_CALL TRACE_WIDTH TRACE_HEIGHT) + if(NOT DEFINED ${required} OR "${${required}}" STREQUAL "") + message(FATAL_ERROR "${required} is required") + endif() +endforeach() + +if(NOT DEFINED TRACE_TOLERANCE OR "${TRACE_TOLERANCE}" STREQUAL "") + set(TRACE_TOLERANCE 0) +endif() +if(NOT DEFINED TRACE_CROP_X OR "${TRACE_CROP_X}" STREQUAL "") + set(TRACE_CROP_X 0) +endif() +if(NOT DEFINED TRACE_CROP_Y OR "${TRACE_CROP_Y}" STREQUAL "") + set(TRACE_CROP_Y 0) +endif() +if(NOT DEFINED TRACE_CROP_WIDTH OR "${TRACE_CROP_WIDTH}" STREQUAL "") + set(TRACE_CROP_WIDTH 0) +endif() +if(NOT DEFINED TRACE_CROP_HEIGHT OR "${TRACE_CROP_HEIGHT}" STREQUAL "") + set(TRACE_CROP_HEIGHT 0) +endif() +if(NOT DEFINED TRACE_FUZZ_PERCENT OR "${TRACE_FUZZ_PERCENT}" STREQUAL "") + set(TRACE_FUZZ_PERCENT 20) +endif() + +if(EXISTS "${TRACE_OUTPUT_DIR}") + file(REMOVE_RECURSE "${TRACE_OUTPUT_DIR}") +endif() +file(MAKE_DIRECTORY "${TRACE_OUTPUT_DIR}/input") +file(MAKE_DIRECTORY "${TRACE_OUTPUT_DIR}/output") + +execute_process( + COMMAND "${CMAKE_COMMAND}" -E tar xzf "${TRACE_ARCHIVE}" + WORKING_DIRECTORY "${TRACE_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 ${TRACE_ARCHIVE}") +endif() + +set(trace_path "${TRACE_OUTPUT_DIR}/input/${TRACE_FILE}") +if(NOT EXISTS "${trace_path}") + message(FATAL_ERROR "extracted trace was not found at ${trace_path}") +endif() + +execute_process( + COMMAND "${TRACE_REPLAY_EXE}" + --trace "${trace_path}" + --golden "${TRACE_GOLDEN}" + --output "${TRACE_OUTPUT_DIR}/output" + --backend "${TRACE_BACKEND}" + --mobilegl-library "${MOBILEGL_LIBRARY}" + --target-call "${TRACE_TARGET_CALL}" + --width "${TRACE_WIDTH}" + --height "${TRACE_HEIGHT}" + --tolerance "${TRACE_TOLERANCE}" + --crop-x "${TRACE_CROP_X}" + --crop-y "${TRACE_CROP_Y}" + --crop-width "${TRACE_CROP_WIDTH}" + --crop-height "${TRACE_CROP_HEIGHT}" + --fuzz-percent "${TRACE_FUZZ_PERCENT}" + RESULT_VARIABLE replay_result + OUTPUT_VARIABLE replay_stdout + ERROR_VARIABLE replay_stderr) + +message(STATUS "${replay_stdout}") +message(STATUS "${replay_stderr}") + +set(retrace_log "${TRACE_OUTPUT_DIR}/output/retrace.log") +set(mobilegl_log "${TRACE_OUTPUT_DIR}/output/mobilegl.log") +if(EXISTS "${retrace_log}") + file(STRINGS "${retrace_log}" gl_identity_lines REGEX "MOBILEGL_TRACE_GL_") + foreach(line IN LISTS gl_identity_lines) + message(STATUS "${line}") + endforeach() +endif() + +set(result_json "${TRACE_OUTPUT_DIR}/output/result.json") +if(DEFINED TRACE_ARTIFACT_DIR AND NOT "${TRACE_ARTIFACT_DIR}" STREQUAL "") + file(MAKE_DIRECTORY "${TRACE_ARTIFACT_DIR}") + set(actual_png "${TRACE_OUTPUT_DIR}/output/actual.png") + if(EXISTS "${actual_png}") + file(COPY_FILE "${actual_png}" "${TRACE_ARTIFACT_DIR}/${TRACE_CASE_NAME}-${TRACE_BACKEND}-actual.png") + endif() + if(EXISTS "${result_json}") + file(COPY_FILE "${result_json}" "${TRACE_ARTIFACT_DIR}/${TRACE_CASE_NAME}-${TRACE_BACKEND}-result.json") + endif() + if(EXISTS "${retrace_log}") + file(COPY_FILE "${retrace_log}" "${TRACE_ARTIFACT_DIR}/${TRACE_CASE_NAME}-${TRACE_BACKEND}-retrace.log") + endif() + if(EXISTS "${mobilegl_log}") + file(COPY_FILE "${mobilegl_log}" "${TRACE_ARTIFACT_DIR}/${TRACE_CASE_NAME}-${TRACE_BACKEND}-mobilegl.log") + endif() +endif() + +if(EXISTS "${result_json}") + file(READ "${result_json}" result_contents) + message(STATUS "${result_contents}") +else() + if(EXISTS "${retrace_log}") + file(READ "${retrace_log}" retrace_log_contents) + message(STATUS "${retrace_log_contents}") + endif() + if(EXISTS "${mobilegl_log}") + file(READ "${mobilegl_log}" mobilegl_log_contents) + message(STATUS "${mobilegl_log_contents}") + endif() + message(FATAL_ERROR "trace replay did not write ${result_json}") +endif() + +if(NOT replay_result EQUAL 0) + message(FATAL_ERROR "${TRACE_CASE_NAME} ${TRACE_BACKEND} trace replay failed with status ${replay_result}") +endif()