From 91ef202ee73100ae1c3f7e0c674eef3888388c39 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Tue, 10 Oct 2017 00:03:08 +0300 Subject: [PATCH] vulkan: Better VkResult handling --- rpcs3/Emu/RSX/VK/VKGSRender.cpp | 138 +++++++++++++++++--------------- rpcs3/Emu/RSX/VK/VKGSRender.h | 1 + rpcs3/Emu/RSX/VK/VKHelpers.cpp | 98 +++++++++++++++++++++++ rpcs3/Emu/RSX/VK/VKHelpers.h | 4 +- 4 files changed, 176 insertions(+), 65 deletions(-) diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index b65aba730a..11c042852f 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -2271,6 +2271,72 @@ void VKGSRender::prepare_rtts() } } +void VKGSRender::reinitialize_swapchain() +{ + /** + * Waiting for the commands to process does not work reliably as the fence can be signaled before swap images are released + * and there are no explicit methods to ensure that the presentation engine is not using the images at all. + */ + + //NOTE: This operation will create a hard sync point + close_and_submit_command_buffer({}, m_current_command_buffer->submit_fence); + m_current_command_buffer->pending = true; + m_current_command_buffer->reset(); + + //Will have to block until rendering is completed + VkFence resize_fence = VK_NULL_HANDLE; + VkFenceCreateInfo infos = {}; + infos.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + + vkCreateFence((*m_device), &infos, nullptr, &resize_fence); + + for (auto &ctx : frame_context_storage) + { + if (ctx.present_image == UINT32_MAX) + continue; + + //Release present image by presenting it + ctx.swap_command_buffer->wait(); + ctx.swap_command_buffer = nullptr; + present(&ctx); + } + + vkQueueWaitIdle(m_swap_chain->get_present_queue()); + vkDeviceWaitIdle(*m_device); + + //Remove any old refs to the old images as they are about to be destroyed + m_framebuffers_to_clean.clear(); + + //Rebuild swapchain. Old swapchain destruction is handled by the init_swapchain call + m_client_width = m_frame->client_width(); + m_client_height = m_frame->client_height(); + m_swap_chain->init_swapchain(m_client_width, m_client_height); + + //Prepare new swapchain images for use + open_command_buffer(); + + for (u32 i = 0; i < m_swap_chain->get_swap_image_count(); ++i) + { + vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, + vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT)); + + VkClearColorValue clear_color{}; + auto range = vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT); + vkCmdClearColorImage(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), VK_IMAGE_LAYOUT_GENERAL, &clear_color, 1, &range); + vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), + VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT)); + } + + //Flush the command buffer + close_and_submit_command_buffer({}, resize_fence); + CHECK_RESULT(vkWaitForFences((*m_device), 1, &resize_fence, VK_TRUE, UINT64_MAX)); + vkDestroyFence((*m_device), resize_fence, nullptr); + + m_current_command_buffer->reset(); + open_command_buffer(); +} void VKGSRender::flip(int buffer) { @@ -2399,8 +2465,12 @@ void VKGSRender::flip(int buffer) continue; } + case VK_ERROR_OUT_OF_DATE_KHR: + LOG_ERROR(RSX, "vkAcquireNextImageKHR failed with VK_ERROR_OUT_OF_DATE_KHR. Flip request ignored until surface is recreated."); + reinitialize_swapchain(); + return; default: - fmt::throw_exception("vkAcquireNextImageKHR failed with status 0x%X" HERE, (u32)status); + vk::die_with_error(HERE, status); } } @@ -2489,69 +2559,9 @@ void VKGSRender::flip(int buffer) } else { - /** - * Waiting for the commands to process does not work reliably as the fence can be signaled before swap images are released - * and there are no explicit methods to ensure that the presentation engine is not using the images at all. - */ - - //NOTE: This operation will create a hard sync point - close_and_submit_command_buffer({}, m_current_command_buffer->submit_fence); - m_current_command_buffer->pending = true; - m_current_command_buffer->reset(); - - //Will have to block until rendering is completed - VkFence resize_fence = VK_NULL_HANDLE; - VkFenceCreateInfo infos = {}; - infos.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - - vkCreateFence((*m_device), &infos, nullptr, &resize_fence); - - for (auto &ctx : frame_context_storage) - { - if (ctx.present_image == UINT32_MAX) - continue; - - //Release present image by presenting it - ctx.swap_command_buffer->wait(); - ctx.swap_command_buffer = nullptr; - present(&ctx); - } - - vkQueueWaitIdle(m_swap_chain->get_present_queue()); - vkDeviceWaitIdle(*m_device); - - //Remove any old refs to the old images as they are about to be destroyed - m_framebuffers_to_clean.clear(); - - //Rebuild swapchain. Old swapchain destruction is handled by the init_swapchain call - m_client_width = m_frame->client_width(); - m_client_height = m_frame->client_height(); - m_swap_chain->init_swapchain(m_client_width, m_client_height); - - //Prepare new swapchain images for use - open_command_buffer(); - - for (u32 i = 0; i < m_swap_chain->get_swap_image_count(); ++i) - { - vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, - vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT)); - - VkClearColorValue clear_color{}; - auto range = vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT); - vkCmdClearColorImage(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), VK_IMAGE_LAYOUT_GENERAL, &clear_color, 1, &range); - vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), - VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT)); - } - - //Flush the command buffer - close_and_submit_command_buffer({}, resize_fence); - CHECK_RESULT(vkWaitForFences((*m_device), 1, &resize_fence, VK_TRUE, UINT64_MAX)); - vkDestroyFence((*m_device), resize_fence, nullptr); - - m_current_command_buffer->reset(); - open_command_buffer(); + //Recreate the swapchain to resize it + //NOTE: Nvidia driver does not invalidate the swapchain immediately on window size changes. The event will fire after a while and an OUT_OF_DATE error will be returned in subsequent rendering commands + reinitialize_swapchain(); } std::chrono::time_point flip_end = steady_clock::now(); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index 358356fe88..a8b5e62924 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -289,6 +289,7 @@ private: void process_swap_request(frame_context_t *ctx, bool free_resources = false); void advance_queued_frames(); void present(frame_context_t *ctx); + void reinitialize_swapchain(); void begin_render_pass(); void close_render_pass(); diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.cpp b/rpcs3/Emu/RSX/VK/VKHelpers.cpp index fc68686ada..c8b30268a3 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.cpp +++ b/rpcs3/Emu/RSX/VK/VKHelpers.cpp @@ -373,6 +373,104 @@ namespace vk return (g_num_processed_frames > 0)? g_num_processed_frames - 1: 0; } + void die_with_error(std::string faulting_addr, VkResult error_code) + { + std::string error_message; + int severity = 0; //0 - die, 1 - warn, 2 - nothing + + switch (error_code) + { + case VK_SUCCESS: + case VK_EVENT_SET: + case VK_EVENT_RESET: + case VK_INCOMPLETE: + return; + case VK_SUBOPTIMAL_KHR: + error_message = "Present surface is suboptimal (VK_SUBOPTIMAL_KHR)"; + severity = 1; + break; + case VK_NOT_READY: + error_message = "Device or resource busy (VK_NOT_READY)"; + break; + case VK_TIMEOUT: + error_message = "Timeout event (VK_TIMEOUT)"; + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + error_message = "Out of host memory (system RAM) (VK_ERROR_OUT_OF_HOST_MEMORY)"; + break; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + error_message = "Out of video memory (VRAM) (VK_ERROR_OUT_OF_DEVICE_MEMORY)"; + break; + case VK_ERROR_INITIALIZATION_FAILED: + error_message = "Initialization failed (VK_ERROR_INITIALIZATION_FAILED)"; + break; + case VK_ERROR_DEVICE_LOST: + error_message = "Device lost (Driver crashed with unspecified error or stopped responding and recovered) (VK_ERROR_DEVICE_LOST)"; + break; + case VK_ERROR_MEMORY_MAP_FAILED: + error_message = "Memory map failed (VK_ERROR_MEMORY_MAP_FAILED)"; + break; + case VK_ERROR_LAYER_NOT_PRESENT: + error_message = "Requested layer is not available (Try disabling debug output or install vulkan SDK) (VK_ERROR_LAYER_NOT_PRESENT)"; + break; + case VK_ERROR_EXTENSION_NOT_PRESENT: + error_message = "Requested extension not available (VK_ERROR_EXTENSION_NOT_PRESENT)"; + break; + case VK_ERROR_FEATURE_NOT_PRESENT: + error_message = "Requested feature not available (VK_ERROR_FEATURE_NOT_PRESENT)"; + break; + case VK_ERROR_INCOMPATIBLE_DRIVER: + error_message = "Incompatible driver (VK_ERROR_INCOMPATIBLE_DRIVER)"; + break; + case VK_ERROR_TOO_MANY_OBJECTS: + error_message = "Too many objects created (Out of handles) (VK_ERROR_TOO_MANY_OBJECTS)"; + break; + case VK_ERROR_FORMAT_NOT_SUPPORTED: + error_message = "Format not supported (VK_ERROR_FORMAT_NOT_SUPPORTED)"; + break; + case VK_ERROR_FRAGMENTED_POOL: + error_message = "Fragmented pool (VK_ERROR_FRAGMENTED_POOL)"; + break; + case VK_ERROR_SURFACE_LOST_KHR: + error_message = "Surface lost (VK_ERROR_SURFACE_LOST)"; + break; + case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: + error_message = "Native window in use (VK_ERROR_NATIVE_WINDOW_IN_USE_KHR)"; + break; + case VK_ERROR_OUT_OF_DATE_KHR: + error_message = "Present surface is out of date (VK_ERROR_OUT_OF_DATE_KHR)"; + severity = 1; + break; + case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: + error_message = "Incompatible display (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR)"; + break; + case VK_ERROR_VALIDATION_FAILED_EXT: + error_message = "Validation failed (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR)"; + break; + case VK_ERROR_INVALID_SHADER_NV: + error_message = "Invalid shader code (VK_ERROR_INVALID_SHADER_NV)"; + break; + case VK_ERROR_OUT_OF_POOL_MEMORY_KHR: + error_message = "Out of pool memory (VK_ERROR_OUT_OF_POOL_MEMORY_KHR)"; + break; + case VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX: + error_message = "Invalid external handle (VK_ERROR_INVALID_EXTERNAL_HANDLE_KHX)"; + break; + default: + error_message = fmt::format("Unknown Code (%Xh, %d)", (s32)error_code, (s32&)error_code); + break; + } + + switch (severity) + { + case 0: + fmt::throw_exception("Assertion Failed! Vulkan API call failed with unrecoverable error: %s", (error_message + faulting_addr).c_str()); + case 1: + LOG_ERROR(RSX, "Vulkan API call has failed with an error but will continue: %s", (error_message + faulting_addr).c_str()); + break; + } + } + VKAPI_ATTR VkBool32 VKAPI_CALL dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, const char *pMsg, void *pUserData) diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.h b/rpcs3/Emu/RSX/VK/VKHelpers.h index 7eaefec51e..6373c8cf5c 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.h +++ b/rpcs3/Emu/RSX/VK/VKHelpers.h @@ -36,7 +36,7 @@ namespace rsx namespace vk { -#define CHECK_RESULT(expr) { VkResult _res = (expr); if (_res != VK_SUCCESS) fmt::throw_exception("Assertion failed! Result is %Xh" HERE, (s32)_res); } +#define CHECK_RESULT(expr) { VkResult _res = (expr); if (_res != VK_SUCCESS) vk::die_with_error(HERE, _res); } VKAPI_ATTR void *VKAPI_CALL mem_realloc(void *pUserData, void *pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope); VKAPI_ATTR void *VKAPI_CALL mem_alloc(void *pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope); @@ -94,6 +94,8 @@ namespace vk const u64 get_current_frame_id(); const u64 get_last_completed_frame_id(); + void die_with_error(std::string faulting_addr, VkResult error_code); + struct memory_type_mapping { uint32_t host_visible_coherent;