mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 05:38:31 +09:00
[Fix] (trace-replay): run Vulkan retrace headlessly
This commit is contained in:
@@ -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<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() {
|
||||
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<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) {
|
||||
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
|
||||
if (IsReleaseCurrentRequest(dpy, draw, read, ctx)) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -105,7 +105,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkSurfaceFormatKHR SwapchainObject::ChooseSwapchainSurfaceFormat(
|
||||
const Vector<VkSurfaceFormatKHR>& 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);
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
static VkPresentModeKHR ChooseSwapchainPresentMode(const Vector<VkPresentModeKHR>& 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; }
|
||||
|
||||
@@ -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<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
|
||||
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<Uint32>(m_config.SurfaceWidth, 1),
|
||||
std::max<Uint32>(m_config.SurfaceHeight, 1),
|
||||
};
|
||||
m_swapchainObject.Create(m_device, m_physicalDevice.handle, m_surface,
|
||||
static_cast<Uint32>(m_physicalDevice.queueFamilies.graphicsFamily),
|
||||
static_cast<Uint32>(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<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
|
||||
auto* nativeWindow = static_cast<ANativeWindow*>(m_window);
|
||||
if (!nativeWindow) throw RuntimeError("ANativeWindowType is null");
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user