video_core: Fix destruction sequence
Rejoice! Vulkan no longer leaks memory
This commit is contained in:
@@ -19,7 +19,7 @@ endif()
|
|||||||
# Install a package using the command line interface
|
# Install a package using the command line interface
|
||||||
function(vcpkg_add_package PKG_NAME)
|
function(vcpkg_add_package PKG_NAME)
|
||||||
# Run the executable to install dependencies
|
# 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}")
|
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)
|
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")
|
if (NOT VCPKG_INSTALL_OK EQUAL "0")
|
||||||
|
@@ -32,6 +32,9 @@ public:
|
|||||||
// Triggers a swapchain buffer swap
|
// Triggers a swapchain buffer swap
|
||||||
virtual void EndPresent() = 0;
|
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
|
// Returns the framebuffer created from the swapchain images
|
||||||
virtual FramebufferHandle GetWindowFramebuffer() = 0;
|
virtual FramebufferHandle GetWindowFramebuffer() = 0;
|
||||||
|
|
||||||
|
@@ -56,7 +56,8 @@ layout (std140, push_constant) uniform PresentUniformData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void main() {
|
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
|
// Create vertex buffer for the screen rectangle
|
||||||
const BufferInfo vertex_info = {
|
const BufferInfo vertex_info = {
|
||||||
.capacity = sizeof(ScreenRectVertex) * 10,
|
.capacity = sizeof(ScreenRectVertex) * 16,
|
||||||
.usage = BufferUsage::Vertex
|
.usage = BufferUsage::Vertex
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -207,6 +208,11 @@ DisplayRenderer::DisplayRenderer(Frontend::EmuWindow& window) : render_window(wi
|
|||||||
present_pipelines[0]->BindSampler(1, 0, screen_sampler);
|
present_pipelines[0]->BindSampler(1, 0, screen_sampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisplayRenderer::~DisplayRenderer() {
|
||||||
|
// Must flush the backend before destroying any pipelines!
|
||||||
|
backend->Flush();
|
||||||
|
}
|
||||||
|
|
||||||
void DisplayRenderer::PrepareRendertarget() {
|
void DisplayRenderer::PrepareRendertarget() {
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
int fb_id = i == 2 ? 1 : 0;
|
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 u32 size = sizeof(ScreenRectVertex) * vertices.size();
|
||||||
const u64 mapped_offset = vertex_buffer->GetCurrentOffset();
|
|
||||||
auto vertex_data = vertex_buffer->Map(size);
|
auto vertex_data = vertex_buffer->Map(size);
|
||||||
|
const u64 mapped_offset = vertex_buffer->GetCurrentOffset();
|
||||||
|
|
||||||
// Copy vertex data
|
// Copy vertex data
|
||||||
std::memcpy(vertex_data.data(), vertices.data(), size);
|
std::memcpy(vertex_data.data(), vertices.data(), size);
|
||||||
|
@@ -67,7 +67,7 @@ constexpr u32 PRESENT_PIPELINES = 3;
|
|||||||
class DisplayRenderer {
|
class DisplayRenderer {
|
||||||
public:
|
public:
|
||||||
DisplayRenderer(Frontend::EmuWindow& window);
|
DisplayRenderer(Frontend::EmuWindow& window);
|
||||||
~DisplayRenderer() = default;
|
~DisplayRenderer();
|
||||||
|
|
||||||
void SwapBuffers();
|
void SwapBuffers();
|
||||||
void TryPresent(int timeout_ms) {}
|
void TryPresent(int timeout_ms) {}
|
||||||
@@ -121,8 +121,8 @@ private:
|
|||||||
void LoadColorToActiveTexture(u8 color_r, u8 color_g, u8 color_b, const ScreenInfo& screen);
|
void LoadColorToActiveTexture(u8 color_r, u8 color_g, u8 color_b, const ScreenInfo& screen);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<VideoCore::Rasterizer> rasterizer;
|
|
||||||
std::unique_ptr<BackendBase> backend;
|
std::unique_ptr<BackendBase> backend;
|
||||||
|
std::unique_ptr<VideoCore::Rasterizer> rasterizer;
|
||||||
Frontend::EmuWindow& render_window;
|
Frontend::EmuWindow& render_window;
|
||||||
Common::Vec4f clear_color;
|
Common::Vec4f clear_color;
|
||||||
f32 m_current_fps = 0.0f;
|
f32 m_current_fps = 0.0f;
|
||||||
|
@@ -81,7 +81,11 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window),
|
|||||||
}
|
}
|
||||||
|
|
||||||
Backend::~Backend() {
|
Backend::~Backend() {
|
||||||
|
|
||||||
|
// Wait for all GPU operations to finish before continuing
|
||||||
vk::Device device = instance.GetDevice();
|
vk::Device device = instance.GetDevice();
|
||||||
|
device.waitIdle();
|
||||||
|
|
||||||
device.destroyPipelineCache(pipeline_cache);
|
device.destroyPipelineCache(pipeline_cache);
|
||||||
|
|
||||||
for (u32 pool = 0; pool < SCHEDULER_COMMAND_COUNT; pool++) {
|
for (u32 pool = 0; pool < SCHEDULER_COMMAND_COUNT; pool++) {
|
||||||
@@ -100,10 +104,14 @@ bool Backend::BeginPresent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Backend::EndPresent() {
|
void Backend::EndPresent() {
|
||||||
scheduler.Submit(false, swapchain.GetAvailableSemaphore(), swapchain.GetPresentSemaphore());
|
scheduler.Submit(false, true, swapchain.GetAvailableSemaphore(), swapchain.GetPresentSemaphore());
|
||||||
swapchain.Present();
|
swapchain.Present();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Backend::Flush() {
|
||||||
|
scheduler.Submit(true);
|
||||||
|
}
|
||||||
|
|
||||||
FramebufferHandle Backend::GetWindowFramebuffer() {
|
FramebufferHandle Backend::GetWindowFramebuffer() {
|
||||||
auto framebuffer = swapchain.GetCurrentFramebuffer();
|
auto framebuffer = swapchain.GetCurrentFramebuffer();
|
||||||
auto extent = swapchain.GetExtent();
|
auto extent = swapchain.GetExtent();
|
||||||
|
@@ -21,11 +21,10 @@ public:
|
|||||||
|
|
||||||
bool BeginPresent() override;
|
bool BeginPresent() override;
|
||||||
void EndPresent() override;
|
void EndPresent() override;
|
||||||
|
void Flush() override;
|
||||||
|
|
||||||
FramebufferHandle GetWindowFramebuffer() override;
|
FramebufferHandle GetWindowFramebuffer() override;
|
||||||
|
|
||||||
u64 QueryDriver(Query query) override { return 0; }
|
u64 QueryDriver(Query query) override { return 0; }
|
||||||
|
|
||||||
u64 PipelineInfoHash(const PipelineInfo& info) override;
|
u64 PipelineInfoHash(const PipelineInfo& info) override;
|
||||||
|
|
||||||
BufferHandle CreateBuffer(BufferInfo info) override;
|
BufferHandle CreateBuffer(BufferInfo info) override;
|
||||||
@@ -40,10 +39,8 @@ public:
|
|||||||
|
|
||||||
void Draw(PipelineHandle pipeline, FramebufferHandle draw_framebuffer,
|
void Draw(PipelineHandle pipeline, FramebufferHandle draw_framebuffer,
|
||||||
u32 base_vertex, u32 num_vertices) override;
|
u32 base_vertex, u32 num_vertices) override;
|
||||||
|
|
||||||
void DrawIndexed(PipelineHandle pipeline, FramebufferHandle draw_framebuffer,
|
void DrawIndexed(PipelineHandle pipeline, FramebufferHandle draw_framebuffer,
|
||||||
u32 base_index, u32 num_indices, u32 base_vertex) override;
|
u32 base_index, u32 num_indices, u32 base_vertex) override;
|
||||||
|
|
||||||
void DispatchCompute(PipelineHandle pipeline, Common::Vec3<u32> groupsize,
|
void DispatchCompute(PipelineHandle pipeline, Common::Vec3<u32> groupsize,
|
||||||
Common::Vec3<u32> groups) override {}
|
Common::Vec3<u32> groups) override {}
|
||||||
|
|
||||||
|
@@ -148,7 +148,7 @@ void Buffer::Commit(u32 size) {
|
|||||||
if (info.usage == BufferUsage::Staging && size > 0) {
|
if (info.usage == BufferUsage::Staging && size > 0) {
|
||||||
vmaFlushAllocation(allocator, allocation, buffer_offset, size);
|
vmaFlushAllocation(allocator, allocation, buffer_offset, size);
|
||||||
} else {
|
} else {
|
||||||
vk::CommandBuffer command_buffer = scheduler.GetUploadCommandBuffer();
|
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||||
Buffer& staging = scheduler.GetCommandUploadBuffer();
|
Buffer& staging = scheduler.GetCommandUploadBuffer();
|
||||||
|
|
||||||
const vk::BufferCopy copy_region = {
|
const vk::BufferCopy copy_region = {
|
||||||
@@ -158,6 +158,7 @@ void Buffer::Commit(u32 size) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Copy staging buffer to device local buffer
|
// Copy staging buffer to device local buffer
|
||||||
|
staging.Commit(size);
|
||||||
command_buffer.copyBuffer(staging.GetHandle(), buffer, copy_region);
|
command_buffer.copyBuffer(staging.GetHandle(), buffer, copy_region);
|
||||||
|
|
||||||
vk::AccessFlags access_mask;
|
vk::AccessFlags access_mask;
|
||||||
@@ -184,6 +185,8 @@ void Buffer::Commit(u32 size) {
|
|||||||
const vk::BufferMemoryBarrier buffer_barrier = {
|
const vk::BufferMemoryBarrier buffer_barrier = {
|
||||||
.srcAccessMask = vk::AccessFlagBits::eTransferWrite,
|
.srcAccessMask = vk::AccessFlagBits::eTransferWrite,
|
||||||
.dstAccessMask = access_mask,
|
.dstAccessMask = access_mask,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.buffer = buffer,
|
.buffer = buffer,
|
||||||
.offset = buffer_offset,
|
.offset = buffer_offset,
|
||||||
.size = size
|
.size = size
|
||||||
|
@@ -60,7 +60,9 @@ Instance::Instance(Frontend::EmuWindow& window) {
|
|||||||
|
|
||||||
Instance::~Instance() {
|
Instance::~Instance() {
|
||||||
device.waitIdle();
|
device.waitIdle();
|
||||||
|
vmaDestroyAllocator(allocator);
|
||||||
device.destroy();
|
device.destroy();
|
||||||
|
instance.destroySurfaceKHR(surface);
|
||||||
instance.destroy();
|
instance.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -183,7 +183,7 @@ PipelineOwner::~PipelineOwner() {
|
|||||||
u32 i = 0;
|
u32 i = 0;
|
||||||
while (set_layouts[i] && update_templates[i]) {
|
while (set_layouts[i] && update_templates[i]) {
|
||||||
device.destroyDescriptorSetLayout(set_layouts[i]);
|
device.destroyDescriptorSetLayout(set_layouts[i]);
|
||||||
device.destroyDescriptorUpdateTemplate(update_templates[i]);
|
device.destroyDescriptorUpdateTemplate(update_templates[i++]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -36,7 +36,7 @@ private:
|
|||||||
// Special renderpass used for rendering to the swapchain
|
// Special renderpass used for rendering to the swapchain
|
||||||
vk::RenderPass present_renderpass;
|
vk::RenderPass present_renderpass;
|
||||||
// [color_format][depth_format][is_clear_pass]
|
// [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
|
} // namespace VideoCore::Vulkan
|
||||||
|
@@ -64,6 +64,9 @@ CommandScheduler::~CommandScheduler() {
|
|||||||
vk::Device device = instance.GetDevice();
|
vk::Device device = instance.GetDevice();
|
||||||
VmaAllocator allocator = instance.GetAllocator();
|
VmaAllocator allocator = instance.GetAllocator();
|
||||||
|
|
||||||
|
// Submit any remaining work
|
||||||
|
Submit(true, false);
|
||||||
|
|
||||||
for (auto& command : commands) {
|
for (auto& command : commands) {
|
||||||
device.destroyFence(command.fence);
|
device.destroyFence(command.fence);
|
||||||
|
|
||||||
@@ -110,10 +113,11 @@ void CommandScheduler::SetSwitchCallback(std::function<void(u32)> callback) {
|
|||||||
switch_callback = callback;
|
switch_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandScheduler::Submit(bool wait_completion, vk::Semaphore wait_semaphore, vk::Semaphore signal_semaphore) {
|
void CommandScheduler::Submit(bool wait_completion, bool begin_next,
|
||||||
const CommandSlot& command = commands[current_command];
|
vk::Semaphore wait_semaphore, vk::Semaphore signal_semaphore) {
|
||||||
|
|
||||||
// End command buffers
|
// End command buffers
|
||||||
|
const CommandSlot& command = commands[current_command];
|
||||||
command.render_command_buffer.end();
|
command.render_command_buffer.end();
|
||||||
if (command.use_upload_buffer) {
|
if (command.use_upload_buffer) {
|
||||||
command.upload_command_buffer.end();
|
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 signal_semaphore_count = signal_semaphore ? 1u : 0u;
|
||||||
const u32 wait_semaphore_count = wait_semaphore ? 1u : 0u;
|
const u32 wait_semaphore_count = wait_semaphore ? 1u : 0u;
|
||||||
const u32 command_buffer_count = command.use_upload_buffer ? 2u : 1u;
|
u32 command_buffer_count = 0;
|
||||||
const std::array command_buffers = { command.render_command_buffer,
|
std::array<vk::CommandBuffer, 2> command_buffers;
|
||||||
command.upload_command_buffer };
|
|
||||||
|
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
|
// Prepeare submit info
|
||||||
const vk::SubmitInfo 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.
|
// Switch to next cmdbuffer.
|
||||||
|
if (begin_next) {
|
||||||
SwitchSlot();
|
SwitchSlot();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandScheduler::Schedule(std::function<void(vk::Device, VmaAllocator)>&& func) {
|
void CommandScheduler::Schedule(std::function<void(vk::Device, VmaAllocator)>&& func) {
|
||||||
|
@@ -37,8 +37,8 @@ public:
|
|||||||
void Schedule(std::function<void(vk::Device, VmaAllocator)>&& func);
|
void Schedule(std::function<void(vk::Device, VmaAllocator)>&& func);
|
||||||
|
|
||||||
// Submits the current command to the graphics queue
|
// Submits the current command to the graphics queue
|
||||||
void Submit(bool wait_completion = false, vk::Semaphore wait = VK_NULL_HANDLE,
|
void Submit(bool wait_completion = false, bool begin_next = true,
|
||||||
vk::Semaphore signal = VK_NULL_HANDLE);
|
vk::Semaphore wait = VK_NULL_HANDLE, vk::Semaphore signal = VK_NULL_HANDLE);
|
||||||
|
|
||||||
// Returns the command buffer used for early upload operations.
|
// Returns the command buffer used for early upload operations.
|
||||||
// This is useful for vertex/uniform buffer uploads that happen once per frame
|
// This is useful for vertex/uniform buffer uploads that happen once per frame
|
||||||
|
Reference in New Issue
Block a user