From a88ca75c1412458fb7b4790d2c12d0c3828d98de Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 15 Jun 2026 20:37:40 +0800 Subject: [PATCH] [Fix] (trace-replay): run Vulkan retrace headlessly --- .github/workflows/retrace.yml | 4 +- .../BackendObject_DirectVulkan.cpp | 26 ++++ .../DirectVulkan/BackendObject_DirectVulkan.h | 2 + .../DirectVulkan/Renderer/SwapchainObject.cpp | 21 ++- .../DirectVulkan/Renderer/SwapchainObject.h | 2 +- .../DirectVulkan/Renderer/VulkanRenderer.cpp | 59 ++++++-- .../DirectVulkan/VulkanRendererConfig.h | 2 + .../app/src/trace/cpp/trace_replay_core.cpp | 3 + tools/trace_replay/CMakeLists.txt | 9 +- tools/trace_replay/apitrace_glws_egl.cpp | 130 +----------------- tools/trace_replay/run_openra_replay.cmake | 28 ++-- 11 files changed, 139 insertions(+), 147 deletions(-) diff --git a/.github/workflows/retrace.yml b/.github/workflows/retrace.yml index fb55a48f..e4a11684 100644 --- a/.github/workflows/retrace.yml +++ b/.github/workflows/retrace.yml @@ -42,7 +42,7 @@ jobs: - 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 xvfb + 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: | @@ -70,7 +70,7 @@ jobs: - name: Retrace and validate Magma (DirectVulkan) working-directory: ${{env.TEST_ROOT}}/build-retrace - run: xvfb-run -a ctest -V -R MobileGLTraceReplay.OpenRA.DirectVulkan + run: ctest -V -R MobileGLTraceReplay.OpenRA.DirectVulkan - name: Upload actual image if: always() diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp index 557394b1..7137eea0 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp @@ -54,6 +54,16 @@ namespace MobileGL::MG_Backend::DirectVulkan { return true; } + Bool BackendObject_DirectVulkan::InitPbufferSurface(EGLint width, EGLint height) { + VulkanRendererConfig config; + config.SurfaceWidth = static_cast(std::max(width, 1)); + config.SurfaceHeight = static_cast(std::max(height, 1)); + pVulkanRenderer = MakeUnique(0, config); + MOBILEGL_ASSERT(pVulkanRenderer != nullptr, "InitPbufferSurface: VulkanRenderer creation failed"); + pVulkanRenderer->Initialize(); + return true; + } + void BackendObject_DirectVulkan::Initialize() { m_initialized = true; } @@ -112,6 +122,22 @@ namespace MobileGL::MG_Backend::DirectVulkan { return BackendObject::CreateEGLWindowSurface(handle); } + Bool BackendObject_DirectVulkan::CreateEGLPbufferSurface(EGLint width, EGLint height) { + const std::lock_guard lock(m_eglStateMutex); + if (!m_initialized) { + MGLOG_E("DirectVulkan backend not initialized"); + return false; + } + if (m_eglSurfaceInitialized && m_eglSurfaceKind == SurfaceKind::Pbuffer) { + return true; + } + if (m_eglSurfaceInitialized || pVulkanRenderer) { + pVulkanRenderer.reset(); + ResetEGLRuntimeState(); + } + return BackendObject::CreateEGLPbufferSurface(width, height); + } + Bool BackendObject_DirectVulkan::MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { const std::lock_guard lock(m_eglStateMutex); if (IsReleaseCurrentRequest(dpy, draw, read, ctx)) { diff --git a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h index 7058cb1a..73571aa2 100644 --- a/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h +++ b/MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h @@ -22,6 +22,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { Bool InitCapabilities() override; Bool InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor) override; Bool CreateEGLWindowSurface(const WindowHandle& handle) override; + Bool CreateEGLPbufferSurface(EGLint width, EGLint height) override; Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) override; Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) override; void ReleaseEGLResources() override; @@ -34,6 +35,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { void ApplyVulkanCapabilitiesForTesting(const MG_External::VulkanCapabilities& capabilities); private: + Bool InitPbufferSurface(EGLint width, EGLint height) override; void UpdateAdvertisedExtensions(); void UpdateDynamicBackendParameters(); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp index 7e4fd215..17023aba 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp @@ -105,7 +105,15 @@ namespace MobileGL::MG_Backend::DirectVulkan { VkSurfaceFormatKHR SwapchainObject::ChooseSwapchainSurfaceFormat( const Vector& availableFormats) { for (const auto& availableFormat : availableFormats) { - if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && + if ((availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM || + availableFormat.format == VK_FORMAT_R8G8B8A8_UNORM) && + availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return availableFormat; + } + } + for (const auto& availableFormat : availableFormats) { + if ((availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB || + availableFormat.format == VK_FORMAT_R8G8B8A8_SRGB) && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { return availableFormat; } @@ -130,7 +138,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { } void SwapchainObject::Create(VkDevice device, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, - Uint32 graphicsQueueFamily, Uint32 presentQueueFamily, Uint32 minImageCountHint) { + Uint32 graphicsQueueFamily, Uint32 presentQueueFamily, Uint32 minImageCountHint, + VkExtent2D desiredExtent) { const auto swapchainCapabilities = GetSwapchainCapabilities(physicalDevice, surface); MOBILEGL_ASSERT(swapchainCapabilities.IsComplete(), "SwapchainObject::Create failed: incomplete swapchain capabilities"); @@ -167,6 +176,14 @@ namespace MobileGL::MG_Backend::DirectVulkan { createInfo.imageFormat = pickedSurfaceFormat.format; createInfo.imageColorSpace = pickedSurfaceFormat.colorSpace; createInfo.imageExtent = swapchainCaps.currentExtent; + if (createInfo.imageExtent.width == UINT32_MAX || createInfo.imageExtent.height == UINT32_MAX) { + createInfo.imageExtent.width = std::clamp(desiredExtent.width, + swapchainCaps.minImageExtent.width, + swapchainCaps.maxImageExtent.width); + createInfo.imageExtent.height = std::clamp(desiredExtent.height, + swapchainCaps.minImageExtent.height, + swapchainCaps.maxImageExtent.height); + } if (swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR || swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) { std::swap(createInfo.imageExtent.width, createInfo.imageExtent.height); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.h index 2179f428..663a5f52 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.h @@ -29,7 +29,7 @@ namespace MobileGL::MG_Backend::DirectVulkan { static VkPresentModeKHR ChooseSwapchainPresentMode(const Vector& availablePresentModes); void Create(VkDevice device, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, Uint32 graphicsQueueFamily, - Uint32 presentQueueFamily, Uint32 minImageCountHint); + Uint32 presentQueueFamily, Uint32 minImageCountHint, VkExtent2D desiredExtent); void Shutdown(VkDevice device); VkSwapchainKHR GetHandle() const { return m_swapchain; } diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index e9fa3a98..b093cf1e 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -4927,10 +4927,20 @@ void main() { if (shouldDumpPresent) { FILE* dump = std::fopen(presentDumpPath, "wb"); if (dump != nullptr) { + const VkFormat presentFormat = m_swapchainObject.GetSurfaceFormat().format; + const Bool presentIsBgra = presentFormat == VK_FORMAT_B8G8R8A8_UNORM || + presentFormat == VK_FORMAT_B8G8R8A8_SRGB || + presentFormat == VK_FORMAT_B8G8R8A8_SNORM || + presentFormat == VK_FORMAT_B8G8R8A8_USCALED || + presentFormat == VK_FORMAT_B8G8R8A8_SSCALED; std::fprintf(dump, "P6\n%u %u\n255\n", presentStatsExtent.width, presentStatsExtent.height); for (SizeT i = 0; i < pixelCount; ++i) { const Uint8* p = pixels + i * 4; - const Uint8 rgb[3] = {p[0], p[1], p[2]}; + const Uint8 rgb[3] = { + presentIsBgra ? p[2] : p[0], + p[1], + presentIsBgra ? p[0] : p[2], + }; std::fwrite(rgb, 1, sizeof(rgb), dump); } std::fclose(dump); @@ -5011,19 +5021,22 @@ void main() { instanceInfo.pApplicationInfo = &appInfo; // Extensions - Vector exts = {VK_KHR_SURFACE_EXTENSION_NAME, + Vector exts = {VK_KHR_SURFACE_EXTENSION_NAME}; + if (!m_window) { + exts.push_back(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME); + } else { #ifdef VK_USE_PLATFORM_ANDROID_KHR - VK_KHR_ANDROID_SURFACE_EXTENSION_NAME + exts.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); #elif defined VK_USE_PLATFORM_WIN32_KHR - VK_KHR_WIN32_SURFACE_EXTENSION_NAME + exts.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); #elif defined VK_USE_PLATFORM_METAL_EXT - VK_EXT_METAL_SURFACE_EXTENSION_NAME + exts.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); #elif defined VK_USE_PLATFORM_XLIB_KHR - VK_KHR_XLIB_SURFACE_EXTENSION_NAME + exts.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); #else #warning "VulkanContext::CreateInstance: VK_KHR_*_surface extension not defined on this platform" #endif - }; // TODO: support more platforms + } // TODO: support more platforms #if defined(VK_USE_PLATFORM_METAL_EXT) exts.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); @@ -5034,6 +5047,11 @@ void main() { exts.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } + for (const char* ext : exts) { + MOBILEGL_ASSERT(IsExtensionSupported(m_extensions, ext), "Required Vulkan instance extension not found: %s", + ext); + } + instanceInfo.enabledExtensionCount = exts.size(); instanceInfo.ppEnabledExtensionNames = exts.data(); @@ -5180,6 +5198,14 @@ void main() { return false; } + // Accept software/virtual/other devices when no discrete or integrated GPU + // has been selected yet. This is important for Linux headless CI using lavapipe. + if (!otherDevice.IsComplete()) { + outBetterDevice = newDevice; + MGLOG_I(" Picked physical device. (Reason: First suitable device)"); + return true; + } + // Pick discrete GPU if (newDevice.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && otherDevice.properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { @@ -5199,7 +5225,7 @@ void main() { // Ignore other GPU when discrete GPU found if (newDevice.properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && otherDevice.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { - outBetterDevice = newDevice; + outBetterDevice = otherDevice; MGLOG_I(" Ignored physical device. (Reason: Already picked discrete GPU)"); return false; } @@ -5395,10 +5421,14 @@ void main() { } void VulkanRenderer::CreateSwapchain() { + const VkExtent2D desiredExtent = { + std::max(m_config.SurfaceWidth, 1), + std::max(m_config.SurfaceHeight, 1), + }; m_swapchainObject.Create(m_device, m_physicalDevice.handle, m_surface, static_cast(m_physicalDevice.queueFamilies.graphicsFamily), static_cast(m_physicalDevice.queueFamilies.presentFamily), - m_config.MaxFramesInFlight); + m_config.MaxFramesInFlight, desiredExtent); } void VulkanRenderer::CreateCommandPool() { @@ -5410,6 +5440,17 @@ void main() { } void VulkanRenderer::CreateSurface() { + if (!m_window) { + auto* createHeadlessSurface = + reinterpret_cast( + vkGetInstanceProcAddr(m_instance, "vkCreateHeadlessSurfaceEXT")); + MOBILEGL_ASSERT(createHeadlessSurface != nullptr, + "VK_EXT_headless_surface is not available for DirectVulkan pbuffer surface"); + VkHeadlessSurfaceCreateInfoEXT sci{VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT}; + VK_VERIFY(createHeadlessSurface(m_instance, &sci, nullptr, &m_surface), + "vkCreateHeadlessSurfaceEXT failed"); + return; + } #if defined VK_USE_PLATFORM_ANDROID_KHR auto* nativeWindow = static_cast(m_window); if (!nativeWindow) throw RuntimeError("ANativeWindowType is null"); diff --git a/MobileGL/MG_Backend/DirectVulkan/VulkanRendererConfig.h b/MobileGL/MG_Backend/DirectVulkan/VulkanRendererConfig.h index 5e0d5520..e3678a7a 100644 --- a/MobileGL/MG_Backend/DirectVulkan/VulkanRendererConfig.h +++ b/MobileGL/MG_Backend/DirectVulkan/VulkanRendererConfig.h @@ -15,6 +15,8 @@ namespace MobileGL::MG_Backend::DirectVulkan { String AppName = "MobileGL-VulkanRenderer"; MobileGL::Version Version = MG_Config::CoreVersion; Uint64 CacheVersion = MG_Config::CacheVersion; + Uint32 SurfaceWidth = 1; + Uint32 SurfaceHeight = 1; #if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG Bool EnableValidationLayers = true; #else 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 d24bf8b1..a65460e4 100644 --- a/android-plugin/app/src/trace/cpp/trace_replay_core.cpp +++ b/android-plugin/app/src/trace/cpp/trace_replay_core.cpp @@ -560,6 +560,7 @@ Result RunTraceReplay(const Request& request) { Result result; result.resultPath = request.outputDir + "/result.json"; result.actualPath = request.outputDir + "/actual.png"; + const std::string mobileGlLogPath = request.outputDir + "/mobilegl.log"; if (!EnsureDirectory(request.outputDir)) { result.statusCode = STATUS_IO_ERROR; @@ -598,6 +599,8 @@ Result RunTraceReplay(const Request& request) { unsetenv("MOBILEGL_DESCRIPTOR_STATS"); } + setenv("MOBILEGL_LOG_FILE_PATH", mobileGlLogPath.c_str(), 1); + std::string mobileGlError; if (!LoadMobileGL(request, mobileGlError)) { result.statusCode = STATUS_MOBILEGL_LOAD_ERROR; diff --git a/tools/trace_replay/CMakeLists.txt b/tools/trace_replay/CMakeLists.txt index b9b287b4..743d4b64 100644 --- a/tools/trace_replay/CMakeLists.txt +++ b/tools/trace_replay/CMakeLists.txt @@ -269,8 +269,13 @@ function(add_openra_trace_replay_test 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) - set_tests_properties(MobileGLTraceReplay.OpenRA.${BACKEND} PROPERTIES - ENVIRONMENT "EGL_PLATFORM=surfaceless;LIBGL_ALWAYS_SOFTWARE=1;MESA_GL_VERSION_OVERRIDE=3.3;MESA_GLSL_VERSION_OVERRIDE=330") + if(BACKEND STREQUAL "DirectGLES") + set_tests_properties(MobileGLTraceReplay.OpenRA.${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 + ENVIRONMENT "LIBGL_ALWAYS_SOFTWARE=1;MESA_GL_VERSION_OVERRIDE=3.3;MESA_GLSL_VERSION_OVERRIDE=330") + endif() endfunction() add_openra_trace_replay_test(DirectGLES) diff --git a/tools/trace_replay/apitrace_glws_egl.cpp b/tools/trace_replay/apitrace_glws_egl.cpp index d2d18d6e..d189de26 100644 --- a/tools/trace_replay/apitrace_glws_egl.cpp +++ b/tools/trace_replay/apitrace_glws_egl.cpp @@ -16,7 +16,6 @@ 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 PfnEglCreateWindowSurface = EGLSurface (*)(EGLDisplay, EGLConfig, EGLNativeWindowType, const EGLint *); using PfnEglDestroyContext = EGLBoolean (*)(EGLDisplay, EGLContext); using PfnEglDestroySurface = EGLBoolean (*)(EGLDisplay, EGLSurface); using PfnEglGetConfigAttrib = EGLBoolean (*)(EGLDisplay, EGLConfig, EGLint, EGLint *); @@ -34,7 +33,6 @@ struct EglFns { PfnEglChooseConfig chooseConfig = nullptr; PfnEglCreateContext createContext = nullptr; PfnEglCreatePbufferSurface createPbufferSurface = nullptr; - PfnEglCreateWindowSurface createWindowSurface = nullptr; PfnEglDestroyContext destroyContext = nullptr; PfnEglDestroySurface destroySurface = nullptr; PfnEglGetConfigAttrib getConfigAttrib = nullptr; @@ -47,32 +45,7 @@ struct EglFns { PfnEglTerminate terminate = nullptr; }; -struct X11Fns { - void *lib = nullptr; - void *display = nullptr; - using XOpenDisplay = void *(*)(const char *); - using XCloseDisplay = int (*)(void *); - using XDefaultScreen = int (*)(void *); - using XDefaultRootWindow = unsigned long (*)(void *); - using XBlackPixel = unsigned long (*)(void *, int); - using XCreateSimpleWindow = unsigned long (*)(void *, unsigned long, int, int, unsigned int, unsigned int, - unsigned int, unsigned long, unsigned long); - using XDestroyWindow = int (*)(void *, unsigned long); - using XMapWindow = int (*)(void *, unsigned long); - using XFlush = int (*)(void *); - XOpenDisplay openDisplay = nullptr; - XCloseDisplay closeDisplay = nullptr; - XDefaultScreen defaultScreen = nullptr; - XDefaultRootWindow defaultRootWindow = nullptr; - XBlackPixel blackPixel = nullptr; - XCreateSimpleWindow createSimpleWindow = nullptr; - XDestroyWindow destroyWindow = nullptr; - XMapWindow mapWindow = nullptr; - XFlush flush = nullptr; -}; - EglFns gEgl; -X11Fns gX11; EGLDisplay gDisplay = EGL_NO_DISPLAY; void *gMobileGl = nullptr; const glws::Drawable *gCurrentDrawable = nullptr; @@ -135,7 +108,6 @@ bool LoadEgl() { Load(gEgl.chooseConfig, "eglChooseConfig") && Load(gEgl.createContext, "eglCreateContext") && Load(gEgl.createPbufferSurface, "eglCreatePbufferSurface") && - Load(gEgl.createWindowSurface, "eglCreateWindowSurface") && Load(gEgl.destroyContext, "eglDestroyContext") && Load(gEgl.destroySurface, "eglDestroySurface") && Load(gEgl.getConfigAttrib, "eglGetConfigAttrib") && @@ -148,79 +120,6 @@ bool LoadEgl() { Load(gEgl.terminate, "eglTerminate"); } -bool IsDirectVulkanBackend() { - const char *backend = std::getenv("MOBILEGL_BACKEND_TYPE"); - return backend != nullptr && std::strcmp(backend, "DirectVulkan") == 0; -} - -template -bool LoadX11(T &slot, const char *name) { - slot = reinterpret_cast(dlsym(gX11.lib, name)); - if (slot == nullptr) { - std::cerr << "error: failed to resolve " << name << " from X11\n"; - return false; - } - return true; -} - -bool EnsureX11() { - if (gX11.display != nullptr) { - return true; - } - if (gX11.lib == nullptr) { - gX11.lib = dlopen("libX11.so.6", RTLD_LOCAL | RTLD_NOW); - if (gX11.lib == nullptr) { - gX11.lib = dlopen("libX11.so", RTLD_LOCAL | RTLD_NOW); - } - if (gX11.lib == nullptr) { - std::cerr << "error: failed to open libX11 for DirectVulkan trace replay\n"; - return false; - } - if (!LoadX11(gX11.openDisplay, "XOpenDisplay") || - !LoadX11(gX11.closeDisplay, "XCloseDisplay") || - !LoadX11(gX11.defaultScreen, "XDefaultScreen") || - !LoadX11(gX11.defaultRootWindow, "XDefaultRootWindow") || - !LoadX11(gX11.blackPixel, "XBlackPixel") || - !LoadX11(gX11.createSimpleWindow, "XCreateSimpleWindow") || - !LoadX11(gX11.destroyWindow, "XDestroyWindow") || - !LoadX11(gX11.mapWindow, "XMapWindow") || - !LoadX11(gX11.flush, "XFlush")) { - return false; - } - } - gX11.display = gX11.openDisplay(std::getenv("DISPLAY")); - if (gX11.display == nullptr) { - std::cerr << "error: XOpenDisplay failed for DirectVulkan trace replay\n"; - return false; - } - return true; -} - -unsigned long CreateX11Window(int width, int height) { - if (!EnsureX11()) { - return 0; - } - const int screen = gX11.defaultScreen(gX11.display); - const unsigned long root = gX11.defaultRootWindow(gX11.display); - const unsigned long black = gX11.blackPixel(gX11.display, screen); - const unsigned long window = gX11.createSimpleWindow(gX11.display, root, 0, 0, - static_cast(std::max(width, 1)), - static_cast(std::max(height, 1)), - 0, black, black); - if (window != 0) { - gX11.mapWindow(gX11.display, window); - gX11.flush(gX11.display); - } - return window; -} - -void DestroyX11Window(unsigned long window) { - if (window != 0 && gX11.display != nullptr && gX11.destroyWindow != nullptr) { - gX11.destroyWindow(gX11.display, window); - gX11.flush(gX11.display); - } -} - void PrintGlIdentityOnce() { if (gPrintedGlIdentity) { return; @@ -261,7 +160,6 @@ public: class EglDrawable final : public glws::Drawable { public: EGLSurface surface = EGL_NO_SURFACE; - unsigned long x11Window = 0; EglDrawable(const EglVisual *visual, int width, int height, bool pbuffer) : Drawable(visual, width, height, pbuffer) { @@ -309,26 +207,14 @@ public: private: void createSurface() { - const int resolvedWidth = ResolveWidth(width); - const int resolvedHeight = ResolveHeight(height); - if (IsDirectVulkanBackend()) { - x11Window = CreateX11Window(resolvedWidth, resolvedHeight); - if (x11Window == 0) { - surface = EGL_NO_SURFACE; - return; - } - surface = gEgl.createWindowSurface(gDisplay, static_cast(visual)->config, - reinterpret_cast(x11Window), nullptr); - } else { - const EGLint attribs[] = { - EGL_WIDTH, resolvedWidth, - EGL_HEIGHT, resolvedHeight, - EGL_NONE, - }; - surface = gEgl.createPbufferSurface(gDisplay, static_cast(visual)->config, attribs); - } + const EGLint attribs[] = { + EGL_WIDTH, ResolveWidth(width), + EGL_HEIGHT, ResolveHeight(height), + EGL_NONE, + }; + surface = gEgl.createPbufferSurface(gDisplay, static_cast(visual)->config, attribs); if (surface == EGL_NO_SURFACE) { - std::cerr << "error: EGL surface creation failed: 0x" << std::hex + std::cerr << "error: EGL pbuffer creation failed: 0x" << std::hex << gEgl.getError() << std::dec << "\n"; } } @@ -338,8 +224,6 @@ private: gEgl.destroySurface(gDisplay, surface); surface = EGL_NO_SURFACE; } - DestroyX11Window(x11Window); - x11Window = 0; } }; diff --git a/tools/trace_replay/run_openra_replay.cmake b/tools/trace_replay/run_openra_replay.cmake index 56466565..640ed9ef 100644 --- a/tools/trace_replay/run_openra_replay.cmake +++ b/tools/trace_replay/run_openra_replay.cmake @@ -50,15 +50,8 @@ execute_process( 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() - set(retrace_log "${OPENRA_OUTPUT_DIR}/output/retrace.log") +set(mobilegl_log "${OPENRA_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) @@ -66,6 +59,7 @@ if(EXISTS "${retrace_log}") endforeach() endif() +set(result_json "${OPENRA_OUTPUT_DIR}/output/result.json") if(DEFINED OPENRA_ARTIFACT_DIR AND NOT "${OPENRA_ARTIFACT_DIR}" STREQUAL "") file(MAKE_DIRECTORY "${OPENRA_ARTIFACT_DIR}") set(actual_png "${OPENRA_OUTPUT_DIR}/output/actual.png") @@ -78,6 +72,24 @@ if(DEFINED OPENRA_ARTIFACT_DIR AND NOT "${OPENRA_ARTIFACT_DIR}" STREQUAL "") if(EXISTS "${retrace_log}") file(COPY_FILE "${retrace_log}" "${OPENRA_ARTIFACT_DIR}/openra-${OPENRA_BACKEND}-retrace.log") endif() + if(EXISTS "${mobilegl_log}") + file(COPY_FILE "${mobilegl_log}" "${OPENRA_ARTIFACT_DIR}/openra-${OPENRA_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)