mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Fix] (trace-replay): run Vulkan retrace headlessly
This commit is contained in:
@@ -42,7 +42,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
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
|
- name: Show installed toolchain
|
||||||
run: |
|
run: |
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Retrace and validate Magma (DirectVulkan)
|
- name: Retrace and validate Magma (DirectVulkan)
|
||||||
working-directory: ${{env.TEST_ROOT}}/build-retrace
|
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
|
- name: Upload actual image
|
||||||
if: always()
|
if: always()
|
||||||
|
|||||||
@@ -54,6 +54,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool BackendObject_DirectVulkan::InitPbufferSurface(EGLint width, EGLint height) {
|
||||||
|
VulkanRendererConfig config;
|
||||||
|
config.SurfaceWidth = static_cast<Uint32>(std::max<EGLint>(width, 1));
|
||||||
|
config.SurfaceHeight = static_cast<Uint32>(std::max<EGLint>(height, 1));
|
||||||
|
pVulkanRenderer = MakeUnique<MG_Backend::DirectVulkan::VulkanRenderer>(0, config);
|
||||||
|
MOBILEGL_ASSERT(pVulkanRenderer != nullptr, "InitPbufferSurface: VulkanRenderer creation failed");
|
||||||
|
pVulkanRenderer->Initialize();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void BackendObject_DirectVulkan::Initialize() {
|
void BackendObject_DirectVulkan::Initialize() {
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
}
|
}
|
||||||
@@ -112,6 +122,22 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return BackendObject::CreateEGLWindowSurface(handle);
|
return BackendObject::CreateEGLWindowSurface(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool BackendObject_DirectVulkan::CreateEGLPbufferSurface(EGLint width, EGLint height) {
|
||||||
|
const std::lock_guard<std::recursive_mutex> 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) {
|
Bool BackendObject_DirectVulkan::MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
|
||||||
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
|
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
|
||||||
if (IsReleaseCurrentRequest(dpy, draw, read, ctx)) {
|
if (IsReleaseCurrentRequest(dpy, draw, read, ctx)) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Bool InitCapabilities() override;
|
Bool InitCapabilities() override;
|
||||||
Bool InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor) override;
|
Bool InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor) override;
|
||||||
Bool CreateEGLWindowSurface(const WindowHandle& handle) 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 MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) override;
|
||||||
Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) override;
|
Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) override;
|
||||||
void ReleaseEGLResources() override;
|
void ReleaseEGLResources() override;
|
||||||
@@ -34,6 +35,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void ApplyVulkanCapabilitiesForTesting(const MG_External::VulkanCapabilities& capabilities);
|
void ApplyVulkanCapabilitiesForTesting(const MG_External::VulkanCapabilities& capabilities);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Bool InitPbufferSurface(EGLint width, EGLint height) override;
|
||||||
void UpdateAdvertisedExtensions();
|
void UpdateAdvertisedExtensions();
|
||||||
void UpdateDynamicBackendParameters();
|
void UpdateDynamicBackendParameters();
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
VkSurfaceFormatKHR SwapchainObject::ChooseSwapchainSurfaceFormat(
|
VkSurfaceFormatKHR SwapchainObject::ChooseSwapchainSurfaceFormat(
|
||||||
const Vector<VkSurfaceFormatKHR>& availableFormats) {
|
const Vector<VkSurfaceFormatKHR>& availableFormats) {
|
||||||
for (const auto& availableFormat : 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) {
|
availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||||
return availableFormat;
|
return availableFormat;
|
||||||
}
|
}
|
||||||
@@ -130,7 +138,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SwapchainObject::Create(VkDevice device, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
|
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);
|
const auto swapchainCapabilities = GetSwapchainCapabilities(physicalDevice, surface);
|
||||||
MOBILEGL_ASSERT(swapchainCapabilities.IsComplete(),
|
MOBILEGL_ASSERT(swapchainCapabilities.IsComplete(),
|
||||||
"SwapchainObject::Create failed: incomplete swapchain capabilities");
|
"SwapchainObject::Create failed: incomplete swapchain capabilities");
|
||||||
@@ -167,6 +176,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
createInfo.imageFormat = pickedSurfaceFormat.format;
|
createInfo.imageFormat = pickedSurfaceFormat.format;
|
||||||
createInfo.imageColorSpace = pickedSurfaceFormat.colorSpace;
|
createInfo.imageColorSpace = pickedSurfaceFormat.colorSpace;
|
||||||
createInfo.imageExtent = swapchainCaps.currentExtent;
|
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 ||
|
if (swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
|
||||||
swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
|
swapchainCaps.currentTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
|
||||||
std::swap(createInfo.imageExtent.width, createInfo.imageExtent.height);
|
std::swap(createInfo.imageExtent.width, createInfo.imageExtent.height);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
static VkPresentModeKHR ChooseSwapchainPresentMode(const Vector<VkPresentModeKHR>& availablePresentModes);
|
static VkPresentModeKHR ChooseSwapchainPresentMode(const Vector<VkPresentModeKHR>& availablePresentModes);
|
||||||
|
|
||||||
void Create(VkDevice device, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, Uint32 graphicsQueueFamily,
|
void Create(VkDevice device, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, Uint32 graphicsQueueFamily,
|
||||||
Uint32 presentQueueFamily, Uint32 minImageCountHint);
|
Uint32 presentQueueFamily, Uint32 minImageCountHint, VkExtent2D desiredExtent);
|
||||||
void Shutdown(VkDevice device);
|
void Shutdown(VkDevice device);
|
||||||
|
|
||||||
VkSwapchainKHR GetHandle() const { return m_swapchain; }
|
VkSwapchainKHR GetHandle() const { return m_swapchain; }
|
||||||
|
|||||||
@@ -4927,10 +4927,20 @@ void main() {
|
|||||||
if (shouldDumpPresent) {
|
if (shouldDumpPresent) {
|
||||||
FILE* dump = std::fopen(presentDumpPath, "wb");
|
FILE* dump = std::fopen(presentDumpPath, "wb");
|
||||||
if (dump != nullptr) {
|
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);
|
std::fprintf(dump, "P6\n%u %u\n255\n", presentStatsExtent.width, presentStatsExtent.height);
|
||||||
for (SizeT i = 0; i < pixelCount; ++i) {
|
for (SizeT i = 0; i < pixelCount; ++i) {
|
||||||
const Uint8* p = pixels + i * 4;
|
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::fwrite(rgb, 1, sizeof(rgb), dump);
|
||||||
}
|
}
|
||||||
std::fclose(dump);
|
std::fclose(dump);
|
||||||
@@ -5011,19 +5021,22 @@ void main() {
|
|||||||
instanceInfo.pApplicationInfo = &appInfo;
|
instanceInfo.pApplicationInfo = &appInfo;
|
||||||
|
|
||||||
// Extensions
|
// Extensions
|
||||||
Vector<const char*> exts = {VK_KHR_SURFACE_EXTENSION_NAME,
|
Vector<const char*> 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
|
#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
|
#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
|
#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
|
#elif defined VK_USE_PLATFORM_XLIB_KHR
|
||||||
VK_KHR_XLIB_SURFACE_EXTENSION_NAME
|
exts.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
||||||
#else
|
#else
|
||||||
#warning "VulkanContext::CreateInstance: VK_KHR_*_surface extension not defined on this platform"
|
#warning "VulkanContext::CreateInstance: VK_KHR_*_surface extension not defined on this platform"
|
||||||
#endif
|
#endif
|
||||||
}; // TODO: support more platforms
|
} // TODO: support more platforms
|
||||||
|
|
||||||
#if defined(VK_USE_PLATFORM_METAL_EXT)
|
#if defined(VK_USE_PLATFORM_METAL_EXT)
|
||||||
exts.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
exts.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
||||||
@@ -5034,6 +5047,11 @@ void main() {
|
|||||||
exts.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
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.enabledExtensionCount = exts.size();
|
||||||
instanceInfo.ppEnabledExtensionNames = exts.data();
|
instanceInfo.ppEnabledExtensionNames = exts.data();
|
||||||
|
|
||||||
@@ -5180,6 +5198,14 @@ void main() {
|
|||||||
return false;
|
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
|
// Pick discrete GPU
|
||||||
if (newDevice.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
|
if (newDevice.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
|
||||||
otherDevice.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
|
// Ignore other GPU when discrete GPU found
|
||||||
if (newDevice.properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
|
if (newDevice.properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
|
||||||
otherDevice.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)");
|
MGLOG_I(" Ignored physical device. (Reason: Already picked discrete GPU)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -5395,10 +5421,14 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::CreateSwapchain() {
|
void VulkanRenderer::CreateSwapchain() {
|
||||||
|
const VkExtent2D desiredExtent = {
|
||||||
|
std::max<Uint32>(m_config.SurfaceWidth, 1),
|
||||||
|
std::max<Uint32>(m_config.SurfaceHeight, 1),
|
||||||
|
};
|
||||||
m_swapchainObject.Create(m_device, m_physicalDevice.handle, m_surface,
|
m_swapchainObject.Create(m_device, m_physicalDevice.handle, m_surface,
|
||||||
static_cast<Uint32>(m_physicalDevice.queueFamilies.graphicsFamily),
|
static_cast<Uint32>(m_physicalDevice.queueFamilies.graphicsFamily),
|
||||||
static_cast<Uint32>(m_physicalDevice.queueFamilies.presentFamily),
|
static_cast<Uint32>(m_physicalDevice.queueFamilies.presentFamily),
|
||||||
m_config.MaxFramesInFlight);
|
m_config.MaxFramesInFlight, desiredExtent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::CreateCommandPool() {
|
void VulkanRenderer::CreateCommandPool() {
|
||||||
@@ -5410,6 +5440,17 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::CreateSurface() {
|
void VulkanRenderer::CreateSurface() {
|
||||||
|
if (!m_window) {
|
||||||
|
auto* createHeadlessSurface =
|
||||||
|
reinterpret_cast<PFN_vkCreateHeadlessSurfaceEXT>(
|
||||||
|
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
|
#if defined VK_USE_PLATFORM_ANDROID_KHR
|
||||||
auto* nativeWindow = static_cast<ANativeWindow*>(m_window);
|
auto* nativeWindow = static_cast<ANativeWindow*>(m_window);
|
||||||
if (!nativeWindow) throw RuntimeError("ANativeWindowType is null");
|
if (!nativeWindow) throw RuntimeError("ANativeWindowType is null");
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
String AppName = "MobileGL-VulkanRenderer";
|
String AppName = "MobileGL-VulkanRenderer";
|
||||||
MobileGL::Version Version = MG_Config::CoreVersion;
|
MobileGL::Version Version = MG_Config::CoreVersion;
|
||||||
Uint64 CacheVersion = MG_Config::CacheVersion;
|
Uint64 CacheVersion = MG_Config::CacheVersion;
|
||||||
|
Uint32 SurfaceWidth = 1;
|
||||||
|
Uint32 SurfaceHeight = 1;
|
||||||
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
|
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
|
||||||
Bool EnableValidationLayers = true;
|
Bool EnableValidationLayers = true;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -560,6 +560,7 @@ Result RunTraceReplay(const Request& request) {
|
|||||||
Result result;
|
Result result;
|
||||||
result.resultPath = request.outputDir + "/result.json";
|
result.resultPath = request.outputDir + "/result.json";
|
||||||
result.actualPath = request.outputDir + "/actual.png";
|
result.actualPath = request.outputDir + "/actual.png";
|
||||||
|
const std::string mobileGlLogPath = request.outputDir + "/mobilegl.log";
|
||||||
|
|
||||||
if (!EnsureDirectory(request.outputDir)) {
|
if (!EnsureDirectory(request.outputDir)) {
|
||||||
result.statusCode = STATUS_IO_ERROR;
|
result.statusCode = STATUS_IO_ERROR;
|
||||||
@@ -598,6 +599,8 @@ Result RunTraceReplay(const Request& request) {
|
|||||||
unsetenv("MOBILEGL_DESCRIPTOR_STATS");
|
unsetenv("MOBILEGL_DESCRIPTOR_STATS");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setenv("MOBILEGL_LOG_FILE_PATH", mobileGlLogPath.c_str(), 1);
|
||||||
|
|
||||||
std::string mobileGlError;
|
std::string mobileGlError;
|
||||||
if (!LoadMobileGL(request, mobileGlError)) {
|
if (!LoadMobileGL(request, mobileGlError)) {
|
||||||
result.statusCode = STATUS_MOBILEGL_LOAD_ERROR;
|
result.statusCode = STATUS_MOBILEGL_LOAD_ERROR;
|
||||||
|
|||||||
@@ -269,8 +269,13 @@ function(add_openra_trace_replay_test BACKEND)
|
|||||||
-DOPENRA_OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}/openra/${BACKEND}
|
-DOPENRA_OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}/openra/${BACKEND}
|
||||||
-DOPENRA_ARTIFACT_DIR=${CMAKE_CURRENT_BINARY_DIR}/openra/actual-images
|
-DOPENRA_ARTIFACT_DIR=${CMAKE_CURRENT_BINARY_DIR}/openra/actual-images
|
||||||
-P ${MOBILEGL_TRACE_ROOT}/run_openra_replay.cmake)
|
-P ${MOBILEGL_TRACE_ROOT}/run_openra_replay.cmake)
|
||||||
set_tests_properties(MobileGLTraceReplay.OpenRA.${BACKEND} PROPERTIES
|
if(BACKEND STREQUAL "DirectGLES")
|
||||||
ENVIRONMENT "EGL_PLATFORM=surfaceless;LIBGL_ALWAYS_SOFTWARE=1;MESA_GL_VERSION_OVERRIDE=3.3;MESA_GLSL_VERSION_OVERRIDE=330")
|
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()
|
endfunction()
|
||||||
|
|
||||||
add_openra_trace_replay_test(DirectGLES)
|
add_openra_trace_replay_test(DirectGLES)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ using PfnEglBindApi = EGLBoolean (*)(EGLenum);
|
|||||||
using PfnEglChooseConfig = EGLBoolean (*)(EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *);
|
using PfnEglChooseConfig = EGLBoolean (*)(EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *);
|
||||||
using PfnEglCreateContext = EGLContext (*)(EGLDisplay, EGLConfig, EGLContext, const EGLint *);
|
using PfnEglCreateContext = EGLContext (*)(EGLDisplay, EGLConfig, EGLContext, const EGLint *);
|
||||||
using PfnEglCreatePbufferSurface = EGLSurface (*)(EGLDisplay, EGLConfig, const EGLint *);
|
using PfnEglCreatePbufferSurface = EGLSurface (*)(EGLDisplay, EGLConfig, const EGLint *);
|
||||||
using PfnEglCreateWindowSurface = EGLSurface (*)(EGLDisplay, EGLConfig, EGLNativeWindowType, const EGLint *);
|
|
||||||
using PfnEglDestroyContext = EGLBoolean (*)(EGLDisplay, EGLContext);
|
using PfnEglDestroyContext = EGLBoolean (*)(EGLDisplay, EGLContext);
|
||||||
using PfnEglDestroySurface = EGLBoolean (*)(EGLDisplay, EGLSurface);
|
using PfnEglDestroySurface = EGLBoolean (*)(EGLDisplay, EGLSurface);
|
||||||
using PfnEglGetConfigAttrib = EGLBoolean (*)(EGLDisplay, EGLConfig, EGLint, EGLint *);
|
using PfnEglGetConfigAttrib = EGLBoolean (*)(EGLDisplay, EGLConfig, EGLint, EGLint *);
|
||||||
@@ -34,7 +33,6 @@ struct EglFns {
|
|||||||
PfnEglChooseConfig chooseConfig = nullptr;
|
PfnEglChooseConfig chooseConfig = nullptr;
|
||||||
PfnEglCreateContext createContext = nullptr;
|
PfnEglCreateContext createContext = nullptr;
|
||||||
PfnEglCreatePbufferSurface createPbufferSurface = nullptr;
|
PfnEglCreatePbufferSurface createPbufferSurface = nullptr;
|
||||||
PfnEglCreateWindowSurface createWindowSurface = nullptr;
|
|
||||||
PfnEglDestroyContext destroyContext = nullptr;
|
PfnEglDestroyContext destroyContext = nullptr;
|
||||||
PfnEglDestroySurface destroySurface = nullptr;
|
PfnEglDestroySurface destroySurface = nullptr;
|
||||||
PfnEglGetConfigAttrib getConfigAttrib = nullptr;
|
PfnEglGetConfigAttrib getConfigAttrib = nullptr;
|
||||||
@@ -47,32 +45,7 @@ struct EglFns {
|
|||||||
PfnEglTerminate terminate = nullptr;
|
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;
|
EglFns gEgl;
|
||||||
X11Fns gX11;
|
|
||||||
EGLDisplay gDisplay = EGL_NO_DISPLAY;
|
EGLDisplay gDisplay = EGL_NO_DISPLAY;
|
||||||
void *gMobileGl = nullptr;
|
void *gMobileGl = nullptr;
|
||||||
const glws::Drawable *gCurrentDrawable = nullptr;
|
const glws::Drawable *gCurrentDrawable = nullptr;
|
||||||
@@ -135,7 +108,6 @@ bool LoadEgl() {
|
|||||||
Load(gEgl.chooseConfig, "eglChooseConfig") &&
|
Load(gEgl.chooseConfig, "eglChooseConfig") &&
|
||||||
Load(gEgl.createContext, "eglCreateContext") &&
|
Load(gEgl.createContext, "eglCreateContext") &&
|
||||||
Load(gEgl.createPbufferSurface, "eglCreatePbufferSurface") &&
|
Load(gEgl.createPbufferSurface, "eglCreatePbufferSurface") &&
|
||||||
Load(gEgl.createWindowSurface, "eglCreateWindowSurface") &&
|
|
||||||
Load(gEgl.destroyContext, "eglDestroyContext") &&
|
Load(gEgl.destroyContext, "eglDestroyContext") &&
|
||||||
Load(gEgl.destroySurface, "eglDestroySurface") &&
|
Load(gEgl.destroySurface, "eglDestroySurface") &&
|
||||||
Load(gEgl.getConfigAttrib, "eglGetConfigAttrib") &&
|
Load(gEgl.getConfigAttrib, "eglGetConfigAttrib") &&
|
||||||
@@ -148,79 +120,6 @@ bool LoadEgl() {
|
|||||||
Load(gEgl.terminate, "eglTerminate");
|
Load(gEgl.terminate, "eglTerminate");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsDirectVulkanBackend() {
|
|
||||||
const char *backend = std::getenv("MOBILEGL_BACKEND_TYPE");
|
|
||||||
return backend != nullptr && std::strcmp(backend, "DirectVulkan") == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool LoadX11(T &slot, const char *name) {
|
|
||||||
slot = reinterpret_cast<T>(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<unsigned int>(std::max(width, 1)),
|
|
||||||
static_cast<unsigned int>(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() {
|
void PrintGlIdentityOnce() {
|
||||||
if (gPrintedGlIdentity) {
|
if (gPrintedGlIdentity) {
|
||||||
return;
|
return;
|
||||||
@@ -261,7 +160,6 @@ public:
|
|||||||
class EglDrawable final : public glws::Drawable {
|
class EglDrawable final : public glws::Drawable {
|
||||||
public:
|
public:
|
||||||
EGLSurface surface = EGL_NO_SURFACE;
|
EGLSurface surface = EGL_NO_SURFACE;
|
||||||
unsigned long x11Window = 0;
|
|
||||||
|
|
||||||
EglDrawable(const EglVisual *visual, int width, int height, bool pbuffer)
|
EglDrawable(const EglVisual *visual, int width, int height, bool pbuffer)
|
||||||
: Drawable(visual, width, height, pbuffer) {
|
: Drawable(visual, width, height, pbuffer) {
|
||||||
@@ -309,26 +207,14 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void createSurface() {
|
void createSurface() {
|
||||||
const int resolvedWidth = ResolveWidth(width);
|
const EGLint attribs[] = {
|
||||||
const int resolvedHeight = ResolveHeight(height);
|
EGL_WIDTH, ResolveWidth(width),
|
||||||
if (IsDirectVulkanBackend()) {
|
EGL_HEIGHT, ResolveHeight(height),
|
||||||
x11Window = CreateX11Window(resolvedWidth, resolvedHeight);
|
EGL_NONE,
|
||||||
if (x11Window == 0) {
|
};
|
||||||
surface = EGL_NO_SURFACE;
|
surface = gEgl.createPbufferSurface(gDisplay, static_cast<const EglVisual *>(visual)->config, attribs);
|
||||||
return;
|
|
||||||
}
|
|
||||||
surface = gEgl.createWindowSurface(gDisplay, static_cast<const EglVisual *>(visual)->config,
|
|
||||||
reinterpret_cast<EGLNativeWindowType>(x11Window), nullptr);
|
|
||||||
} else {
|
|
||||||
const EGLint attribs[] = {
|
|
||||||
EGL_WIDTH, resolvedWidth,
|
|
||||||
EGL_HEIGHT, resolvedHeight,
|
|
||||||
EGL_NONE,
|
|
||||||
};
|
|
||||||
surface = gEgl.createPbufferSurface(gDisplay, static_cast<const EglVisual *>(visual)->config, attribs);
|
|
||||||
}
|
|
||||||
if (surface == EGL_NO_SURFACE) {
|
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";
|
<< gEgl.getError() << std::dec << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,8 +224,6 @@ private:
|
|||||||
gEgl.destroySurface(gDisplay, surface);
|
gEgl.destroySurface(gDisplay, surface);
|
||||||
surface = EGL_NO_SURFACE;
|
surface = EGL_NO_SURFACE;
|
||||||
}
|
}
|
||||||
DestroyX11Window(x11Window);
|
|
||||||
x11Window = 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -50,15 +50,8 @@ execute_process(
|
|||||||
message(STATUS "${replay_stdout}")
|
message(STATUS "${replay_stdout}")
|
||||||
message(STATUS "${replay_stderr}")
|
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(retrace_log "${OPENRA_OUTPUT_DIR}/output/retrace.log")
|
||||||
|
set(mobilegl_log "${OPENRA_OUTPUT_DIR}/output/mobilegl.log")
|
||||||
if(EXISTS "${retrace_log}")
|
if(EXISTS "${retrace_log}")
|
||||||
file(STRINGS "${retrace_log}" gl_identity_lines REGEX "MOBILEGL_TRACE_GL_")
|
file(STRINGS "${retrace_log}" gl_identity_lines REGEX "MOBILEGL_TRACE_GL_")
|
||||||
foreach(line IN LISTS gl_identity_lines)
|
foreach(line IN LISTS gl_identity_lines)
|
||||||
@@ -66,6 +59,7 @@ if(EXISTS "${retrace_log}")
|
|||||||
endforeach()
|
endforeach()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(result_json "${OPENRA_OUTPUT_DIR}/output/result.json")
|
||||||
if(DEFINED OPENRA_ARTIFACT_DIR AND NOT "${OPENRA_ARTIFACT_DIR}" STREQUAL "")
|
if(DEFINED OPENRA_ARTIFACT_DIR AND NOT "${OPENRA_ARTIFACT_DIR}" STREQUAL "")
|
||||||
file(MAKE_DIRECTORY "${OPENRA_ARTIFACT_DIR}")
|
file(MAKE_DIRECTORY "${OPENRA_ARTIFACT_DIR}")
|
||||||
set(actual_png "${OPENRA_OUTPUT_DIR}/output/actual.png")
|
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}")
|
if(EXISTS "${retrace_log}")
|
||||||
file(COPY_FILE "${retrace_log}" "${OPENRA_ARTIFACT_DIR}/openra-${OPENRA_BACKEND}-retrace.log")
|
file(COPY_FILE "${retrace_log}" "${OPENRA_ARTIFACT_DIR}/openra-${OPENRA_BACKEND}-retrace.log")
|
||||||
endif()
|
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()
|
endif()
|
||||||
|
|
||||||
if(NOT replay_result EQUAL 0)
|
if(NOT replay_result EQUAL 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user