Vulkan: implement Android surface & recreation

This commit is contained in:
SSimco 2026-05-09 17:01:21 +03:00
parent 03e1aab320
commit 4f64e1186f
7 changed files with 146 additions and 17 deletions

View File

@ -22,6 +22,10 @@ elseif(UNIX)
else()
add_compile_definitions(_XOPEN_SOURCE)
endif()
elseif(ANDROID)
if(ENABLE_VULKAN)
add_compile_definitions(VK_USE_PLATFORM_ANDROID_KHR)
endif()
else()
if (ENABLE_VULKAN)
add_compile_definitions(

View File

@ -15,7 +15,11 @@ SwapchainInfoVk::SwapchainInfoVk(bool mainWindow, Vector2i size) : mainWindow(ma
m_logicalDevice = renderer->GetLogicalDevice();
m_physicalDevice = renderer->GetPhysicalDevice();
#if BOOST_PLAT_ANDROID
m_surface = renderer->CreateFramebufferSurface(m_instance, windowHandleInfo, &m_currentWindow);
#else
m_surface = renderer->CreateFramebufferSurface(m_instance, windowHandleInfo);
#endif
}
@ -26,8 +30,31 @@ SwapchainInfoVk::~SwapchainInfoVk()
vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
}
#if BOOST_PLAT_ANDROID
void SwapchainInfoVk::RecreateSurface()
{
if (!mainWindow)
return;
auto& windowHandleInfo = WindowSystem::GetWindowInfo().canvas_main;
windowHandleInfo.surface.wait(m_currentWindow);
auto newSurface = VulkanRenderer::CreateFramebufferSurface(m_instance, windowHandleInfo, &m_currentWindow);
vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
m_surface = newSurface;
surfaceWasLost = false;
}
#endif
void SwapchainInfoVk::Create()
{
#if BOOST_PLAT_ANDROID
if (surfaceWasLost)
RecreateSurface();
#endif
const auto details = QuerySwapchainSupport(m_surface, m_physicalDevice);
m_surfaceFormat = ChooseSurfaceFormat(details.formats);
m_actualExtent = ChooseSwapExtent(details.capabilities);
@ -231,6 +258,15 @@ bool SwapchainInfoVk::AcquireImage()
VkResult result = vkAcquireNextImageKHR(m_logicalDevice, m_swapchain, 1'000'000'000, acquireSemaphore, m_imageAvailableFence, &swapchainImageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
m_shouldRecreate = true;
#if BOOST_PLAT_ANDROID
if (result == VK_ERROR_SURFACE_LOST_KHR)
{
surfaceWasLost = true;
m_shouldRecreate = true;
}
#endif
if (result == VK_TIMEOUT)
{
swapchainImageIndex = -1;
@ -240,10 +276,17 @@ bool SwapchainInfoVk::AcquireImage()
if (result < 0)
{
swapchainImageIndex = -1;
if (result != VK_ERROR_OUT_OF_DATE_KHR)
throw std::runtime_error(fmt::format("Failed to acquire next image: {}", result));
return false;
if (result == VK_ERROR_OUT_OF_DATE_KHR)
return false;
#if BOOST_PLAT_ANDROID
if (result == VK_ERROR_SURFACE_LOST_KHR)
return false;
#endif
throw std::runtime_error(fmt::format("Failed to acquire next image: {}", result));
}
m_currentSemaphore = acquireSemaphore;
m_awaitableFence = m_imageAvailableFence;
m_acquireIndex = (m_acquireIndex + 1) % m_swapchainImages.size();
@ -319,7 +362,7 @@ VkSurfaceFormatKHR SwapchainInfoVk::ChooseSurfaceFormat(const std::vector<VkSurf
for (const auto& format : formats)
{
if (format.format == VK_FORMAT_B8G8R8A8_UNORM && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
if ((format.format == VK_FORMAT_B8G8R8A8_UNORM || format.format == VK_FORMAT_R8G8B8A8_UNORM) && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
return format;
}
@ -391,8 +434,11 @@ VkSwapchainCreateInfoKHR SwapchainInfoVk::CreateSwapchainCreateInfo(VkSurfaceKHR
}
else
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
#if BOOST_PLAT_ANDROID
createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
#else
createInfo.preTransform = swapchainSupport.capabilities.currentTransform;
#endif
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = ChoosePresentMode(swapchainSupport.presentModes);
createInfo.clipped = VK_TRUE;

View File

@ -3,6 +3,10 @@
#include "util/math/vector2.h"
#include <vulkan/vulkan_core.h>
#if BOOST_PLAT_ANDROID
#include <android/native_window.h>
#endif
struct SwapchainInfoVk
{
enum class VSync
@ -58,10 +62,13 @@ struct SwapchainInfoVk
bool mainWindow{};
#if BOOST_PLAT_ANDROID
bool surfaceWasLost = false;
#endif
bool m_shouldRecreate = false;
VSync m_vsyncState = VSync::Immediate;
bool hasDefinedSwapchainImage{}; // indicates if the swapchain image is in a defined state
VkInstance m_instance{};
VkPhysicalDevice m_physicalDevice{};
VkDevice m_logicalDevice{};
@ -91,5 +98,10 @@ private:
VkFence m_awaitableFence = VK_NULL_HANDLE;
VkSemaphore m_currentSemaphore = VK_NULL_HANDLE;
#if BOOST_PLAT_ANDROID
void RecreateSurface();
ANativeWindow* m_currentWindow = nullptr;
#endif
std::array<uint32, 2> m_swapchainQueueFamilyIndices;
};

View File

@ -130,12 +130,16 @@ VKFUNC_DEVICE(vkDestroyPipeline);
VKFUNC_DEVICE(vkCmdBindPipeline);
// swapchain
#if BOOST_OS_LINUX || BOOST_OS_BSD
#if BOOST_PLAT_ANDROID
VKFUNC_INSTANCE(vkCreateAndroidSurfaceKHR);
#elif BOOST_OS_LINUX || BOOST_OS_BSD
VKFUNC_INSTANCE(vkCreateXlibSurfaceKHR);
VKFUNC_INSTANCE(vkCreateXcbSurfaceKHR);
#ifdef HAS_WAYLAND
VKFUNC_INSTANCE(vkCreateWaylandSurfaceKHR);
#endif
#endif
#if BOOST_OS_WINDOWS

View File

@ -113,6 +113,8 @@ std::vector<VulkanRenderer::DeviceInfo> VulkanRenderer::GetDevices()
requiredExtensions.emplace_back(VK_KHR_SURFACE_EXTENSION_NAME);
#if BOOST_OS_WINDOWS
requiredExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#elif BOOST_PLAT_ANDROID
requiredExtensions.emplace_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
#elif BOOST_OS_LINUX || BOOST_OS_BSD
auto backend = WindowSystem::GetWindowInfo().window_main.backend;
if(backend == WindowSystem::WindowHandleInfo::Backend::X11)
@ -1481,6 +1483,8 @@ std::vector<const char*> VulkanRenderer::CheckInstanceExtensionSupport(FeatureCo
requiredInstanceExtensions.emplace_back(VK_KHR_SURFACE_EXTENSION_NAME);
#if BOOST_OS_WINDOWS
requiredInstanceExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#elif BOOST_PLAT_ANDROID
requiredInstanceExtensions.emplace_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
#elif BOOST_OS_LINUX || BOOST_OS_BSD
auto backend = WindowSystem::GetWindowInfo().window_main.backend;
if(backend == WindowSystem::WindowHandleInfo::Backend::X11)
@ -1568,7 +1572,25 @@ VkSurfaceKHR VulkanRenderer::CreateWinSurface(VkInstance instance, HWND hwindow)
}
#endif
#if BOOST_OS_LINUX || BOOST_OS_BSD
#if BOOST_PLAT_ANDROID
VkSurfaceKHR VulkanRenderer::CreateAndroidSurface(VkInstance instance, ANativeWindow* window)
{
VkAndroidSurfaceCreateInfoKHR sci{};
sci.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
sci.flags = 0;
sci.window = window;
VkSurfaceKHR result;
VkResult err;
if ((err = vkCreateAndroidSurfaceKHR(instance, &sci, nullptr, &result)) != VK_SUCCESS)
{
cemuLog_log(LogType::Force, "Cannot create an Android Vulkan surface: {}", (sint32)err);
throw std::runtime_error(fmt::format("Cannot create an Android Vulkan surface: {}", err));
}
return result;
}
#elif BOOST_OS_LINUX || BOOST_OS_BSD
VkSurfaceKHR VulkanRenderer::CreateXlibSurface(VkInstance instance, Display* dpy, Window window)
{
VkXlibSurfaceCreateInfoKHR sci{};
@ -1625,9 +1647,23 @@ VkSurfaceKHR VulkanRenderer::CreateWaylandSurface(VkInstance instance, wl_displa
return result;
}
#endif // HAS_WAYLAND
#endif // BOOST_OS_LINUX
#endif
#endif
#if BOOST_PLAT_ANDROID
VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, struct WindowSystem::WindowHandleInfo& windowInfo, ANativeWindow** nativeWindow)
{
auto window = static_cast<ANativeWindow*>(windowInfo.surface.load());
VkSurfaceKHR surface = CreateAndroidSurface(instance, window);
if (nativeWindow != nullptr)
{
*nativeWindow = window;
}
return surface;
}
#else
VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, WindowSystem::WindowHandleInfo& windowInfo)
{
#if BOOST_OS_WINDOWS
@ -1644,6 +1680,7 @@ VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, Windo
return CreateCocoaSurface(instance, windowInfo.surface.load());
#endif
}
#endif
void VulkanRenderer::CreateCommandPool()
{
@ -3029,6 +3066,11 @@ bool VulkanRenderer::UpdateSwapchainProperties(bool mainWindow)
if (width != extent.width || height != extent.height)
stateChanged = true;
#if BOOST_PLAT_ANDROID
if (chainInfo.surfaceWasLost)
stateChanged = true;
#endif
if(stateChanged)
{
try
@ -3104,19 +3146,34 @@ void VulkanRenderer::SwapBuffer(bool mainWindow)
}
VkResult result = vkQueuePresentKHR(m_presentQueue, &presentInfo);
if (result < 0 && result != VK_ERROR_OUT_OF_DATE_KHR)
if (result < 0 && result != VK_ERROR_OUT_OF_DATE_KHR
#if BOOST_PLAT_ANDROID
&& result != VK_ERROR_SURFACE_LOST_KHR
#endif
)
{
throw std::runtime_error(fmt::format("Failed to present image: {}", result));
}
if(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR)
if (result == VK_ERROR_OUT_OF_DATE_KHR)
chainInfo.m_shouldRecreate = true;
if(result >= 0)
if (result >= 0)
{
chainInfo.m_queueDepth++;
chainInfo.m_presentId++;
}
#if BOOST_PLAT_ANDROID
if (result == VK_ERROR_SURFACE_LOST_KHR)
chainInfo.surfaceWasLost = true;
#endif
#if !BOOST_PLAT_ANDROID
if (result == VK_SUBOPTIMAL_KHR)
chainInfo.m_shouldRecreate = true;
#endif
chainInfo.hasDefinedSwapchainImage = false;
chainInfo.swapchainImageIndex = -1;

View File

@ -216,16 +216,21 @@ public:
#if BOOST_OS_WINDOWS
static VkSurfaceKHR CreateWinSurface(VkInstance instance, HWND hwindow);
#endif
#if BOOST_OS_LINUX || BOOST_OS_BSD
#if BOOST_PLAT_ANDROID
static VkSurfaceKHR CreateAndroidSurface(VkInstance instance, ANativeWindow* window);
#elif BOOST_OS_LINUX || BOOST_OS_BSD
static VkSurfaceKHR CreateXlibSurface(VkInstance instance, Display* dpy, Window window);
static VkSurfaceKHR CreateXcbSurface(VkInstance instance, xcb_connection_t* connection, xcb_window_t window);
#ifdef HAS_WAYLAND
#ifdef HAS_WAYLAND
static VkSurfaceKHR CreateWaylandSurface(VkInstance instance, wl_display* display, wl_surface* surface);
#endif
#endif // HAS_WAYLAND
#endif
#if BOOST_PLAT_ANDROID
static VkSurfaceKHR CreateFramebufferSurface(VkInstance instance, struct WindowSystem::WindowHandleInfo& windowInfo, ANativeWindow** nativeWindow = nullptr);
#else
static VkSurfaceKHR CreateFramebufferSurface(VkInstance instance, struct WindowSystem::WindowHandleInfo& windowInfo);
#endif
void AppendOverlayDebugInfo() override;
void ImguiInit();

View File

@ -12,6 +12,7 @@ namespace WindowSystem
Wayland,
Cocoa,
Windows,
Android,
} backend;
std::atomic<void*> display = nullptr;
std::atomic<void*> surface = nullptr;