diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index a356e17ed..8cb5e76be 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -54,6 +54,7 @@ add_library(libSceAgcDriver SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/BdaFeatures.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Pm4.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/VulkanDevice.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/SwapchainState.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/AspectFit.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/PresentationScaler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/Draw.cpp @@ -281,6 +282,7 @@ if(AGC_BUILD_VISUAL_TEST) add_executable(agc_driver_visual_test EXCLUDE_FROM_ALL tests/Visual.cpp Execution/src/VulkanDevice.cpp + Execution/src/SwapchainState.cpp Execution/src/AspectFit.cpp Execution/src/PresentationScaler.cpp Execution/src/GuestMemory.cpp diff --git a/core/libs/prx/libSceAgcDriver/Execution/include/SwapchainState.hpp b/core/libs/prx/libSceAgcDriver/Execution/include/SwapchainState.hpp new file mode 100644 index 000000000..4ffc3a574 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/Execution/include/SwapchainState.hpp @@ -0,0 +1,20 @@ +#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_SWAPCHAINSTATE_HPP +#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_SWAPCHAINSTATE_HPP + +#include + +namespace AgcDriver { + +class SwapchainState { +public: + bool NeedsRecreation() const; + void Recreated(); + bool ProcessResult(VkResult result, const char* operation); + +private: + bool outdated = false; +}; + +} + +#endif diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/SwapchainState.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/SwapchainState.cpp new file mode 100644 index 000000000..df9042651 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/Execution/src/SwapchainState.cpp @@ -0,0 +1,28 @@ +#include "prx/libSceAgcDriver/Execution/include/SwapchainState.hpp" +#include +#include + +namespace AgcDriver { + +bool SwapchainState::NeedsRecreation() const { + return outdated; +} + +void SwapchainState::Recreated() { + outdated = false; +} + +bool SwapchainState::ProcessResult(VkResult result, const char* operation) { + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + outdated = true; + return false; + } + if (result == VK_SUBOPTIMAL_KHR) { + outdated = true; + return true; + } + if (result != VK_SUCCESS) throw std::runtime_error(std::string(operation) + ": Vulkan result " + std::to_string(result)); + return true; +} + +} diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index cf874be94..0895917a6 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -7,6 +7,7 @@ #include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp" #include "prx/libSceAgcDriver/Execution/include/BdaFeatures.hpp" #include "prx/libSceAgcDriver/Execution/include/PresentationScaler.hpp" +#include "prx/libSceAgcDriver/Execution/include/SwapchainState.hpp" #include "prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp" #include "prx/libSceAgcDriver/Graphics/include/GpuColorTransfer.hpp" #include "prx/libSceAgcDriver/Graphics/include/BufferPool.hpp" @@ -52,6 +53,7 @@ struct VulkanDevice::State { void* window = nullptr; VkSurfaceKHR surface = VK_NULL_HANDLE; VkSwapchainKHR swapchain = VK_NULL_HANDLE; + SwapchainState swapchainState; VkExtent2D extent{}; std::vector images; VkFence acquireFence = VK_NULL_HANDLE; @@ -520,11 +522,18 @@ void VulkanDevice::Resize(std::uint32_t width, std::uint32_t height) { state->extent = {0, 0}; return; } - if (state->extent.width == width && state->extent.height == height) return; - WaitIdle(); VkSurfaceCapabilitiesKHR surface{}; check(state->InstanceFunction("vkGetPhysicalDeviceSurfaceCapabilitiesKHR")(state->physical, state->surface, &surface), "vkGetPhysicalDeviceSurfaceCapabilitiesKHR resize"); - require(surface.currentExtent.width == std::numeric_limits::max() || (surface.currentExtent.width == width && surface.currentExtent.height == height), "resized surface extent differs from output"); + if (surface.currentExtent.width != std::numeric_limits::max()) { + width = surface.currentExtent.width; + height = surface.currentExtent.height; + } + if (width == 0 || height == 0) { + state->extent = {0, 0}; + return; + } + if (!state->swapchainState.NeedsRecreation() && state->extent.width == width && state->extent.height == height) return; + WaitIdle(); require(width >= surface.minImageExtent.width && width <= surface.maxImageExtent.width && height >= surface.minImageExtent.height && height <= surface.maxImageExtent.height, "unsupported resized output extent"); require((surface.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0 && (surface.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) != 0 && (surface.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0, "resized surface capabilities are unsupported"); VkSwapchainCreateInfoKHR create{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR}; @@ -553,6 +562,7 @@ void VulkanDevice::Resize(std::uint32_t width, std::uint32_t height) { check(getImages(state->device, replacement, &count, state->images.data()), "vkGetSwapchainImagesKHR resize"); state->images.resize(count); state->rendered.assign(count, VK_NULL_HANDLE); + state->swapchainState.Recreated(); } bool VulkanDevice::Presentable() const { @@ -599,7 +609,7 @@ void VulkanDevice::present(std::uint32_t width, std::uint32_t height, bool opaqu check(reset(state->device, static_cast(fences.size()), fences.data()), "vkResetFences"); std::uint32_t index = 0; timing.Mark("fence_reset"); - check(state->DeviceFunction("vkAcquireNextImageKHR")(state->device, state->swapchain, 5'000'000'000ULL, VK_NULL_HANDLE, state->acquireFence, &index), "vkAcquireNextImageKHR"); + if (!state->swapchainState.ProcessResult(state->DeviceFunction("vkAcquireNextImageKHR")(state->device, state->swapchain, 5'000'000'000ULL, VK_NULL_HANDLE, state->acquireFence, &index), "vkAcquireNextImageKHR")) return; timing.Mark("acquire_image"); check(wait(state->device, 1, &state->acquireFence, VK_TRUE, std::numeric_limits::max()), "vkWaitForFences acquire"); timing.Mark("acquire_fence_wait"); @@ -678,7 +688,7 @@ void VulkanDevice::present(std::uint32_t width, std::uint32_t height, bool opaqu present.swapchainCount = 1; present.pSwapchains = &state->swapchain; present.pImageIndices = &index; - check(state->DeviceFunction("vkQueuePresentKHR")(state->queue, &present), "vkQueuePresentKHR"); + state->swapchainState.ProcessResult(state->DeviceFunction("vkQueuePresentKHR")(state->queue, &present), "vkQueuePresentKHR"); timing.Mark("queue_present"); } diff --git a/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp b/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp index d4efe8873..05cc10db2 100644 --- a/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp +++ b/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp @@ -349,6 +349,11 @@ void VideoOutDriver::processFlip(FlipRequest& req) { if (!SDL_Vulkan_CreateSurface(static_cast(context), instance, &surface)) throw std::runtime_error(std::string("SDL_Vulkan_CreateSurface failed: ") + SDL_GetError()); return surface; }, [](void* context, std::uint32_t* width, std::uint32_t* height) { + if ((SDL_GetWindowFlags(static_cast(context)) & SDL_WINDOW_MINIMIZED) != 0) { + *width = 0; + *height = 0; + return; + } int drawableWidth = 0; int drawableHeight = 0; SDL_Vulkan_GetDrawableSize(static_cast(context), &drawableWidth, &drawableHeight); @@ -401,6 +406,12 @@ void VideoOutDriver::presentLoop(std::stop_token token) { PadInput padInput; try { while (!token.stop_requested()) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + require(event.type != SDL_QUIT, "window was closed"); + padInput.HandleEvent(event); + } + padInput.Update(); { std::unique_lock lock(flipQueue->mutex); flipQueue->changed.wait_for(lock, std::chrono::milliseconds(10), [&] { return token.stop_requested() || flipQueue->failure || !flipQueue->requests.empty(); }); @@ -427,12 +438,6 @@ void VideoOutDriver::presentLoop(std::stop_token token) { current->timing->Print(current->outputHandle, current->index, current->flipArg, finished, interval); } current.reset(); - SDL_Event event; - while (SDL_PollEvent(&event)) { - require(event.type != SDL_QUIT, "window was closed"); - padInput.HandleEvent(event); - } - padInput.Update(); } std::list> cancelled; {