From 1286c309e3a7b9a7a28b6128e68ab06f1502864c Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 1 Oct 2016 10:40:44 +1000 Subject: [PATCH 1/7] Vulkan: Fix compilation on 32-bit targets --- .../Core/VideoBackends/Vulkan/BoundingBox.h | 2 +- .../Vulkan/CommandBufferManager.cpp | 52 +++++++- .../Vulkan/CommandBufferManager.h | 14 +-- .../Core/VideoBackends/Vulkan/RasterFont.cpp | 8 +- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 4 +- Source/Core/VideoBackends/Vulkan/Renderer.h | 4 +- .../VideoBackends/Vulkan/StagingBuffer.cpp | 4 +- .../VideoBackends/Vulkan/StagingTexture2D.cpp | 8 +- .../VideoBackends/Vulkan/StateTracker.cpp | 2 +- .../VideoBackends/Vulkan/StreamBuffer.cpp | 8 +- .../Core/VideoBackends/Vulkan/SwapChain.cpp | 4 +- Source/Core/VideoBackends/Vulkan/SwapChain.h | 6 +- .../Core/VideoBackends/Vulkan/Texture2D.cpp | 10 +- .../VideoBackends/Vulkan/TextureCache.cpp | 2 +- Source/Core/VideoBackends/Vulkan/Util.cpp | 114 ------------------ Source/Core/VideoBackends/Vulkan/Util.h | 22 ---- .../VideoBackends/Vulkan/VulkanContext.cpp | 3 +- 17 files changed, 88 insertions(+), 179 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/BoundingBox.h b/Source/Core/VideoBackends/Vulkan/BoundingBox.h index 3bb8606d05..0647c08607 100644 --- a/Source/Core/VideoBackends/Vulkan/BoundingBox.h +++ b/Source/Core/VideoBackends/Vulkan/BoundingBox.h @@ -40,7 +40,7 @@ private: void Readback(StateTracker* state_tracker); VkBuffer m_gpu_buffer = VK_NULL_HANDLE; - VkDeviceMemory m_gpu_memory = nullptr; + VkDeviceMemory m_gpu_memory = VK_NULL_HANDLE; static const size_t NUM_VALUES = 4; static const size_t BUFFER_SIZE = sizeof(u32) * NUM_VALUES; diff --git a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp index 537c21c514..202c3f6777 100644 --- a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.cpp @@ -73,7 +73,7 @@ void CommandBufferManager::DestroyCommandPool() if (m_command_pool) { vkDestroyCommandPool(g_vulkan_context->GetDevice(), m_command_pool, nullptr); - m_command_pool = nullptr; + m_command_pool = VK_NULL_HANDLE; } } @@ -141,8 +141,8 @@ void CommandBufferManager::DestroyCommandBuffers() for (FrameResources& resources : m_frame_resources) { - for (const auto& it : resources.cleanup_resources) - it.destroy_callback(device, it.object); + for (auto& it : resources.cleanup_resources) + it(); resources.cleanup_resources.clear(); if (resources.fence != VK_NULL_HANDLE) @@ -385,8 +385,8 @@ void CommandBufferManager::OnCommandBufferExecuted(size_t index) iter.second.second(resources.fence); // Clean up all objects pending destruction on this command buffer - for (const auto& it : resources.cleanup_resources) - it.destroy_callback(g_vulkan_context->GetDevice(), it.object); + for (auto& it : resources.cleanup_resources) + it(); resources.cleanup_resources.clear(); } @@ -446,6 +446,48 @@ void CommandBufferManager::ExecuteCommandBuffer(bool submit_off_thread, bool wai WaitForFence(pending_fence); } +void CommandBufferManager::DeferBufferDestruction(VkBuffer object) +{ + FrameResources& resources = m_frame_resources[m_current_frame]; + resources.cleanup_resources.push_back( + [object]() { vkDestroyBuffer(g_vulkan_context->GetDevice(), object, nullptr); }); +} + +void CommandBufferManager::DeferBufferViewDestruction(VkBufferView object) +{ + FrameResources& resources = m_frame_resources[m_current_frame]; + resources.cleanup_resources.push_back( + [object]() { vkDestroyBufferView(g_vulkan_context->GetDevice(), object, nullptr); }); +} + +void CommandBufferManager::DeferDeviceMemoryDestruction(VkDeviceMemory object) +{ + FrameResources& resources = m_frame_resources[m_current_frame]; + resources.cleanup_resources.push_back( + [object]() { vkFreeMemory(g_vulkan_context->GetDevice(), object, nullptr); }); +} + +void CommandBufferManager::DeferFramebufferDestruction(VkFramebuffer object) +{ + FrameResources& resources = m_frame_resources[m_current_frame]; + resources.cleanup_resources.push_back( + [object]() { vkDestroyFramebuffer(g_vulkan_context->GetDevice(), object, nullptr); }); +} + +void CommandBufferManager::DeferImageDestruction(VkImage object) +{ + FrameResources& resources = m_frame_resources[m_current_frame]; + resources.cleanup_resources.push_back( + [object]() { vkDestroyImage(g_vulkan_context->GetDevice(), object, nullptr); }); +} + +void CommandBufferManager::DeferImageViewDestruction(VkImageView object) +{ + FrameResources& resources = m_frame_resources[m_current_frame]; + resources.cleanup_resources.push_back( + [object]() { vkDestroyImageView(g_vulkan_context->GetDevice(), object, nullptr); }); +} + void CommandBufferManager::AddFencePointCallback( const void* key, const CommandBufferQueuedCallback& queued_callback, const CommandBufferExecutedCallback& executed_callback) diff --git a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h index 1b6d3ea665..96b09b1bde 100644 --- a/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h +++ b/Source/Core/VideoBackends/Vulkan/CommandBufferManager.h @@ -81,12 +81,12 @@ public: // Schedule a vulkan resource for destruction later on. This will occur when the command buffer // is next re-used, and the GPU has finished working with the specified resource. - template - void DeferResourceDestruction(T object) - { - DeferredResourceDestruction wrapper = DeferredResourceDestruction::Wrapper(object); - m_frame_resources[m_current_frame].cleanup_resources.push_back(wrapper); - } + void DeferBufferDestruction(VkBuffer object); + void DeferBufferViewDestruction(VkBufferView object); + void DeferDeviceMemoryDestruction(VkDeviceMemory object); + void DeferFramebufferDestruction(VkFramebuffer object); + void DeferImageDestruction(VkImage object); + void DeferImageViewDestruction(VkImageView object); // Instruct the manager to fire the specified callback when a fence is flagged to be signaled. // This happens when command buffers are executed, and can be tested if signaled, which means @@ -124,7 +124,7 @@ private: bool init_command_buffer_used; bool needs_fence_wait; - std::vector cleanup_resources; + std::vector> cleanup_resources; }; std::array m_frame_resources = {}; diff --git a/Source/Core/VideoBackends/Vulkan/RasterFont.cpp b/Source/Core/VideoBackends/Vulkan/RasterFont.cpp index 72f393bb1e..f67412e243 100644 --- a/Source/Core/VideoBackends/Vulkan/RasterFont.cpp +++ b/Source/Core/VideoBackends/Vulkan/RasterFont.cpp @@ -170,9 +170,9 @@ RasterFont::RasterFont() RasterFont::~RasterFont() { if (m_vertex_shader != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferResourceDestruction(m_vertex_shader); + vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_vertex_shader, nullptr); if (m_fragment_shader != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferResourceDestruction(m_fragment_shader); + vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_fragment_shader, nullptr); } bool RasterFont::Initialize() @@ -279,8 +279,8 @@ bool RasterFont::CreateTexture() // Free temp buffers after command buffer executes m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - g_command_buffer_mgr->DeferResourceDestruction(temp_buffer); - g_command_buffer_mgr->DeferResourceDestruction(temp_buffer_memory); + g_command_buffer_mgr->DeferBufferDestruction(temp_buffer); + g_command_buffer_mgr->DeferDeviceMemoryDestruction(temp_buffer_memory); return true; } diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index e1c6394b30..c6837ef7f8 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -164,13 +164,13 @@ void Renderer::DestroySemaphores() if (m_image_available_semaphore) { vkDestroySemaphore(g_vulkan_context->GetDevice(), m_image_available_semaphore, nullptr); - m_image_available_semaphore = nullptr; + m_image_available_semaphore = VK_NULL_HANDLE; } if (m_rendering_finished_semaphore) { vkDestroySemaphore(g_vulkan_context->GetDevice(), m_rendering_finished_semaphore, nullptr); - m_rendering_finished_semaphore = nullptr; + m_rendering_finished_semaphore = VK_NULL_HANDLE; } } diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index 3110a090e5..1b1e175272 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -105,8 +105,8 @@ private: FramebufferManager* m_framebuffer_mgr = nullptr; - VkSemaphore m_image_available_semaphore = nullptr; - VkSemaphore m_rendering_finished_semaphore = nullptr; + VkSemaphore m_image_available_semaphore = VK_NULL_HANDLE; + VkSemaphore m_rendering_finished_semaphore = VK_NULL_HANDLE; std::unique_ptr m_swap_chain; std::unique_ptr m_state_tracker; diff --git a/Source/Core/VideoBackends/Vulkan/StagingBuffer.cpp b/Source/Core/VideoBackends/Vulkan/StagingBuffer.cpp index 052897d9bc..f972492abb 100644 --- a/Source/Core/VideoBackends/Vulkan/StagingBuffer.cpp +++ b/Source/Core/VideoBackends/Vulkan/StagingBuffer.cpp @@ -26,8 +26,8 @@ StagingBuffer::~StagingBuffer() if (m_map_pointer) Unmap(); - g_command_buffer_mgr->DeferResourceDestruction(m_memory); - g_command_buffer_mgr->DeferResourceDestruction(m_buffer); + g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory); + g_command_buffer_mgr->DeferBufferDestruction(m_buffer); } bool StagingBuffer::Map(VkDeviceSize offset, VkDeviceSize size) diff --git a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp index c82d449c49..1f5be7563c 100644 --- a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp +++ b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp @@ -133,8 +133,8 @@ StagingTexture2DLinear::~StagingTexture2DLinear() if (m_map_pointer) Unmap(); - g_command_buffer_mgr->DeferResourceDestruction(m_memory); - g_command_buffer_mgr->DeferResourceDestruction(m_image); + g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory); + g_command_buffer_mgr->DeferImageDestruction(m_image); } void StagingTexture2DLinear::CopyFromImage(VkCommandBuffer command_buffer, VkImage image, @@ -362,8 +362,8 @@ StagingTexture2DBuffer::~StagingTexture2DBuffer() if (m_map_pointer) Unmap(); - g_command_buffer_mgr->DeferResourceDestruction(m_memory); - g_command_buffer_mgr->DeferResourceDestruction(m_buffer); + g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory); + g_command_buffer_mgr->DeferBufferDestruction(m_buffer); } void StagingTexture2DBuffer::CopyFromImage(VkCommandBuffer command_buffer, VkImage image, diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp index 4d6f8dea7a..80c13323b0 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp @@ -539,7 +539,7 @@ void StateTracker::EndRenderPass() return; vkCmdEndRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer()); - m_current_render_pass = nullptr; + m_current_render_pass = VK_NULL_HANDLE; } void StateTracker::BeginClearRenderPass(const VkRect2D& area, const VkClearValue clear_values[2]) diff --git a/Source/Core/VideoBackends/Vulkan/StreamBuffer.cpp b/Source/Core/VideoBackends/Vulkan/StreamBuffer.cpp index 9d7f4d8820..4d90401bcf 100644 --- a/Source/Core/VideoBackends/Vulkan/StreamBuffer.cpp +++ b/Source/Core/VideoBackends/Vulkan/StreamBuffer.cpp @@ -35,9 +35,9 @@ StreamBuffer::~StreamBuffer() vkUnmapMemory(g_vulkan_context->GetDevice(), m_memory); if (m_buffer != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferResourceDestruction(m_buffer); + g_command_buffer_mgr->DeferBufferDestruction(m_buffer); if (m_memory != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferResourceDestruction(m_memory); + g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory); } std::unique_ptr StreamBuffer::Create(VkBufferUsageFlags usage, size_t initial_size, @@ -124,9 +124,9 @@ bool StreamBuffer::ResizeBuffer(size_t size) // Destroy the backings for the buffer after the command buffer executes if (m_buffer != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferResourceDestruction(m_buffer); + g_command_buffer_mgr->DeferBufferDestruction(m_buffer); if (m_memory != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferResourceDestruction(m_memory); + g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory); // Replace with the new buffer m_buffer = buffer; diff --git a/Source/Core/VideoBackends/Vulkan/SwapChain.cpp b/Source/Core/VideoBackends/Vulkan/SwapChain.cpp index f0a35fd526..34ad5c39f2 100644 --- a/Source/Core/VideoBackends/Vulkan/SwapChain.cpp +++ b/Source/Core/VideoBackends/Vulkan/SwapChain.cpp @@ -280,8 +280,8 @@ void SwapChain::DestroyRenderPass() if (!m_render_pass) return; - g_command_buffer_mgr->DeferResourceDestruction(m_render_pass); - m_render_pass = nullptr; + vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_render_pass, nullptr); + m_render_pass = VK_NULL_HANDLE; } bool SwapChain::CreateSwapChain() diff --git a/Source/Core/VideoBackends/Vulkan/SwapChain.h b/Source/Core/VideoBackends/Vulkan/SwapChain.h index 3278139d1b..f0ccb81921 100644 --- a/Source/Core/VideoBackends/Vulkan/SwapChain.h +++ b/Source/Core/VideoBackends/Vulkan/SwapChain.h @@ -77,15 +77,15 @@ private: }; void* m_native_handle = nullptr; - VkSurfaceKHR m_surface = nullptr; + VkSurfaceKHR m_surface = VK_NULL_HANDLE; VkSurfaceFormatKHR m_surface_format = {}; VkPresentModeKHR m_present_mode = VK_PRESENT_MODE_RANGE_SIZE_KHR; - VkSwapchainKHR m_swap_chain = nullptr; + VkSwapchainKHR m_swap_chain = VK_NULL_HANDLE; std::vector m_swap_chain_images; u32 m_current_swap_chain_image_index = 0; - VkRenderPass m_render_pass = nullptr; + VkRenderPass m_render_pass = VK_NULL_HANDLE; u32 m_width = 0; u32 m_height = 0; diff --git a/Source/Core/VideoBackends/Vulkan/Texture2D.cpp b/Source/Core/VideoBackends/Vulkan/Texture2D.cpp index 470328c47c..9dda089b21 100644 --- a/Source/Core/VideoBackends/Vulkan/Texture2D.cpp +++ b/Source/Core/VideoBackends/Vulkan/Texture2D.cpp @@ -21,13 +21,13 @@ Texture2D::Texture2D(u32 width, u32 height, u32 levels, u32 layers, VkFormat for Texture2D::~Texture2D() { - g_command_buffer_mgr->DeferResourceDestruction(m_view); + g_command_buffer_mgr->DeferImageViewDestruction(m_view); // If we don't have device memory allocated, the image is not owned by us (e.g. swapchain) if (m_device_memory != VK_NULL_HANDLE) { - g_command_buffer_mgr->DeferResourceDestruction(m_image); - g_command_buffer_mgr->DeferResourceDestruction(m_device_memory); + g_command_buffer_mgr->DeferImageDestruction(m_image); + g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_device_memory); } } @@ -134,6 +134,8 @@ std::unique_ptr Texture2D::CreateFromExistingImage(u32 width, u32 hei static_cast(VK_IMAGE_ASPECT_COLOR_BIT), 0, levels, 0, layers}}; + // Memory is managed by the owner of the image. + VkDeviceMemory memory = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE; VkResult res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view); if (res != VK_SUCCESS) @@ -143,7 +145,7 @@ std::unique_ptr Texture2D::CreateFromExistingImage(u32 width, u32 hei } return std::make_unique(width, height, levels, layers, format, samples, view_type, - existing_image, nullptr, view); + existing_image, memory, view); } void Texture2D::OverrideImageLayout(VkImageLayout new_layout) diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp index a866202545..e3b989c34e 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp @@ -316,7 +316,7 @@ TextureCache::TCacheEntry::~TCacheEntry() m_parent->m_state_tracker->UnbindTexture(m_texture->GetView()); if (m_framebuffer != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferResourceDestruction(m_framebuffer); + g_command_buffer_mgr->DeferFramebufferDestruction(m_framebuffer); } void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, diff --git a/Source/Core/VideoBackends/Vulkan/Util.cpp b/Source/Core/VideoBackends/Vulkan/Util.cpp index 63bc9b94c5..9ef432d779 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.cpp +++ b/Source/Core/VideoBackends/Vulkan/Util.cpp @@ -260,120 +260,6 @@ VkShaderModule CompileAndCreateFragmentShader(const std::string& source_code, bo } // namespace Util -template <> -DeferredResourceDestruction -DeferredResourceDestruction::Wrapper(VkCommandPool object) -{ - DeferredResourceDestruction ret; - ret.object.command_pool = object; - ret.destroy_callback = [](VkDevice device, const Object& obj) { - vkDestroyCommandPool(device, obj.command_pool, nullptr); - }; - return ret; -} - -template <> -DeferredResourceDestruction -DeferredResourceDestruction::Wrapper(VkDeviceMemory object) -{ - DeferredResourceDestruction ret; - ret.object.device_memory = object; - ret.destroy_callback = [](VkDevice device, const Object& obj) { - vkFreeMemory(device, obj.device_memory, nullptr); - }; - return ret; -} - -template <> -DeferredResourceDestruction DeferredResourceDestruction::Wrapper(VkBuffer object) -{ - DeferredResourceDestruction ret; - ret.object.buffer = object; - ret.destroy_callback = [](VkDevice device, const Object& obj) { - vkDestroyBuffer(device, obj.buffer, nullptr); - }; - return ret; -} - -template <> -DeferredResourceDestruction DeferredResourceDestruction::Wrapper(VkBufferView object) -{ - DeferredResourceDestruction ret; - ret.object.buffer_view = object; - ret.destroy_callback = [](VkDevice device, const Object& obj) { - vkDestroyBufferView(device, obj.buffer_view, nullptr); - }; - return ret; -} - -template <> -DeferredResourceDestruction DeferredResourceDestruction::Wrapper(VkImage object) -{ - DeferredResourceDestruction ret; - ret.object.image = object; - ret.destroy_callback = [](VkDevice device, const Object& obj) { - vkDestroyImage(device, obj.image, nullptr); - }; - return ret; -} - -template <> -DeferredResourceDestruction DeferredResourceDestruction::Wrapper(VkImageView object) -{ - DeferredResourceDestruction ret; - ret.object.image_view = object; - ret.destroy_callback = [](VkDevice device, const Object& obj) { - vkDestroyImageView(device, obj.image_view, nullptr); - }; - return ret; -} - -template <> -DeferredResourceDestruction DeferredResourceDestruction::Wrapper(VkRenderPass object) -{ - DeferredResourceDestruction ret; - ret.object.render_pass = object; - ret.destroy_callback = [](VkDevice device, const Object& obj) { - vkDestroyRenderPass(device, obj.render_pass, nullptr); - }; - return ret; -} - -template <> -DeferredResourceDestruction -DeferredResourceDestruction::Wrapper(VkFramebuffer object) -{ - DeferredResourceDestruction ret; - ret.object.framebuffer = object; - ret.destroy_callback = [](VkDevice device, const Object& obj) { - vkDestroyFramebuffer(device, obj.framebuffer, nullptr); - }; - return ret; -} - -template <> -DeferredResourceDestruction -DeferredResourceDestruction::Wrapper(VkShaderModule object) -{ - DeferredResourceDestruction ret; - ret.object.shader_module = object; - ret.destroy_callback = [](VkDevice device, const Object& obj) { - vkDestroyShaderModule(device, obj.shader_module, nullptr); - }; - return ret; -} - -template <> -DeferredResourceDestruction DeferredResourceDestruction::Wrapper(VkPipeline object) -{ - DeferredResourceDestruction ret; - ret.object.pipeline = object; - ret.destroy_callback = [](VkDevice device, const Object& obj) { - vkDestroyPipeline(device, obj.pipeline, nullptr); - }; - return ret; -} - UtilityShaderDraw::UtilityShaderDraw(VkCommandBuffer command_buffer, VkPipelineLayout pipeline_layout, VkRenderPass render_pass, VkShaderModule vertex_shader, VkShaderModule geometry_shader, diff --git a/Source/Core/VideoBackends/Vulkan/Util.h b/Source/Core/VideoBackends/Vulkan/Util.h index dce0c720c7..e9dec51700 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.h +++ b/Source/Core/VideoBackends/Vulkan/Util.h @@ -66,28 +66,6 @@ VkShaderModule CompileAndCreateFragmentShader(const std::string& source_code, bool prepend_header = true); } -// Helper methods for cleaning up device objects, used by deferred destruction -struct DeferredResourceDestruction -{ - union Object { - VkCommandPool command_pool; - VkDeviceMemory device_memory; - VkBuffer buffer; - VkBufferView buffer_view; - VkImage image; - VkImageView image_view; - VkRenderPass render_pass; - VkFramebuffer framebuffer; - VkShaderModule shader_module; - VkPipeline pipeline; - } object; - - void (*destroy_callback)(VkDevice device, const Object& object); - - template - static DeferredResourceDestruction Wrapper(T object); -}; - // Utility shader vertex format #pragma pack(push, 1) struct UtilityShaderVertex diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index ef2e6e1fb3..7696568ef4 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -526,7 +526,8 @@ bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_la device_info.pQueueCreateInfos = &queue_info; ExtensionList enabled_extensions; - if (!SelectDeviceExtensions(&enabled_extensions, (surface != nullptr), enable_validation_layer)) + if (!SelectDeviceExtensions(&enabled_extensions, (surface != VK_NULL_HANDLE), + enable_validation_layer)) return false; device_info.enabledLayerCount = 0; From 4a8766cec4432f2e8bd31a973f44c020a69bd2c2 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 1 Oct 2016 22:22:14 +1000 Subject: [PATCH 2/7] Vulkan: Fix resource leaks present at shutdown and mode changes Infrequent, but still happened. --- .../Vulkan/FramebufferManager.cpp | 26 ++++++++++++++ .../Vulkan/PaletteTextureConverter.cpp | 3 ++ Source/Core/VideoBackends/Vulkan/Renderer.cpp | 2 ++ .../VideoBackends/Vulkan/TextureCache.cpp | 34 +++++++++++-------- 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp index 531327f53a..f78cf9953f 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp @@ -46,6 +46,7 @@ FramebufferManager::~FramebufferManager() DestroyReadbackRenderPasses(); DestroyPokeVertexBuffer(); + DestroyPokeShaders(); } bool FramebufferManager::Initialize() @@ -207,6 +208,12 @@ void FramebufferManager::DestroyEFBRenderPass() m_efb_load_render_pass = VK_NULL_HANDLE; } + if (m_efb_clear_render_pass != VK_NULL_HANDLE) + { + vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_efb_clear_render_pass, nullptr); + m_efb_clear_render_pass = VK_NULL_HANDLE; + } + if (m_depth_resolve_render_pass != VK_NULL_HANDLE) { vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_depth_resolve_render_pass, nullptr); @@ -352,6 +359,18 @@ void FramebufferManager::DestroyEFBFramebuffer() m_efb_framebuffer = VK_NULL_HANDLE; } + if (m_efb_convert_framebuffer != VK_NULL_HANDLE) + { + vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_efb_convert_framebuffer, nullptr); + m_efb_convert_framebuffer = VK_NULL_HANDLE; + } + + if (m_depth_resolve_framebuffer != VK_NULL_HANDLE) + { + vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_depth_resolve_framebuffer, nullptr); + m_depth_resolve_framebuffer = VK_NULL_HANDLE; + } + m_efb_color_texture.reset(); m_efb_convert_color_texture.reset(); m_efb_depth_texture.reset(); @@ -899,9 +918,15 @@ bool FramebufferManager::CreateReadbackRenderPasses() void FramebufferManager::DestroyReadbackRenderPasses() { if (m_copy_color_render_pass != VK_NULL_HANDLE) + { vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_copy_color_render_pass, nullptr); + m_copy_color_render_pass = VK_NULL_HANDLE; + } if (m_copy_depth_render_pass != VK_NULL_HANDLE) + { vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_copy_depth_render_pass, nullptr); + m_copy_depth_render_pass = VK_NULL_HANDLE; + } } bool FramebufferManager::CompileReadbackShaders() @@ -1238,6 +1263,7 @@ bool FramebufferManager::CreatePokeVertexBuffer() void FramebufferManager::DestroyPokeVertexBuffer() { + m_poke_vertex_stream_buffer.reset(); } bool FramebufferManager::CompilePokeShaders() diff --git a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp index 638edd5213..f67ed33b28 100644 --- a/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/PaletteTextureConverter.cpp @@ -41,6 +41,9 @@ PaletteTextureConverter::~PaletteTextureConverter() if (m_palette_buffer_view != VK_NULL_HANDLE) vkDestroyBufferView(g_vulkan_context->GetDevice(), m_palette_buffer_view, nullptr); + if (m_pipeline_layout != VK_NULL_HANDLE) + vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_pipeline_layout, nullptr); + if (m_palette_set_layout != VK_NULL_HANDLE) vkDestroyDescriptorSetLayout(g_vulkan_context->GetDevice(), m_palette_set_layout, nullptr); } diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index c6837ef7f8..9865dc2a0f 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -61,6 +61,8 @@ Renderer::~Renderer() { g_Config.bRunning = false; UpdateActiveConfig(); + DestroyScreenshotResources(); + DestroyShaders(); DestroySemaphores(); } diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp index e3b989c34e..4b83a76b59 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp @@ -41,6 +41,7 @@ TextureCache::~TextureCache() vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_initialize_render_pass, nullptr); if (m_update_render_pass != VK_NULL_HANDLE) vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_update_render_pass, nullptr); + TextureCache::DeleteShaders(); } bool TextureCache::Initialize(StateTracker* state_tracker) @@ -725,20 +726,25 @@ bool TextureCache::CompileShaders() void TextureCache::DeleteShaders() { - auto DestroyShader = [this](VkShaderModule& shader) { - if (shader != VK_NULL_HANDLE) - { - vkDestroyShaderModule(g_vulkan_context->GetDevice(), shader, nullptr); - shader = VK_NULL_HANDLE; - } - }; - - // Since this can be called by the base class we need to wait for idle. - g_command_buffer_mgr->WaitForGPUIdle(); - - DestroyShader(m_copy_shader); - DestroyShader(m_efb_color_to_tex_shader); - DestroyShader(m_efb_depth_to_tex_shader); + // It is safe to destroy shader modules after they are consumed by creating a pipeline. + // Therefore, no matter where this function is called from, it won't cause an issue due to + // pending commands, although at the time of writing should only be called at the end of + // a frame. See Vulkan spec, section 2.3.1. Object Lifetime. + if (m_copy_shader != VK_NULL_HANDLE) + { + vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_copy_shader, nullptr); + m_copy_shader = VK_NULL_HANDLE; + } + if (m_efb_color_to_tex_shader != VK_NULL_HANDLE) + { + vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_efb_color_to_tex_shader, nullptr); + m_efb_color_to_tex_shader = VK_NULL_HANDLE; + } + if (m_efb_depth_to_tex_shader != VK_NULL_HANDLE) + { + vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_efb_depth_to_tex_shader, nullptr); + m_efb_depth_to_tex_shader = VK_NULL_HANDLE; + } } } // namespace Vulkan From 7d14b9b48b3fd448db951eb29b64e24cead7aae5 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 1 Oct 2016 22:54:02 +1000 Subject: [PATCH 3/7] Vulkan: Add missing call to TextureCache::OnConfigChanged This was preventing certain settings from being updated when changed at runtime. --- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 9865dc2a0f..ff07889f8b 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -22,6 +22,7 @@ #include "VideoBackends/Vulkan/StagingTexture2D.h" #include "VideoBackends/Vulkan/StateTracker.h" #include "VideoBackends/Vulkan/SwapChain.h" +#include "VideoBackends/Vulkan/TextureCache.h" #include "VideoBackends/Vulkan/Util.h" #include "VideoBackends/Vulkan/VulkanContext.h" @@ -553,7 +554,7 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // Clean up stale textures - TextureCacheBase::Cleanup(frameCount); + TextureCache::Cleanup(frameCount); // Handle window resizes. CheckForTargetResize(fb_width, fb_stride, fb_height); @@ -928,6 +929,9 @@ void Renderer::CheckForConfigChanges() // Copy g_Config to g_ActiveConfig. UpdateActiveConfig(); + // Update texture cache settings with any changed options. + TextureCache::OnConfigChanged(g_ActiveConfig); + // MSAA samples changed, we need to recreate the EFB render pass, and all shaders. if (msaa_changed) { From b1932828303d290bb886fde940bf21a6e207d266 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 2 Oct 2016 21:37:24 +1000 Subject: [PATCH 4/7] Vulkan: Correct logic for handling target and window size changes Should fix a possible reference to deleted framebuffers, as well as fixing the issues with the render area being correct if the game's source area changes, or auto-scaling is enabled. --- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 108 +++++++++--------- Source/Core/VideoBackends/Vulkan/Renderer.h | 4 +- Source/Core/VideoBackends/Vulkan/main.cpp | 16 ++- 3 files changed, 69 insertions(+), 59 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index ff07889f8b..f9177d67cd 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -42,20 +42,25 @@ namespace Vulkan { -Renderer::Renderer() +Renderer::Renderer(std::unique_ptr swap_chain) : m_swap_chain(std::move(swap_chain)) { + g_Config.bRunning = true; + UpdateActiveConfig(); + // Set to something invalid, forcing all states to be re-initialized. for (size_t i = 0; i < m_sampler_states.size(); i++) m_sampler_states[i].bits = std::numeric_limits::max(); - // Set default size so a decent EFB is created initially. - s_backbuffer_width = MAX_XFB_WIDTH; - s_backbuffer_height = MAX_XFB_HEIGHT; + // These have to be initialized before FramebufferManager is created. + // If running surfaceless, assume a window size of MAX_XFB_{WIDTH,HEIGHT}. FramebufferManagerBase::SetLastXfbWidth(MAX_XFB_WIDTH); FramebufferManagerBase::SetLastXfbHeight(MAX_XFB_HEIGHT); - PixelShaderManager::SetEfbScaleChanged(); + s_backbuffer_width = m_swap_chain ? m_swap_chain->GetWidth() : MAX_XFB_WIDTH; + s_backbuffer_height = m_swap_chain ? m_swap_chain->GetHeight() : MAX_XFB_HEIGHT; + s_last_efb_scale = g_ActiveConfig.iEFBScale; UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height); CalculateTargetSize(s_backbuffer_width, s_backbuffer_height); + PixelShaderManager::SetEfbScaleChanged(); } Renderer::~Renderer() @@ -67,14 +72,9 @@ Renderer::~Renderer() DestroySemaphores(); } -bool Renderer::Initialize(FramebufferManager* framebuffer_mgr, void* window_handle, - VkSurfaceKHR surface) +bool Renderer::Initialize(FramebufferManager* framebuffer_mgr) { m_framebuffer_mgr = framebuffer_mgr; - g_Config.bRunning = true; - UpdateActiveConfig(); - - // Create state tracker, doesn't require any resources m_state_tracker = std::make_unique(); BindEFBToStateTracker(); @@ -112,24 +112,6 @@ bool Renderer::Initialize(FramebufferManager* framebuffer_mgr, void* window_hand m_bounding_box->GetGPUBufferSize()); } - // Initialize annoying statics - s_last_efb_scale = g_ActiveConfig.iEFBScale; - - // Create swap chain - if (surface) - { - // Update backbuffer dimensions - m_swap_chain = SwapChain::Create(window_handle, surface); - if (!m_swap_chain) - { - PanicAlert("Failed to create swap chain."); - return false; - } - - // Update render rectangle etc. - OnSwapChainResized(); - } - // Various initialization routines will have executed commands on the command buffer. // Execute what we have done before beginning the first frame. g_command_buffer_mgr->PrepareToSubmitCommandBuffer(); @@ -494,9 +476,6 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height // Scale the source rectangle to the selected internal resolution. TargetRectangle source_rc = Renderer::ConvertEFBRectangle(rc); - // Target rectangle can change if the VI aspect ratio changes. - UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height); - // Transition the EFB render target to a shader resource. VkRect2D src_region = {{0, 0}, {m_framebuffer_mgr->GetEFBWidth(), m_framebuffer_mgr->GetEFBHeight()}}; @@ -522,6 +501,10 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height StopFrameDump(); } + // Restore the EFB color texture to color attachment ready for rendering the next frame. + m_framebuffer_mgr->GetEFBColorTexture()->TransitionToLayout( + g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + // Ensure the worker thread is not still submitting a previous command buffer. // In other words, the last frame has been submitted (otherwise the next call would // be a race, as the image may not have been consumed yet). @@ -546,22 +529,28 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height g_command_buffer_mgr->SubmitCommandBuffer(true); } + // NOTE: It is important that no rendering calls are made to the EFB between submitting the + // (now-previous) frame and after the below config checks are completed. If the target size + // changes, as the resize methods to not defer the destruction of the framebuffer, the current + // command buffer will contain references to a now non-existent framebuffer. + // Prep for the next frame (get command buffer ready) before doing anything else. BeginFrame(); - // Restore the EFB color texture to color attachment ready for rendering. - m_framebuffer_mgr->GetEFBColorTexture()->TransitionToLayout( - g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + // Determine what (if anything) has changed in the config. + CheckForConfigChanges(); - // Clean up stale textures - TextureCache::Cleanup(frameCount); - - // Handle window resizes. - CheckForTargetResize(fb_width, fb_stride, fb_height); + // Handle host window resizes. CheckForSurfaceChange(); - // Determine what has changed in the config. - CheckForConfigChanges(); + // Handle output size changes from the guest. + // There is a downside to doing this here is that if the game changes its XFB source area, + // the changes will be delayed by one frame. For the moment it has to be done here because + // this can cause a target size change, which would result in a black frame if done earlier. + CheckForTargetResize(fb_width, fb_stride, fb_height); + + // Clean up stale textures. + TextureCacheBase::Cleanup(frameCount); } void Renderer::DrawScreen(const TargetRectangle& src_rect, const Texture2D* src_tex) @@ -832,13 +821,23 @@ void Renderer::StopFrameDump() void Renderer::CheckForTargetResize(u32 fb_width, u32 fb_stride, u32 fb_height) { - if (FramebufferManagerBase::LastXfbWidth() != fb_stride || - FramebufferManagerBase::LastXfbHeight() != fb_height) + if (FramebufferManagerBase::LastXfbWidth() == fb_stride && + FramebufferManagerBase::LastXfbHeight() == fb_height) { - u32 last_w = (fb_stride < 1 || fb_stride > MAX_XFB_WIDTH) ? MAX_XFB_WIDTH : fb_stride; - u32 last_h = (fb_height < 1 || fb_height > MAX_XFB_HEIGHT) ? MAX_XFB_HEIGHT : fb_height; - FramebufferManagerBase::SetLastXfbWidth(last_w); - FramebufferManagerBase::SetLastXfbHeight(last_h); + return; + } + + u32 new_width = (fb_stride < 1 || fb_stride > MAX_XFB_WIDTH) ? MAX_XFB_WIDTH : fb_stride; + u32 new_height = (fb_height < 1 || fb_height > MAX_XFB_HEIGHT) ? MAX_XFB_HEIGHT : fb_height; + FramebufferManagerBase::SetLastXfbWidth(new_width); + FramebufferManagerBase::SetLastXfbHeight(new_height); + + // Changing the XFB source area will likely change the final drawing rectangle. + UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height); + if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height)) + { + PixelShaderManager::SetEfbScaleChanged(); + ResizeEFBTextures(); } // This call is needed for auto-resizing to work. @@ -853,9 +852,6 @@ void Renderer::CheckForSurfaceChange() u32 old_width = m_swap_chain ? m_swap_chain->GetWidth() : 0; u32 old_height = m_swap_chain ? m_swap_chain->GetHeight() : 0; - // Wait for the GPU to catch up since we're going to destroy the swap chain. - g_command_buffer_mgr->WaitForGPUIdle(); - // Fast path, if the surface handle is the same, the window has just been resized. if (m_swap_chain && s_new_surface_handle == m_swap_chain->GetNativeHandle()) { @@ -869,6 +865,9 @@ void Renderer::CheckForSurfaceChange() } else { + // Wait for the GPU to catch up since we're going to destroy the swap chain. + g_command_buffer_mgr->WaitForGPUIdle(); + // Did we previously have a swap chain? if (m_swap_chain) { @@ -976,13 +975,12 @@ void Renderer::OnSwapChainResized() { s_backbuffer_width = m_swap_chain->GetWidth(); s_backbuffer_height = m_swap_chain->GetHeight(); - FramebufferManagerBase::SetLastXfbWidth(MAX_XFB_WIDTH); - FramebufferManagerBase::SetLastXfbHeight(MAX_XFB_HEIGHT); UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height); if (CalculateTargetSize(s_backbuffer_width, s_backbuffer_height)) + { + PixelShaderManager::SetEfbScaleChanged(); ResizeEFBTextures(); - - PixelShaderManager::SetEfbScaleChanged(); + } } void Renderer::BindEFBToStateTracker() diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index 1b1e175272..d59ad2fe73 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -25,13 +25,13 @@ class RasterFont; class Renderer : public ::Renderer { public: - Renderer(); + Renderer(std::unique_ptr swap_chain); ~Renderer(); SwapChain* GetSwapChain() const { return m_swap_chain.get(); } StateTracker* GetStateTracker() const { return m_state_tracker.get(); } BoundingBox* GetBoundingBox() const { return m_bounding_box.get(); } - bool Initialize(FramebufferManager* framebuffer_mgr, void* window_handle, VkSurfaceKHR surface); + bool Initialize(FramebufferManager* framebuffer_mgr); void RenderText(const std::string& pstr, int left, int top, u32 color) override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; diff --git a/Source/Core/VideoBackends/Vulkan/main.cpp b/Source/Core/VideoBackends/Vulkan/main.cpp index a3ec2bc9f9..a9debda541 100644 --- a/Source/Core/VideoBackends/Vulkan/main.cpp +++ b/Source/Core/VideoBackends/Vulkan/main.cpp @@ -165,6 +165,18 @@ bool VideoBackend::Initialize(void* window_handle) return false; } + // Create swap chain. This has to be done early so that the target size is correct for auto-scale. + std::unique_ptr swap_chain; + if (surface != VK_NULL_HANDLE) + { + swap_chain = SwapChain::Create(window_handle, surface); + if (!swap_chain) + { + PanicAlert("Failed to create Vulkan swap chain."); + return false; + } + } + // Create command buffers. We do this separately because the other classes depend on it. g_command_buffer_mgr = std::make_unique(g_Config.bBackendMultithreading); if (!g_command_buffer_mgr->Initialize()) @@ -180,7 +192,7 @@ bool VideoBackend::Initialize(void* window_handle) // Create main wrapper instances. g_object_cache = std::make_unique(); g_framebuffer_manager = std::make_unique(); - g_renderer = std::make_unique(); + g_renderer = std::make_unique(std::move(swap_chain)); // Cast to our wrapper classes, so we can call the init methods. Renderer* renderer = static_cast(g_renderer.get()); @@ -191,7 +203,7 @@ bool VideoBackend::Initialize(void* window_handle) // These have to be done before the others because the destructors // for the remaining classes may call methods on these. if (!g_object_cache->Initialize() || !framebuffer_mgr->Initialize() || - !renderer->Initialize(framebuffer_mgr, window_handle, surface)) + !renderer->Initialize(framebuffer_mgr)) { PanicAlert("Failed to initialize Vulkan classes."); g_renderer.reset(); From 5e29508b8f4747e92e8de68b75971f26e4a78cd4 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 2 Oct 2016 22:09:19 +1000 Subject: [PATCH 5/7] Vulkan: Fix vsync behavior when throttler is temp disabled --- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 10 ++++--- .../Core/VideoBackends/Vulkan/SwapChain.cpp | 26 ++++++++++++------- Source/Core/VideoBackends/Vulkan/SwapChain.h | 11 +++++--- Source/Core/VideoBackends/Vulkan/main.cpp | 2 +- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index f9177d67cd..4a0c29bac3 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -890,7 +890,7 @@ void Renderer::CheckForSurfaceChange() s_new_surface_handle); if (surface != VK_NULL_HANDLE) { - m_swap_chain = SwapChain::Create(s_new_surface_handle, surface); + m_swap_chain = SwapChain::Create(s_new_surface_handle, surface, g_ActiveConfig.IsVSync()); if (!m_swap_chain) PanicAlert("Failed to create swap chain."); } @@ -917,7 +917,6 @@ void Renderer::CheckForSurfaceChange() void Renderer::CheckForConfigChanges() { // Compare g_Config to g_ActiveConfig to determine what has changed before copying. - bool vsync_changed = (g_Config.bVSync != g_ActiveConfig.bVSync); bool msaa_changed = (g_Config.iMultisamples != g_ActiveConfig.iMultisamples); bool ssaa_changed = (g_Config.bSSAA != g_ActiveConfig.bSSAA); bool anisotropy_changed = (g_Config.iMaxAnisotropy != g_ActiveConfig.iMaxAnisotropy); @@ -963,8 +962,11 @@ void Renderer::CheckForConfigChanges() } // For vsync, we need to change the present mode, which means recreating the swap chain. - if (vsync_changed) - ResizeSwapChain(); + if (m_swap_chain && g_ActiveConfig.IsVSync() != m_swap_chain->IsVSyncEnabled()) + { + g_command_buffer_mgr->WaitForGPUIdle(); + m_swap_chain->SetVSync(g_ActiveConfig.IsVSync()); + } // Wipe sampler cache if force texture filtering or anisotropy changes. if (anisotropy_changed || force_texture_filtering_changed) diff --git a/Source/Core/VideoBackends/Vulkan/SwapChain.cpp b/Source/Core/VideoBackends/Vulkan/SwapChain.cpp index 34ad5c39f2..899f931c6d 100644 --- a/Source/Core/VideoBackends/Vulkan/SwapChain.cpp +++ b/Source/Core/VideoBackends/Vulkan/SwapChain.cpp @@ -24,8 +24,8 @@ namespace Vulkan { -SwapChain::SwapChain(void* native_handle, VkSurfaceKHR surface) - : m_native_handle(native_handle), m_surface(surface) +SwapChain::SwapChain(void* native_handle, VkSurfaceKHR surface, bool vsync) + : m_native_handle(native_handle), m_surface(surface), m_vsync_enabled(vsync) { } @@ -127,9 +127,10 @@ VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, void* hwnd) #endif } -std::unique_ptr SwapChain::Create(void* native_handle, VkSurfaceKHR surface) +std::unique_ptr SwapChain::Create(void* native_handle, VkSurfaceKHR surface, bool vsync) { - std::unique_ptr swap_chain = std::make_unique(native_handle, surface); + std::unique_ptr swap_chain = + std::make_unique(native_handle, surface, vsync); if (!swap_chain->CreateSwapChain() || !swap_chain->CreateRenderPass() || !swap_chain->SetupSwapChainImages()) @@ -198,7 +199,7 @@ bool SwapChain::SelectPresentMode() }; // If vsync is enabled, prefer VK_PRESENT_MODE_FIFO_KHR. - if (g_ActiveConfig.IsVSync()) + if (m_vsync_enabled) { // Try for relaxed vsync first, since it's likely the VI won't line up with // the refresh rate of the system exactly, so tearing once is better than @@ -456,11 +457,8 @@ VkResult SwapChain::AcquireNextImage(VkSemaphore available_semaphore) bool SwapChain::ResizeSwapChain() { - if (!CreateSwapChain()) - return false; - DestroySwapChainImages(); - if (!SetupSwapChainImages()) + if (!CreateSwapChain() || !SetupSwapChainImages()) { PanicAlert("Failed to re-configure swap chain images, this is fatal (for now)"); return false; @@ -469,6 +467,16 @@ bool SwapChain::ResizeSwapChain() return true; } +bool SwapChain::SetVSync(bool enabled) +{ + if (m_vsync_enabled == enabled) + return true; + + // Resizing recreates the swap chain with the new present mode. + m_vsync_enabled = enabled; + return ResizeSwapChain(); +} + bool SwapChain::RecreateSurface(void* native_handle) { // Destroy the old swap chain, images, and surface. diff --git a/Source/Core/VideoBackends/Vulkan/SwapChain.h b/Source/Core/VideoBackends/Vulkan/SwapChain.h index f0ccb81921..0ff480d64c 100644 --- a/Source/Core/VideoBackends/Vulkan/SwapChain.h +++ b/Source/Core/VideoBackends/Vulkan/SwapChain.h @@ -19,18 +19,19 @@ class ObjectCache; class SwapChain { public: - SwapChain(void* native_handle, VkSurfaceKHR surface); + SwapChain(void* native_handle, VkSurfaceKHR surface, bool vsync); ~SwapChain(); // Creates a vulkan-renderable surface for the specified window handle. static VkSurfaceKHR CreateVulkanSurface(VkInstance instance, void* hwnd); // Create a new swap chain from a pre-existing surface. - static std::unique_ptr Create(void* native_handle, VkSurfaceKHR surface); + static std::unique_ptr Create(void* native_handle, VkSurfaceKHR surface, bool vsync); void* GetNativeHandle() const { return m_native_handle; } VkSurfaceKHR GetSurface() const { return m_surface; } VkSurfaceFormatKHR GetSurfaceFormat() const { return m_surface_format; } + bool IsVSyncEnabled() const { return m_vsync_enabled; } VkSwapchainKHR GetSwapChain() const { return m_swap_chain; } VkRenderPass GetRenderPass() const { return m_render_pass; } u32 GetWidth() const { return m_width; } @@ -54,6 +55,9 @@ public: bool RecreateSurface(void* native_handle); bool ResizeSwapChain(); + // Change vsync enabled state. This may fail as it causes a swapchain recreation. + bool SetVSync(bool enabled); + private: bool SelectSurfaceFormat(); bool SelectPresentMode(); @@ -76,10 +80,11 @@ private: VkFramebuffer framebuffer; }; - void* m_native_handle = nullptr; + void* m_native_handle; VkSurfaceKHR m_surface = VK_NULL_HANDLE; VkSurfaceFormatKHR m_surface_format = {}; VkPresentModeKHR m_present_mode = VK_PRESENT_MODE_RANGE_SIZE_KHR; + bool m_vsync_enabled; VkSwapchainKHR m_swap_chain = VK_NULL_HANDLE; std::vector m_swap_chain_images; diff --git a/Source/Core/VideoBackends/Vulkan/main.cpp b/Source/Core/VideoBackends/Vulkan/main.cpp index a9debda541..1fb8f6c6ab 100644 --- a/Source/Core/VideoBackends/Vulkan/main.cpp +++ b/Source/Core/VideoBackends/Vulkan/main.cpp @@ -169,7 +169,7 @@ bool VideoBackend::Initialize(void* window_handle) std::unique_ptr swap_chain; if (surface != VK_NULL_HANDLE) { - swap_chain = SwapChain::Create(window_handle, surface); + swap_chain = SwapChain::Create(window_handle, surface, g_Config.IsVSync()); if (!swap_chain) { PanicAlert("Failed to create Vulkan swap chain."); From f595fe080f49613195ab52a7c7fd711aa5c1fe56 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 2 Oct 2016 23:53:09 +1000 Subject: [PATCH 6/7] Vulkan: Fix bug with fractional LOD bias and min/max LOD --- Source/Core/VideoBackends/Vulkan/Constants.h | 4 ++-- .../Core/VideoBackends/Vulkan/ObjectCache.cpp | 24 +++++++++++-------- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 21 ++++------------ .../Core/VideoBackends/Vulkan/VulkanContext.h | 2 +- 4 files changed, 22 insertions(+), 29 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/Constants.h b/Source/Core/VideoBackends/Vulkan/Constants.h index d6ac8d7ae2..a135c76180 100644 --- a/Source/Core/VideoBackends/Vulkan/Constants.h +++ b/Source/Core/VideoBackends/Vulkan/Constants.h @@ -130,8 +130,8 @@ union SamplerState { BitField<5, 2, VkSamplerAddressMode> wrap_v; BitField<7, 8, u32> min_lod; BitField<15, 8, u32> max_lod; - BitField<23, 6, s32> lod_bias; // tm0.lod_bias (8 bits) / 32 gives us 0-7. - BitField<29, 3, u32> anisotropy; // max_anisotropy = 1 << anisotropy, max of 16, so range 0-4. + BitField<23, 8, s32> lod_bias; + BitField<31, 1, u32> enable_anisotropic_filtering; u32 bits; }; diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp index 6932830bfb..cf90fc2f60 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp @@ -784,11 +784,6 @@ VkSampler ObjectCache::GetSampler(const SamplerState& info) if (iter != m_sampler_cache.end()) return iter->second; - // Cap anisotropy to device limits. - VkBool32 anisotropy_enable = (info.anisotropy != 0) ? VK_TRUE : VK_FALSE; - float max_anisotropy = std::min(static_cast(1 << info.anisotropy), - g_vulkan_context->GetMaxSaxmplerAnisotropy()); - VkSamplerCreateInfo create_info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // VkStructureType sType nullptr, // const void* pNext @@ -799,17 +794,26 @@ VkSampler ObjectCache::GetSampler(const SamplerState& info) info.wrap_u, // VkSamplerAddressMode addressModeU info.wrap_v, // VkSamplerAddressMode addressModeV VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeW - static_cast(info.lod_bias.Value()), // float mipLodBias - anisotropy_enable, // VkBool32 anisotropyEnable - max_anisotropy, // float maxAnisotropy + static_cast(info.lod_bias / 32.0f), // float mipLodBias + VK_FALSE, // VkBool32 anisotropyEnable + 0.0f, // float maxAnisotropy VK_FALSE, // VkBool32 compareEnable VK_COMPARE_OP_ALWAYS, // VkCompareOp compareOp - static_cast(info.min_lod.Value()), // float minLod - static_cast(info.max_lod.Value()), // float maxLod + static_cast(info.min_lod / 16.0f), // float minLod + static_cast(info.max_lod / 16.0f), // float maxLod VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, // VkBorderColor borderColor VK_FALSE // VkBool32 unnormalizedCoordinates }; + // Can we use anisotropic filtering with this sampler? + if (info.enable_anisotropic_filtering && g_vulkan_context->SupportsAnisotropicFiltering()) + { + // Cap anisotropy to device limits. + create_info.anisotropyEnable = VK_TRUE; + create_info.maxAnisotropy = std::min(static_cast(1 << g_ActiveConfig.iMaxAnisotropy), + g_vulkan_context->GetMaxSamplerAnisotropy()); + } + VkSampler sampler = VK_NULL_HANDLE; VkResult res = vkCreateSampler(g_vulkan_context->GetDevice(), &create_info, nullptr, &sampler); if (res != VK_SUCCESS) diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 4a0c29bac3..7bb15aacdf 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -1367,13 +1367,9 @@ void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex) } // If mipmaps are disabled, clamp min/max lod - new_state.max_lod = (SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0)) ? - static_cast(MathUtil::Clamp(tm1.max_lod / 16.0f, 0.0f, 255.0f)) : - 0; - new_state.min_lod = - std::min(new_state.max_lod.Value(), - static_cast(MathUtil::Clamp(tm1.min_lod / 16.0f, 0.0f, 255.0f))); - new_state.lod_bias = static_cast(tm0.lod_bias / 32.0f); + new_state.max_lod = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm1.max_lod : 0; + new_state.min_lod = std::min(new_state.max_lod.Value(), tm1.min_lod); + new_state.lod_bias = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm0.lod_bias : 0; // Custom textures may have a greater number of mips if (custom_tex) @@ -1387,15 +1383,7 @@ void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex) new_state.wrap_v = address_modes[tm0.wrap_t]; // Only use anisotropic filtering for textures that would be linearly filtered. - if (g_vulkan_context->SupportsAnisotropicFiltering() && g_ActiveConfig.iMaxAnisotropy > 0 && - !SamplerCommon::IsBpTexMode0PointFiltering(tm0)) - { - new_state.anisotropy = g_ActiveConfig.iMaxAnisotropy; - } - else - { - new_state.anisotropy = 0; - } + new_state.enable_anisotropic_filtering = SamplerCommon::IsBpTexMode0PointFiltering(tm0) ? 0 : 1; // Skip lookup if the state hasn't changed. size_t bind_index = (texindex * 4) + stage; @@ -1417,6 +1405,7 @@ void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex) void Renderer::ResetSamplerStates() { // Ensure none of the sampler objects are in use. + // This assumes that none of the samplers are in use on the command list currently being recorded. g_command_buffer_mgr->WaitForGPUIdle(); // Invalidate all sampler states, next draw will re-initialize them. diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.h b/Source/Core/VideoBackends/Vulkan/VulkanContext.h index 382163b654..8227f1e6af 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.h +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.h @@ -93,7 +93,7 @@ public: { return m_device_properties.limits.bufferImageGranularity; } - float GetMaxSaxmplerAnisotropy() const { return m_device_properties.limits.maxSamplerAnisotropy; } + float GetMaxSamplerAnisotropy() const { return m_device_properties.limits.maxSamplerAnisotropy; } // Finds a memory type index for the specified memory properties and the bits returned by // vkGetImageMemoryRequirements bool GetMemoryType(u32 bits, VkMemoryPropertyFlags properties, u32* out_type_index); From 28e5fa8d261cd486e9339a66d91b4713e83d9a14 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 2 Oct 2016 23:56:55 +1000 Subject: [PATCH 7/7] Vulkan: Handle both destination alpha and logic ops being enabled Same way as GL with the dual-pass fallback. Not highly accurate, but does fix the Kirby shadow bug. --- Source/Core/VideoBackends/Vulkan/VertexManager.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp index e95c1d6ade..02be335aea 100644 --- a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp @@ -198,8 +198,14 @@ void VertexManager::vFlush(bool use_dst_alpha) vkCmdDrawIndexed(g_command_buffer_mgr->GetCurrentCommandBuffer(), index_count, 1, m_current_draw_base_index, m_current_draw_base_vertex, 0); - // If we can't do single pass dst alpha, we now need to draw the alpha pass. - if (use_dst_alpha && !g_vulkan_context->SupportsDualSourceBlend()) + // If the GPU does not support dual-source blending, we can approximate the effect by drawing + // the object a second time, with the write mask set to alpha only using a shader that outputs + // the destination/constant alpha value (which would normally be SRC_COLOR.a). + // + // This is also used when logic ops and destination alpha is enabled, since we can't enable + // blending and logic ops concurrently (and the logical operation applies to all channels). + bool logic_op_enabled = bpmem.blendmode.logicopenable && !bpmem.blendmode.blendenable; + if (use_dst_alpha && (!g_vulkan_context->SupportsDualSourceBlend() || logic_op_enabled)) { m_state_tracker->CheckForShaderChanges(m_current_primitive_type, DSTALPHA_ALPHA_PASS); if (!m_state_tracker->Bind())