From 734077642ebd80892ba27dc928802f19373ffae9 Mon Sep 17 00:00:00 2001 From: emufan4568 Date: Fri, 12 Aug 2022 15:36:09 +0300 Subject: [PATCH] video_core: Fix destruction sequence Rejoice! Vulkan no longer leaks memory --- CMakeModules/VcpkgCmakeUtils.cmake | 2 +- src/video_core/common/backend.h | 3 +++ src/video_core/common/renderer.cpp | 12 +++++++--- src/video_core/common/renderer.h | 4 ++-- src/video_core/renderer_vulkan/vk_backend.cpp | 10 +++++++- src/video_core/renderer_vulkan/vk_backend.h | 5 +--- src/video_core/renderer_vulkan/vk_buffer.cpp | 5 +++- .../renderer_vulkan/vk_instance.cpp | 2 ++ .../renderer_vulkan/vk_pipeline.cpp | 2 +- .../renderer_vulkan/vk_renderpass_cache.h | 2 +- .../renderer_vulkan/vk_task_scheduler.cpp | 23 ++++++++++++++----- .../renderer_vulkan/vk_task_scheduler.h | 4 ++-- 12 files changed, 52 insertions(+), 22 deletions(-) diff --git a/CMakeModules/VcpkgCmakeUtils.cmake b/CMakeModules/VcpkgCmakeUtils.cmake index 9c34d40ba..b171c604d 100644 --- a/CMakeModules/VcpkgCmakeUtils.cmake +++ b/CMakeModules/VcpkgCmakeUtils.cmake @@ -19,7 +19,7 @@ endif() # Install a package using the command line interface function(vcpkg_add_package PKG_NAME) # Run the executable to install dependencies - set(VCPKG_TARGET_TRIPLET_FLAG "--triplet=${VCPKG_TARGET_TRIPLET}") + set(VCPKG_TARGET_TRIPLET_FLAG "--triplet=x64-windows-static") message(STATUS "VCPKG: Installing ${PKG_NAME}") execute_process(COMMAND ${VCPKG_EXECUTABLE} ${VCPKG_TARGET_TRIPLET_FLAG} ${VCPKG_RECURSE_REBUILD_FLAG} --disable-metrics install "${PKG_NAME}" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} RESULT_VARIABLE VCPKG_INSTALL_OK) if (NOT VCPKG_INSTALL_OK EQUAL "0") diff --git a/src/video_core/common/backend.h b/src/video_core/common/backend.h index ad2dff664..3544f260a 100644 --- a/src/video_core/common/backend.h +++ b/src/video_core/common/backend.h @@ -32,6 +32,9 @@ public: // Triggers a swapchain buffer swap virtual void EndPresent() = 0; + // Submits any pending work and blocks the host until it completes + virtual void Flush() = 0; + // Returns the framebuffer created from the swapchain images virtual FramebufferHandle GetWindowFramebuffer() = 0; diff --git a/src/video_core/common/renderer.cpp b/src/video_core/common/renderer.cpp index 7c959561c..42f9baae2 100644 --- a/src/video_core/common/renderer.cpp +++ b/src/video_core/common/renderer.cpp @@ -56,7 +56,8 @@ layout (std140, push_constant) uniform PresentUniformData { }; void main() { - color = texture(sampler2D(top_screen, screen_sampler), frag_tex_coord); + //color = texture(sampler2D(top_screen, screen_sampler), frag_tex_coord); + color = vec4(1.f, 0.f, 0.f, 1.f); } )"; @@ -158,7 +159,7 @@ DisplayRenderer::DisplayRenderer(Frontend::EmuWindow& window) : render_window(wi // Create vertex buffer for the screen rectangle const BufferInfo vertex_info = { - .capacity = sizeof(ScreenRectVertex) * 10, + .capacity = sizeof(ScreenRectVertex) * 16, .usage = BufferUsage::Vertex }; @@ -207,6 +208,11 @@ DisplayRenderer::DisplayRenderer(Frontend::EmuWindow& window) : render_window(wi present_pipelines[0]->BindSampler(1, 0, screen_sampler); } +DisplayRenderer::~DisplayRenderer() { + // Must flush the backend before destroying any pipelines! + backend->Flush(); +} + void DisplayRenderer::PrepareRendertarget() { for (int i = 0; i < 3; i++) { int fb_id = i == 2 ? 1 : 0; @@ -387,8 +393,8 @@ void DisplayRenderer::DrawSingleScreen(u32 screen, bool rotate, float x, float y } const u32 size = sizeof(ScreenRectVertex) * vertices.size(); - const u64 mapped_offset = vertex_buffer->GetCurrentOffset(); auto vertex_data = vertex_buffer->Map(size); + const u64 mapped_offset = vertex_buffer->GetCurrentOffset(); // Copy vertex data std::memcpy(vertex_data.data(), vertices.data(), size); diff --git a/src/video_core/common/renderer.h b/src/video_core/common/renderer.h index 4e3d17bdf..add6dfd9f 100644 --- a/src/video_core/common/renderer.h +++ b/src/video_core/common/renderer.h @@ -67,7 +67,7 @@ constexpr u32 PRESENT_PIPELINES = 3; class DisplayRenderer { public: DisplayRenderer(Frontend::EmuWindow& window); - ~DisplayRenderer() = default; + ~DisplayRenderer(); void SwapBuffers(); void TryPresent(int timeout_ms) {} @@ -121,8 +121,8 @@ private: void LoadColorToActiveTexture(u8 color_r, u8 color_g, u8 color_b, const ScreenInfo& screen); private: - std::unique_ptr rasterizer; std::unique_ptr backend; + std::unique_ptr rasterizer; Frontend::EmuWindow& render_window; Common::Vec4f clear_color; f32 m_current_fps = 0.0f; diff --git a/src/video_core/renderer_vulkan/vk_backend.cpp b/src/video_core/renderer_vulkan/vk_backend.cpp index ee8b7d731..618fb70a7 100644 --- a/src/video_core/renderer_vulkan/vk_backend.cpp +++ b/src/video_core/renderer_vulkan/vk_backend.cpp @@ -81,7 +81,11 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window), } Backend::~Backend() { + + // Wait for all GPU operations to finish before continuing vk::Device device = instance.GetDevice(); + device.waitIdle(); + device.destroyPipelineCache(pipeline_cache); for (u32 pool = 0; pool < SCHEDULER_COMMAND_COUNT; pool++) { @@ -100,10 +104,14 @@ bool Backend::BeginPresent() { } void Backend::EndPresent() { - scheduler.Submit(false, swapchain.GetAvailableSemaphore(), swapchain.GetPresentSemaphore()); + scheduler.Submit(false, true, swapchain.GetAvailableSemaphore(), swapchain.GetPresentSemaphore()); swapchain.Present(); } +void Backend::Flush() { + scheduler.Submit(true); +} + FramebufferHandle Backend::GetWindowFramebuffer() { auto framebuffer = swapchain.GetCurrentFramebuffer(); auto extent = swapchain.GetExtent(); diff --git a/src/video_core/renderer_vulkan/vk_backend.h b/src/video_core/renderer_vulkan/vk_backend.h index 8aeb129c9..b7c98ed99 100644 --- a/src/video_core/renderer_vulkan/vk_backend.h +++ b/src/video_core/renderer_vulkan/vk_backend.h @@ -21,11 +21,10 @@ public: bool BeginPresent() override; void EndPresent() override; + void Flush() override; FramebufferHandle GetWindowFramebuffer() override; - u64 QueryDriver(Query query) override { return 0; } - u64 PipelineInfoHash(const PipelineInfo& info) override; BufferHandle CreateBuffer(BufferInfo info) override; @@ -40,10 +39,8 @@ public: void Draw(PipelineHandle pipeline, FramebufferHandle draw_framebuffer, u32 base_vertex, u32 num_vertices) override; - void DrawIndexed(PipelineHandle pipeline, FramebufferHandle draw_framebuffer, u32 base_index, u32 num_indices, u32 base_vertex) override; - void DispatchCompute(PipelineHandle pipeline, Common::Vec3 groupsize, Common::Vec3 groups) override {} diff --git a/src/video_core/renderer_vulkan/vk_buffer.cpp b/src/video_core/renderer_vulkan/vk_buffer.cpp index a03f0e9c3..e1f1ca93a 100644 --- a/src/video_core/renderer_vulkan/vk_buffer.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer.cpp @@ -148,7 +148,7 @@ void Buffer::Commit(u32 size) { if (info.usage == BufferUsage::Staging && size > 0) { vmaFlushAllocation(allocator, allocation, buffer_offset, size); } else { - vk::CommandBuffer command_buffer = scheduler.GetUploadCommandBuffer(); + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); Buffer& staging = scheduler.GetCommandUploadBuffer(); const vk::BufferCopy copy_region = { @@ -158,6 +158,7 @@ void Buffer::Commit(u32 size) { }; // Copy staging buffer to device local buffer + staging.Commit(size); command_buffer.copyBuffer(staging.GetHandle(), buffer, copy_region); vk::AccessFlags access_mask; @@ -184,6 +185,8 @@ void Buffer::Commit(u32 size) { const vk::BufferMemoryBarrier buffer_barrier = { .srcAccessMask = vk::AccessFlagBits::eTransferWrite, .dstAccessMask = access_mask, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .buffer = buffer, .offset = buffer_offset, .size = size diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 3376db618..a1b7f8eb2 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -60,7 +60,9 @@ Instance::Instance(Frontend::EmuWindow& window) { Instance::~Instance() { device.waitIdle(); + vmaDestroyAllocator(allocator); device.destroy(); + instance.destroySurfaceKHR(surface); instance.destroy(); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline.cpp b/src/video_core/renderer_vulkan/vk_pipeline.cpp index d2e8c974e..1c51a483d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline.cpp @@ -183,7 +183,7 @@ PipelineOwner::~PipelineOwner() { u32 i = 0; while (set_layouts[i] && update_templates[i]) { device.destroyDescriptorSetLayout(set_layouts[i]); - device.destroyDescriptorUpdateTemplate(update_templates[i]); + device.destroyDescriptorUpdateTemplate(update_templates[i++]); } } diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.h b/src/video_core/renderer_vulkan/vk_renderpass_cache.h index 98c69390e..07d48f795 100644 --- a/src/video_core/renderer_vulkan/vk_renderpass_cache.h +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.h @@ -36,7 +36,7 @@ private: // Special renderpass used for rendering to the swapchain vk::RenderPass present_renderpass; // [color_format][depth_format][is_clear_pass] - vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS][MAX_DEPTH_FORMATS][2]; + vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS+1][MAX_DEPTH_FORMATS+1][2]; }; } // namespace VideoCore::Vulkan diff --git a/src/video_core/renderer_vulkan/vk_task_scheduler.cpp b/src/video_core/renderer_vulkan/vk_task_scheduler.cpp index 3ae907844..b58f59f21 100644 --- a/src/video_core/renderer_vulkan/vk_task_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_task_scheduler.cpp @@ -64,6 +64,9 @@ CommandScheduler::~CommandScheduler() { vk::Device device = instance.GetDevice(); VmaAllocator allocator = instance.GetAllocator(); + // Submit any remaining work + Submit(true, false); + for (auto& command : commands) { device.destroyFence(command.fence); @@ -110,10 +113,11 @@ void CommandScheduler::SetSwitchCallback(std::function callback) { switch_callback = callback; } -void CommandScheduler::Submit(bool wait_completion, vk::Semaphore wait_semaphore, vk::Semaphore signal_semaphore) { - const CommandSlot& command = commands[current_command]; +void CommandScheduler::Submit(bool wait_completion, bool begin_next, + vk::Semaphore wait_semaphore, vk::Semaphore signal_semaphore) { // End command buffers + const CommandSlot& command = commands[current_command]; command.render_command_buffer.end(); if (command.use_upload_buffer) { command.upload_command_buffer.end(); @@ -126,9 +130,14 @@ void CommandScheduler::Submit(bool wait_completion, vk::Semaphore wait_semaphore const u32 signal_semaphore_count = signal_semaphore ? 1u : 0u; const u32 wait_semaphore_count = wait_semaphore ? 1u : 0u; - const u32 command_buffer_count = command.use_upload_buffer ? 2u : 1u; - const std::array command_buffers = { command.render_command_buffer, - command.upload_command_buffer }; + u32 command_buffer_count = 0; + std::array command_buffers; + + if (command.use_upload_buffer) { + command_buffers[command_buffer_count++] = command.upload_command_buffer; + } + + command_buffers[command_buffer_count++] = command.render_command_buffer; // Prepeare submit info const vk::SubmitInfo submit_info = { @@ -151,7 +160,9 @@ void CommandScheduler::Submit(bool wait_completion, vk::Semaphore wait_semaphore } // Switch to next cmdbuffer. - SwitchSlot(); + if (begin_next) { + SwitchSlot(); + } } void CommandScheduler::Schedule(std::function&& func) { diff --git a/src/video_core/renderer_vulkan/vk_task_scheduler.h b/src/video_core/renderer_vulkan/vk_task_scheduler.h index 1b651d712..be7aa1282 100644 --- a/src/video_core/renderer_vulkan/vk_task_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_task_scheduler.h @@ -37,8 +37,8 @@ public: void Schedule(std::function&& func); // Submits the current command to the graphics queue - void Submit(bool wait_completion = false, vk::Semaphore wait = VK_NULL_HANDLE, - vk::Semaphore signal = VK_NULL_HANDLE); + void Submit(bool wait_completion = false, bool begin_next = true, + vk::Semaphore wait = VK_NULL_HANDLE, vk::Semaphore signal = VK_NULL_HANDLE); // Returns the command buffer used for early upload operations. // This is useful for vertex/uniform buffer uploads that happen once per frame