From a2ca75bff44c65127a3649c78c2e885e046d95a4 Mon Sep 17 00:00:00 2001 From: emufan4568 Date: Fri, 12 Aug 2022 13:38:36 +0300 Subject: [PATCH] video_core: Fix remaining validation errors. Introduce PoolManager --- src/video_core/CMakeLists.txt | 1 + src/video_core/common/backend.h | 2 + src/video_core/common/buffer.h | 38 ++++++--- src/video_core/common/framebuffer.h | 18 +++- src/video_core/common/pipeline.h | 14 +++- src/video_core/common/pipeline_cache.cpp | 4 + src/video_core/common/pool_manager.h | 35 ++++++++ src/video_core/common/rasterizer.cpp | 47 ++++++++--- src/video_core/common/rasterizer.h | 2 +- src/video_core/common/rasterizer_cache.cpp | 3 +- src/video_core/common/shader.h | 22 +++-- src/video_core/common/texture.h | 82 ++++++++++++------- src/video_core/renderer_vulkan/vk_backend.cpp | 74 +++++++++-------- src/video_core/renderer_vulkan/vk_backend.h | 2 + src/video_core/renderer_vulkan/vk_buffer.cpp | 58 ++++++++----- src/video_core/renderer_vulkan/vk_buffer.h | 16 ++-- .../renderer_vulkan/vk_framebuffer.cpp | 32 ++++++-- .../renderer_vulkan/vk_framebuffer.h | 29 ++++--- .../renderer_vulkan/vk_pipeline.cpp | 11 ++- src/video_core/renderer_vulkan/vk_pipeline.h | 14 +++- .../renderer_vulkan/vk_renderpass_cache.cpp | 14 ++-- src/video_core/renderer_vulkan/vk_shader.cpp | 11 ++- src/video_core/renderer_vulkan/vk_shader.h | 9 +- .../renderer_vulkan/vk_swapchain.cpp | 26 +++--- src/video_core/renderer_vulkan/vk_swapchain.h | 46 ++++++----- .../renderer_vulkan/vk_task_scheduler.cpp | 22 +++-- .../renderer_vulkan/vk_task_scheduler.h | 44 ++++++---- src/video_core/renderer_vulkan/vk_texture.cpp | 45 +++++----- src/video_core/renderer_vulkan/vk_texture.h | 43 ++++++---- 29 files changed, 506 insertions(+), 258 deletions(-) create mode 100644 src/video_core/common/pool_manager.h diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 23c758a69..2d0deaa75 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -33,6 +33,7 @@ add_library(video_core STATIC common/pica_uniforms.h common/pipeline_cache.cpp common/pipeline_cache.h + common/pool_manager.h common/rasterizer.cpp common/rasterizer.h common/rasterizer_cache.cpp diff --git a/src/video_core/common/backend.h b/src/video_core/common/backend.h index 86b26665f..ad2dff664 100644 --- a/src/video_core/common/backend.h +++ b/src/video_core/common/backend.h @@ -5,6 +5,7 @@ #pragma once #include "common/vector_math.h" +#include "video_core/common/pool_manager.h" #include "video_core/common/pipeline.h" #include "video_core/common/framebuffer.h" @@ -77,6 +78,7 @@ public: Common::Vec3 groups) = 0; protected: Frontend::EmuWindow& window; + PoolManager pool_manager; }; } // namespace VideoCore diff --git a/src/video_core/common/buffer.h b/src/video_core/common/buffer.h index 9cc9f7ec0..f14203214 100644 --- a/src/video_core/common/buffer.h +++ b/src/video_core/common/buffer.h @@ -12,20 +12,20 @@ namespace VideoCore { enum class BufferUsage : u8 { - Vertex = 0, - Index = 1, - Uniform = 2, - Texel = 3, - Staging = 4, - Undefined = 255 + Undefined = 0, + Vertex = 1, + Index = 2, + Uniform = 3, + Texel = 4, + Staging = 5 }; enum class ViewFormat : u8 { - R32Float = 0, - R32G32Float = 1, - R32G32B32Float = 2, - R32G32B32A32Float = 3, - Undefined = 255 + Undefined = 0, + R32Float = 1, + R32G32Float = 2, + R32G32B32Float = 3, + R32G32B32A32Float = 4, }; constexpr u32 MAX_BUFFER_VIEWS = 3; @@ -33,7 +33,7 @@ constexpr u32 MAX_BUFFER_VIEWS = 3; struct BufferInfo { u32 capacity = 0; BufferUsage usage = BufferUsage::Undefined; - std::array views{ViewFormat::Undefined}; + std::array views{}; auto operator<=>(const BufferInfo& info) const = default; @@ -45,12 +45,17 @@ struct BufferInfo { static_assert(sizeof(BufferInfo) == 8, "BufferInfo not packed!"); static_assert(std::is_standard_layout_v, "BufferInfo is not a standard layout!"); -class BufferBase : public IntrusivePtrEnabled { +struct BufferDeleter; + +class BufferBase : public IntrusivePtrEnabled { public: BufferBase() = default; BufferBase(const BufferInfo& info) : info(info) {} virtual ~BufferBase() = default; + // This method is called by BufferDeleter. Forward to the derived pool! + virtual void Free() = 0; + // Disable copy constructor BufferBase(const BufferBase&) = delete; BufferBase& operator=(const BufferBase&) = delete; @@ -95,6 +100,13 @@ protected: bool invalid = false; }; +// Foward pointer to its parent pool +struct BufferDeleter { + void operator()(BufferBase* buffer) { + buffer->Free(); + } +}; + using BufferHandle = IntrusivePtr; } // namespace VideoCore diff --git a/src/video_core/common/framebuffer.h b/src/video_core/common/framebuffer.h index cc5d18d29..ceaa51b5b 100644 --- a/src/video_core/common/framebuffer.h +++ b/src/video_core/common/framebuffer.h @@ -22,9 +22,7 @@ enum class LoadOp : u8 { Clear = 1 }; -/** - * Information about a framebuffer - */ +// Information about a framebuffer struct FramebufferInfo { TextureHandle color; TextureHandle depth_stencil; @@ -39,12 +37,17 @@ struct FramebufferInfo { } }; +struct FramebufferDeleter; + // A framebuffer is a collection of render targets and their configuration -class FramebufferBase : public IntrusivePtrEnabled { +class FramebufferBase : public IntrusivePtrEnabled { public: FramebufferBase(const FramebufferInfo& info) : info(info) {} virtual ~FramebufferBase() = default; + // This method is called by FramebufferDeleter. Forward to the derived pool! + virtual void Free() = 0; + // Disable copy constructor FramebufferBase(const FramebufferBase&) = delete; FramebufferBase& operator=(const FramebufferBase&) = delete; @@ -100,6 +103,13 @@ protected: FramebufferInfo info; }; +// Foward pointer to its parent pool +struct FramebufferDeleter { + void operator()(FramebufferBase* framebuffer) { + framebuffer->Free(); + } +}; + using FramebufferHandle = IntrusivePtr; } // namespace VideoCore diff --git a/src/video_core/common/pipeline.h b/src/video_core/common/pipeline.h index 1fbb54f2b..8b536424f 100644 --- a/src/video_core/common/pipeline.h +++ b/src/video_core/common/pipeline.h @@ -140,12 +140,17 @@ struct PipelineInfo { constexpr s32 WHOLE_SIZE = -1; +struct PipelineDeleter; + // An opaque handle to a backend specific program pipeline -class PipelineBase : public IntrusivePtrEnabled { +class PipelineBase : public IntrusivePtrEnabled { public: PipelineBase(PipelineType type, PipelineInfo info) : type(type) {} virtual ~PipelineBase() = default; + // This method is called by PipelineDeleter. Forward to the derived pool! + virtual void Free() = 0; + // Disable copy constructor PipelineBase(const PipelineBase&) = delete; PipelineBase& operator=(const PipelineBase&) = delete; @@ -181,6 +186,13 @@ protected: PipelineType type = PipelineType::Graphics; }; +// Foward pointer to its parent pool +struct PipelineDeleter { + void operator()(PipelineBase* pipeline) { + pipeline->Free(); + } +}; + using PipelineHandle = IntrusivePtr; } // namespace VideoCore diff --git a/src/video_core/common/pipeline_cache.cpp b/src/video_core/common/pipeline_cache.cpp index 36a247927..185d42386 100644 --- a/src/video_core/common/pipeline_cache.cpp +++ b/src/video_core/common/pipeline_cache.cpp @@ -44,6 +44,10 @@ PipelineCache::PipelineCache(Frontend::EmuWindow& emu_window, std::unique_ptr(); + + trivial_vertex_shader = backend->CreateShader(ShaderStage::Vertex, "Trivial vertex shader", + generator->GenerateTrivialVertexShader()); + trivial_vertex_shader->Compile(ShaderOptimization::Debug); } PipelineHandle PipelineCache::GetPipeline(PipelineInfo& info) { diff --git a/src/video_core/common/pool_manager.h b/src/video_core/common/pool_manager.h new file mode 100644 index 000000000..2140874f7 --- /dev/null +++ b/src/video_core/common/pool_manager.h @@ -0,0 +1,35 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/object_pool.h" +#include "common/intrusive_ptr.h" + +namespace VideoCore { + +// Manages (de)allocation of video backend resources +class PoolManager { +public: + template + IntrusivePtr Allocate(P&&... p) { + auto& pool = GetPoolForType(); + return IntrusivePtr{pool.Allocate(std::forward

(p)...)}; + } + + template + void Free(T* ptr) { + auto& pool = GetPoolForType(); + pool.Free(ptr); + } + +private: + template + ObjectPool& GetPoolForType() { + static ObjectPool resource_pool; + return resource_pool; + } +}; + +} // namespace VideoCore diff --git a/src/video_core/common/rasterizer.cpp b/src/video_core/common/rasterizer.cpp index e0627f881..afada6075 100644 --- a/src/video_core/common/rasterizer.cpp +++ b/src/video_core/common/rasterizer.cpp @@ -186,6 +186,17 @@ Rasterizer::Rasterizer(Frontend::EmuWindow& emu_window, std::unique_ptrCreateBuffer(TEXEL_BUFFER_INFO); texel_buffer_lut_lf = backend->CreateBuffer(TEXEL_BUFFER_LF_INFO); + const SamplerInfo cube_sampler_info = { + .mag_filter = Pica::TextureFilter::Linear, + .min_filter = Pica::TextureFilter::Linear, + .mip_filter = Pica::TextureFilter::Linear, + .wrap_s = Pica::WrapMode::ClampToEdge, + .wrap_t = Pica::WrapMode::ClampToEdge + }; + + // TODO: Texture cubes + texture_cube_sampler = backend->CreateSampler(cube_sampler_info); + // TODO: Have the backend say this uniform_buffer_alignment = 64; uniform_size_aligned_vs = Common::AlignUp(sizeof(VSUniformData), uniform_buffer_alignment); @@ -794,8 +805,9 @@ bool Rasterizer::Draw(bool accelerate, bool is_indexed) { if (auto iter = sampler_cache.find(key); iter != sampler_cache.end()) { raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, iter->second); } else { - auto result = sampler_cache.emplace(key, backend->CreateSampler(key)); - raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, result.first->second); + SamplerHandle texture_sampler = backend->CreateSampler(key); + auto result = sampler_cache.emplace(key, texture_sampler); + raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, texture_sampler); } Surface surface = res_cache.GetTextureSurface(texture); @@ -815,9 +827,16 @@ bool Rasterizer::Draw(bool accelerate, bool is_indexed) { } else { //state.texture_units[texture_index].texture_2d = 0; raster_pipeline->BindTexture(TEXTURE_GROUP, texture_index, clear_texture); + raster_pipeline->BindSampler(SAMPLER_GROUP, texture_index, texture_cube_sampler); } } + // TODO: Implement texture cubes + raster_pipeline->BindTexture(TEXTURE_GROUP, 3, clear_texture); + + // TODO: Implement texture cubes + raster_pipeline->BindSampler(SAMPLER_GROUP, 3, texture_cube_sampler); + // Sync the LUTs within the texture buffer SyncAndUploadLUTs(); SyncAndUploadLUTsLF(); @@ -1921,7 +1940,9 @@ void Rasterizer::SyncAndUploadLUTsLF() { uniform_block_data.fog_lut_dirty = false; } - texel_buffer_lut_lf->Commit(bytes_used); + if (bytes_used > 0) { + texel_buffer_lut_lf->Commit(bytes_used); + } } void Rasterizer::SyncAndUploadLUTs() { @@ -2022,7 +2043,9 @@ void Rasterizer::SyncAndUploadLUTs() { uniform_block_data.proctex_diff_lut_dirty = false; } - texel_buffer_lut->Commit(bytes_used); + if (bytes_used > 0) { + texel_buffer_lut->Commit(bytes_used); + } } void Rasterizer::UploadUniforms(PipelineHandle pipeline, bool accelerate_draw) { @@ -2040,28 +2063,30 @@ void Rasterizer::UploadUniforms(PipelineHandle pipeline, bool accelerate_draw) { const bool invalidate = uniform_buffer->IsInvalid(); const u32 offset = uniform_buffer->GetCurrentOffset(); + // Re-bind uniform buffer with the new range + pipeline->BindBuffer(UTILITY_GROUP, 0, uniform_buffer, offset + used_bytes, sizeof(VSUniformData)); + if (sync_vs) { VSUniformData vs_uniforms; vs_uniforms.uniforms.SetFromRegs(Pica::g_state.regs.vs, Pica::g_state.vs); std::memcpy(uniforms.data() + used_bytes, &vs_uniforms, sizeof(vs_uniforms)); - // Re-bind uniform buffer with the new range - pipeline->BindBuffer(UTILITY_GROUP, 0, uniform_buffer, offset + used_bytes, sizeof(VSUniformData)); - used_bytes += uniform_size_aligned_vs; } + // Re-bind uniform buffer with the new range + pipeline->BindBuffer(UTILITY_GROUP, 1, uniform_buffer, offset + used_bytes, sizeof(UniformData)); + if (sync_fs || invalidate) { std::memcpy(uniforms.data() + used_bytes, &uniform_block_data.data, sizeof(UniformData)); - // Re-bind uniform buffer with the new range - pipeline->BindBuffer(UTILITY_GROUP, 1, uniform_buffer, offset + used_bytes, sizeof(UniformData)); - uniform_block_data.dirty = false; used_bytes += uniform_size_aligned_fs; } - uniform_buffer->Commit(used_bytes); + if (used_bytes > 0) { + uniform_buffer->Commit(used_bytes); + } } } // namespace VideoCore diff --git a/src/video_core/common/rasterizer.h b/src/video_core/common/rasterizer.h index acb8700c4..10480d8c4 100644 --- a/src/video_core/common/rasterizer.h +++ b/src/video_core/common/rasterizer.h @@ -236,7 +236,7 @@ private: std::array proctex_diff_lut_data{}; // Texture unit sampler cache - SamplerInfo texture_cube_sampler; + SamplerHandle texture_cube_sampler; std::array texture_samplers; std::unordered_map sampler_cache; diff --git a/src/video_core/common/rasterizer_cache.cpp b/src/video_core/common/rasterizer_cache.cpp index 09d92e51b..5f280954f 100644 --- a/src/video_core/common/rasterizer_cache.cpp +++ b/src/video_core/common/rasterizer_cache.cpp @@ -51,8 +51,7 @@ TextureFormat GetTextureFormat(PixelFormat pixel_format) { return depth_texture_formats[tuple_idx]; } - LOG_ERROR(Render_Vulkan, "Unknown pixel format {}!", pixel_format); - return TextureFormat::Undefined; + return TextureFormat::RGBA8; } template diff --git a/src/video_core/common/shader.h b/src/video_core/common/shader.h index 7742f96fd..6c64b74f2 100644 --- a/src/video_core/common/shader.h +++ b/src/video_core/common/shader.h @@ -26,22 +26,27 @@ enum class ShaderOptimization : u32 { Debug = 1 }; -/// Compiles shader source to backend representation -class ShaderBase : public IntrusivePtrEnabled { +struct ShaderDeleter; + +// Compiles shader source to backend representation +class ShaderBase : public IntrusivePtrEnabled { public: ShaderBase(ShaderStage stage, std::string_view name, std::string&& source) : name(name), stage(stage), source(source) {} virtual ~ShaderBase() = default; - /// Compiles the shader source code + // This method is called by ShaderDeleter. Forward to the derived pool! + virtual void Free() = 0; + + // Compiles the shader source code virtual bool Compile(ShaderOptimization level) = 0; - /// Returns the name given the shader module + // Returns the name given the shader module std::string_view GetName() const { return name; } - /// Returns the pipeline stage the shader is assigned to + // Returns the pipeline stage the shader is assigned to ShaderStage GetStage() const { return stage; } @@ -52,6 +57,13 @@ protected: std::string source = ""; }; +// Foward pointer to its parent pool +struct ShaderDeleter { + void operator()(ShaderBase* shader) { + shader->Free(); + } +}; + using ShaderHandle = IntrusivePtr; } // namespace VideoCore diff --git a/src/video_core/common/texture.h b/src/video_core/common/texture.h index cb332c047..5c2752533 100644 --- a/src/video_core/common/texture.h +++ b/src/video_core/common/texture.h @@ -16,34 +16,34 @@ constexpr u32 MAX_COLOR_FORMATS = 5; constexpr u32 MAX_DEPTH_FORMATS = 3; enum class TextureFormat : u8 { - RGBA8 = 0, - RGB8 = 1, - RGB5A1 = 2, - RGB565 = 3, - RGBA4 = 4, - D16 = 5, - D24 = 6, - D24S8 = 7, - PresentColor = 8, // Backend specific swapchain format - Undefined = 255 + Undefined = 0, + RGBA8 = 1, + RGB8 = 2, + RGB5A1 = 3, + RGB565 = 4, + RGBA4 = 5, + D16 = 6, + D24 = 7, + D24S8 = 8, + PresentColor = 9 // Backend specific swapchain format }; enum class TextureType : u8 { - Texture1D = 0, - Texture2D = 1, - Texture3D = 2, - Undefined = 255 + Undefined = 0, + Texture1D = 1, + Texture2D = 2, + Texture3D = 3 }; enum class TextureViewType : u8 { - View1D = 0, - View2D = 1, - View3D = 2, - ViewCube = 3, - View1DArray = 4, - View2DArray = 5, - ViewCubeArray = 6, - Undefined = 255 + Undefined = 0, + View1D = 1, + View2D = 2, + View3D = 3, + ViewCube = 4, + View1DArray = 5, + View2DArray = 6, + ViewCubeArray = 7, }; /** @@ -64,9 +64,7 @@ struct Rect2D { u32 height = 0; }; -/** - * Information about a texture packed to 8 bytes - */ +// Information about a texture packed to 8 bytes struct TextureInfo { u16 width = 0; u16 height = 0; @@ -92,12 +90,17 @@ static_assert(std::is_standard_layout_v, "TextureInfo is not a stan class TextureBase; using TextureHandle = IntrusivePtr; -class TextureBase : public IntrusivePtrEnabled { +struct TextureDeleter; + +class TextureBase : public IntrusivePtrEnabled { public: TextureBase() = default; TextureBase(const TextureInfo& info) : info(info) {} virtual ~TextureBase() = default; + // This method is called by TextureDeleter. Forward to the derived pool! + virtual void Free() = 0; + // Disable copy constructor TextureBase(const TextureBase&) = delete; TextureBase& operator=(const TextureBase&) = delete; @@ -111,9 +114,8 @@ public: u32 level = 0) {}; // Copies the rectangle area specified to the destionation texture - virtual void BlitTo(TextureHandle dest, Rect2D source_rect, Rect2D dest_rect, - u32 src_level = 0, u32 dest_level = 0, - u32 src_layer = 0, u32 dest_layer = 0) {}; + virtual void BlitTo(TextureHandle dest, Common::Rectangle source_rect, Common::Rectangle dest_rect, + u32 src_level = 0, u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) {}; // Copies texture data from the source texture virtual void CopyFrom(TextureHandle source) {}; @@ -155,6 +157,14 @@ protected: TextureInfo info; }; +// Foward pointer to its parent pool +struct TextureDeleter { + void operator()(TextureBase* texture) { + texture->Free(); + } +}; + +// Information about a sampler struct SamplerInfo { Pica::TextureFilter mag_filter; Pica::TextureFilter min_filter; @@ -173,11 +183,16 @@ struct SamplerInfo { } }; -class SamplerBase : public IntrusivePtrEnabled { +struct SamplerDeleter; + +class SamplerBase : public IntrusivePtrEnabled { public: SamplerBase(SamplerInfo info) : info(info) {} virtual ~SamplerBase() = default; + // This method is called by SamplerDeleter. Forward to the derived pool! + virtual void Free() = 0; + // Disable copy constructor SamplerBase(const SamplerBase&) = delete; SamplerBase& operator=(const SamplerBase&) = delete; @@ -186,6 +201,13 @@ protected: SamplerInfo info{}; }; +// Foward pointer to its parent pool +struct SamplerDeleter { + void operator()(SamplerBase* sampler) { + sampler->Free(); + } +}; + using SamplerHandle = IntrusivePtr; } // namespace VideoCore diff --git a/src/video_core/renderer_vulkan/vk_backend.cpp b/src/video_core/renderer_vulkan/vk_backend.cpp index 634bd7be9..ee8b7d731 100644 --- a/src/video_core/renderer_vulkan/vk_backend.cpp +++ b/src/video_core/renderer_vulkan/vk_backend.cpp @@ -3,9 +3,9 @@ // Refer to the license.txt file included. #define VULKAN_HPP_NO_CONSTRUCTORS +#include #include "core/core.h" #include "core/frontend/emu_window.h" -#include "common/object_pool.h" #include "video_core/renderer_vulkan/vk_backend.h" #include "video_core/renderer_vulkan/vk_buffer.h" #include "video_core/renderer_vulkan/vk_texture.h" @@ -43,8 +43,8 @@ constexpr vk::IndexType ToVkIndexType(AttribType type) { } Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window), - instance(window), scheduler(instance), renderpass_cache(instance), - swapchain(instance, scheduler, renderpass_cache, this, instance.GetSurface()) { + instance(window), scheduler(instance, pool_manager), renderpass_cache(instance), + swapchain(instance, scheduler, renderpass_cache, pool_manager, instance.GetSurface()) { // TODO: Properly report GPU hardware auto& telemetry_session = Core::System::GetInstance().TelemetrySession(); @@ -67,7 +67,7 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window), const vk::DescriptorPoolCreateInfo pool_info = { .maxSets = 2048, - .poolSizeCount = pool_sizes.size(), + .poolSizeCount = static_cast(pool_sizes.size()), .pPoolSizes = pool_sizes.data() }; @@ -76,9 +76,8 @@ Backend::Backend(Frontend::EmuWindow& window) : BackendBase(window), descriptor_pools[pool] = device.createDescriptorPool(pool_info); } - // Create swapchain with the initial window dimentions - const auto& layout = window.GetFramebufferLayout(); - swapchain.Create(layout.width, layout.height, false); + auto callback = std::bind(&Backend::OnCommandSwitch, this, std::placeholders::_1); + scheduler.SetSwitchCallback(callback); } Backend::~Backend() { @@ -90,13 +89,10 @@ Backend::~Backend() { } } -static bool first = true; - bool Backend::BeginPresent() { const auto& layout = window.GetFramebufferLayout(); - if (swapchain.NeedsRecreation() || first) { + if (swapchain.NeedsRecreation()) { swapchain.Create(layout.width, layout.height, false); - first = false; } swapchain.AcquireNextImage(); @@ -109,7 +105,11 @@ void Backend::EndPresent() { } FramebufferHandle Backend::GetWindowFramebuffer() { - return swapchain.GetCurrentFramebuffer(); + auto framebuffer = swapchain.GetCurrentFramebuffer(); + auto extent = swapchain.GetExtent(); + framebuffer->SetDrawRect({0, extent.height, extent.width, 0}); + + return framebuffer; } u64 Backend::PipelineInfoHash(const PipelineInfo& info) { @@ -130,64 +130,58 @@ u64 Backend::PipelineInfoHash(const PipelineInfo& info) { * associated with it that batch-allocates memory. */ BufferHandle Backend::CreateBuffer(BufferInfo info) { - static ObjectPool buffer_pool; - return BufferHandle{buffer_pool.Allocate(instance, scheduler, info)}; + return pool_manager.Allocate(instance, scheduler, pool_manager, info); } FramebufferHandle Backend::CreateFramebuffer(FramebufferInfo info) { - static ObjectPool framebuffer_pool; - // Get renderpass TextureFormat color = info.color.IsValid() ? info.color->GetFormat() : TextureFormat::Undefined; TextureFormat depth = info.depth_stencil.IsValid() ? info.depth_stencil->GetFormat() : TextureFormat::Undefined; vk::RenderPass load_renderpass = GetRenderPass(color, depth, false); vk::RenderPass clear_renderpass = GetRenderPass(color, depth, true); - return FramebufferHandle{framebuffer_pool.Allocate(instance, scheduler, info, load_renderpass, clear_renderpass)}; + return pool_manager.Allocate(instance, scheduler, pool_manager, info, + load_renderpass, clear_renderpass); } TextureHandle Backend::CreateTexture(TextureInfo info) { - static ObjectPool texture_pool; - return TextureHandle{texture_pool.Allocate(instance, scheduler, info)}; + return pool_manager.Allocate(instance, scheduler, pool_manager, info); } PipelineHandle Backend::CreatePipeline(PipelineType type, PipelineInfo info) { - static ObjectPool pipeline_pool; - // Get renderpass vk::RenderPass renderpass = GetRenderPass(info.color_attachment, info.depth_attachment); // Find an owner first const u64 layout_hash = Common::ComputeHash64(&info.layout, sizeof(PipelineLayoutInfo)); if (auto iter = pipeline_owners.find(layout_hash); iter != pipeline_owners.end()) { - return PipelineHandle{pipeline_pool.Allocate(instance, scheduler, *iter->second.get(), type, info, - renderpass, pipeline_cache)}; + return pool_manager.Allocate(instance, scheduler, pool_manager, *iter->second.get(), type, + info, renderpass, pipeline_cache); } // Create the layout auto result = pipeline_owners.emplace(layout_hash, std::make_unique(instance, info.layout)); - return PipelineHandle{pipeline_pool.Allocate(instance, scheduler, *result.first->second.get(), type, info, - renderpass, pipeline_cache)}; + return pool_manager.Allocate(instance, scheduler, pool_manager, *result.first->second.get(), type, + info, renderpass, pipeline_cache); } SamplerHandle Backend::CreateSampler(SamplerInfo info) { - static ObjectPool sampler_pool; - return SamplerHandle{sampler_pool.Allocate(instance, info)}; + return pool_manager.Allocate(instance, pool_manager, info); } ShaderHandle Backend::CreateShader(ShaderStage stage, std::string_view name, std::string source) { - static ObjectPool shader_pool; - return ShaderHandle{shader_pool.Allocate(instance, stage, name, std::move(source))}; + return pool_manager.Allocate(instance, pool_manager, stage, name, std::move(source)); } void Backend::BindVertexBuffer(BufferHandle buffer, std::span offsets) { const Buffer* vertex = static_cast(buffer.Get()); + const u32 buffer_count = static_cast(offsets.size()); std::array buffers; buffers.fill(vertex->GetHandle()); vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - command_buffer.bindVertexBuffers(0, offsets.size(), buffers.data(), offsets.data()); + command_buffer.bindVertexBuffers(0, buffer_count, buffers.data(), offsets.data()); } void Backend::BindIndexBuffer(BufferHandle buffer, AttribType index_type, u64 offset) { @@ -253,7 +247,7 @@ void Backend::BindDescriptorSets(PipelineHandle handle) { std::array bound_sets; const u32 set_count = pipeline_owner.GetDescriptorSetLayoutCount(); - for (int i = 0; i < set_count; i++) { + for (u32 i = 0; i < set_count; i++) { if (!pipeline_owner.descriptor_dirty[i]) { // Get the ready descriptor if it hasn't been modified bound_sets[i] = pipeline_owner.descriptor_bank[i]; @@ -284,7 +278,7 @@ void Backend::BindDescriptorSets(PipelineHandle handle) { } void Backend::BeginRenderpass(FramebufferHandle draw_framebuffer) { - const Framebuffer* framebuffer = static_cast(draw_framebuffer.Get()); + Framebuffer* framebuffer = static_cast(draw_framebuffer.Get()); u32 clear_value_count = 0; std::array clear_values{}; @@ -301,6 +295,9 @@ void Backend::BeginRenderpass(FramebufferHandle draw_framebuffer) { clear_values[clear_value_count++].depthStencil.stencil = framebuffer->clear_stencil_value; } + // Transition attachments to required layout + framebuffer->PrepareAttachments(); + // Use the clear renderpass if the framebuffer was configured so const vk::RenderPassBeginInfo renderpass_begin = { .renderPass = framebuffer->GetLoadOp() == LoadOp::Load ? @@ -316,4 +313,17 @@ void Backend::BeginRenderpass(FramebufferHandle draw_framebuffer) { command_buffer.beginRenderPass(renderpass_begin, vk::SubpassContents::eInline); } +void Backend::OnCommandSwitch(u32 new_slot) { + // Reset the descriptor pool assigned to the new command slot. This is called + // after Synchronize, so it's guaranteed that the descriptor sets are no longer + // in use. + vk::Device device = instance.GetDevice(); + device.resetDescriptorPool(descriptor_pools[new_slot]); + + // Mark all descriptor sets as dirty + for (auto& [key, owner] : pipeline_owners) { + owner->descriptor_dirty.fill(true); + } +} + } // namespace VideoCore::Vulkan diff --git a/src/video_core/renderer_vulkan/vk_backend.h b/src/video_core/renderer_vulkan/vk_backend.h index b1d415cc5..8aeb129c9 100644 --- a/src/video_core/renderer_vulkan/vk_backend.h +++ b/src/video_core/renderer_vulkan/vk_backend.h @@ -66,6 +66,8 @@ private: // Begins the renderpass for the provided framebuffer void BeginRenderpass(FramebufferHandle framebuffer); + void OnCommandSwitch(u32 new_slot); + private: Instance instance; CommandScheduler scheduler; diff --git a/src/video_core/renderer_vulkan/vk_buffer.cpp b/src/video_core/renderer_vulkan/vk_buffer.cpp index 645487fa4..a03f0e9c3 100644 --- a/src/video_core/renderer_vulkan/vk_buffer.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer.cpp @@ -6,6 +6,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "video_core/common/pool_manager.h" #include "video_core/renderer_vulkan/vk_buffer.h" #include "video_core/renderer_vulkan/vk_task_scheduler.h" #include "video_core/renderer_vulkan/vk_instance.h" @@ -13,35 +14,45 @@ namespace VideoCore::Vulkan { inline vk::BufferUsageFlags ToVkBufferUsage(BufferUsage usage) { - constexpr std::array vk_buffer_usages = { - vk::BufferUsageFlagBits::eVertexBuffer, - vk::BufferUsageFlagBits::eIndexBuffer, - vk::BufferUsageFlagBits::eUniformBuffer, - vk::BufferUsageFlagBits::eUniformTexelBuffer, - vk::BufferUsageFlagBits::eTransferSrc - }; - - return vk::BufferUsageFlagBits::eTransferDst | - vk_buffer_usages.at(static_cast(usage)); + switch (usage) { + case BufferUsage::Vertex: + return vk::BufferUsageFlagBits::eVertexBuffer; + case BufferUsage::Index: + return vk::BufferUsageFlagBits::eIndexBuffer; + case BufferUsage::Uniform: + return vk::BufferUsageFlagBits::eUniformBuffer; + case BufferUsage::Texel: + return vk::BufferUsageFlagBits::eUniformTexelBuffer; + case BufferUsage::Staging: + return vk::BufferUsageFlagBits::eTransferSrc; + default: + LOG_CRITICAL(Render_Vulkan, "Unknown buffer usage flag {}!", usage); + UNREACHABLE(); + } } inline vk::Format ToVkViewFormat(ViewFormat format) { - constexpr std::array vk_view_formats = { - vk::Format::eR32Sfloat, - vk::Format::eR32G32Sfloat, - vk::Format::eR32G32B32Sfloat, - vk::Format::eR32G32B32A32Sfloat - }; - - return vk_view_formats.at(static_cast(format)); + switch (format) { + case ViewFormat::R32Float: + return vk::Format::eR32Sfloat; + case ViewFormat::R32G32Float: + return vk::Format::eR32G32Sfloat; + case ViewFormat::R32G32B32Float: + return vk::Format::eR32G32B32Sfloat; + case ViewFormat::R32G32B32A32Float: + return vk::Format::eR32G32B32A32Sfloat; + default: + LOG_CRITICAL(Render_Vulkan, "Unknown buffer view format {}!", format); + UNREACHABLE(); + } } -Buffer::Buffer(Instance& instance, CommandScheduler& scheduler, const BufferInfo& info) : - BufferBase(info), instance(instance), scheduler(scheduler) { +Buffer::Buffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, const BufferInfo& info) + : BufferBase(info), instance(instance), scheduler(scheduler), pool_manager(pool_manager) { vk::BufferCreateInfo buffer_info = { .size = info.capacity, - .usage = ToVkBufferUsage(info.usage) + .usage = ToVkBufferUsage(info.usage) | vk::BufferUsageFlagBits::eTransferDst }; VmaAllocationCreateInfo alloc_create_info = { @@ -65,6 +76,7 @@ Buffer::Buffer(Instance& instance, CommandScheduler& scheduler, const BufferInfo vk::Device device = instance.GetDevice(); for (u32 view = 0; view < info.views.size(); view++) { if (info.views[view] == ViewFormat::Undefined) { + view_count = view; break; } @@ -107,6 +119,10 @@ Buffer::~Buffer() { } } +void Buffer::Free() { + pool_manager.Free(this); +} + std::span Buffer::Map(u32 size, u32 alignment) { ASSERT(size <= info.capacity && alignment <= info.capacity); diff --git a/src/video_core/renderer_vulkan/vk_buffer.h b/src/video_core/renderer_vulkan/vk_buffer.h index ac1241ab2..cd4991810 100644 --- a/src/video_core/renderer_vulkan/vk_buffer.h +++ b/src/video_core/renderer_vulkan/vk_buffer.h @@ -9,6 +9,10 @@ #include "video_core/common/buffer.h" #include "video_core/renderer_vulkan/vk_common.h" +namespace VideoCore { +class PoolManager; +} + namespace VideoCore::Vulkan { class Instance; @@ -16,20 +20,21 @@ class CommandScheduler; class Buffer : public VideoCore::BufferBase { public: - Buffer(Instance& instance, CommandScheduler& scheduler, const BufferInfo& info); + Buffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, + const BufferInfo& info); ~Buffer() override; - std::span Map(u32 size, u32 alignment = 0) override; + void Free() override; - /// Flushes write to buffer memory + std::span Map(u32 size, u32 alignment = 0) override; void Commit(u32 size = 0) override; - /// Returns the Vulkan buffer handle + // Returns the Vulkan buffer handle vk::Buffer GetHandle() const { return buffer; } - /// Returns an immutable reference to the requested buffer view + // Returns an immutable reference to the requested buffer view const vk::BufferView& GetView(u32 index = 0) const { ASSERT(index < view_count); return views[index]; @@ -38,6 +43,7 @@ public: protected: Instance& instance; CommandScheduler& scheduler; + PoolManager& pool_manager; // Vulkan buffer handle void* mapped_ptr = nullptr; diff --git a/src/video_core/renderer_vulkan/vk_framebuffer.cpp b/src/video_core/renderer_vulkan/vk_framebuffer.cpp index 7964b74f8..fd587b0d1 100644 --- a/src/video_core/renderer_vulkan/vk_framebuffer.cpp +++ b/src/video_core/renderer_vulkan/vk_framebuffer.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #define VULKAN_HPP_NO_CONSTRUCTORS +#include "video_core/common/pool_manager.h" #include "video_core/renderer_vulkan/vk_framebuffer.h" #include "video_core/renderer_vulkan/vk_texture.h" #include "video_core/renderer_vulkan/vk_task_scheduler.h" @@ -17,10 +18,10 @@ inline vk::Rect2D ToVkRect2D(Rect2D rect) { }; } -Framebuffer::Framebuffer(Instance& instance, CommandScheduler& scheduler, const FramebufferInfo& info, - vk::RenderPass load_renderpass, vk::RenderPass clear_renderpass) : - FramebufferBase(info), instance(instance), scheduler(scheduler), load_renderpass(load_renderpass), - clear_renderpass(clear_renderpass) { +Framebuffer::Framebuffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, + const FramebufferInfo& info, vk::RenderPass load_renderpass, vk::RenderPass clear_renderpass) : + FramebufferBase(info), instance(instance), scheduler(scheduler), pool_manager(pool_manager), + load_renderpass(load_renderpass), clear_renderpass(clear_renderpass) { const Texture* color = static_cast(info.color.Get()); const Texture* depth_stencil = static_cast(info.depth_stencil.Get()); @@ -57,9 +58,11 @@ Framebuffer::~Framebuffer() { device.destroyFramebuffer(framebuffer); } -void Framebuffer::DoClear() { - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); +void Framebuffer::Free() { + pool_manager.Free(this); +} +void Framebuffer::DoClear() { u32 clear_value_count = 0; std::array clear_values{}; @@ -81,6 +84,9 @@ void Framebuffer::DoClear() { }; } + // Prepare attachments + PrepareAttachments(); + const vk::RenderPassBeginInfo begin_info = { .renderPass = clear_renderpass, .framebuffer = framebuffer, @@ -90,8 +96,22 @@ void Framebuffer::DoClear() { }; // Begin clear pass + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); command_buffer.beginRenderPass(begin_info, vk::SubpassContents::eInline); command_buffer.endRenderPass(); } +void Framebuffer::PrepareAttachments() { + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + if (info.color.IsValid()) { + Texture* color = static_cast(info.color.Get()); + color->Transition(command_buffer, vk::ImageLayout::eColorAttachmentOptimal); + } + + if (info.depth_stencil.IsValid()) { + Texture* depth_stencil = static_cast(info.depth_stencil.Get()); + depth_stencil->Transition(command_buffer, vk::ImageLayout::eDepthAttachmentOptimal); + } +} + } // namespace VideoCore::Vulkan diff --git a/src/video_core/renderer_vulkan/vk_framebuffer.h b/src/video_core/renderer_vulkan/vk_framebuffer.h index 15ba13753..2a71a4ef6 100644 --- a/src/video_core/renderer_vulkan/vk_framebuffer.h +++ b/src/video_core/renderer_vulkan/vk_framebuffer.h @@ -7,6 +7,10 @@ #include "video_core/common/framebuffer.h" #include "video_core/renderer_vulkan/vk_common.h" +namespace VideoCore { +class PoolManager; +} + namespace VideoCore::Vulkan { class Instance; @@ -15,37 +19,38 @@ class CommandScheduler; class Framebuffer : public VideoCore::FramebufferBase { friend class Backend; public: - Framebuffer(Instance& instance, CommandScheduler& scheduler, const FramebufferInfo& info, - vk::RenderPass load_renderpass, vk::RenderPass clear_renderpass); + Framebuffer(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, + const FramebufferInfo& info, vk::RenderPass load_renderpass, + vk::RenderPass clear_renderpass); ~Framebuffer() override; + void Free() override; void DoClear() override; + // Transitions the attachments to the required layout + void PrepareAttachments(); + + // Returns the vulkan framebuffer handle vk::Framebuffer GetHandle() const { return framebuffer; } + // Returns the renderpass with VK_LOAD_OP_LOAD vk::RenderPass GetLoadRenderpass() const { return load_renderpass; } + // Returns the renderpass with VK_LOAD_OP_CLEAR (used for optimized GPU clear) vk::RenderPass GetClearRenderpass() const { return clear_renderpass; } - u32 GetAttachmentCount() const { - if (info.color.IsValid() && info.depth_stencil.IsValid()) { - return 2; - } else if (!info.color.IsValid() && !info.depth_stencil.IsValid()) { - return 0; - } else { - return 1; - } - } - private: Instance& instance; CommandScheduler& scheduler; + PoolManager& pool_manager; + + // Vulkan framebuffer vk::Framebuffer framebuffer; vk::RenderPass load_renderpass, clear_renderpass; }; diff --git a/src/video_core/renderer_vulkan/vk_pipeline.cpp b/src/video_core/renderer_vulkan/vk_pipeline.cpp index 62194c0ae..d2e8c974e 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline.cpp @@ -4,6 +4,7 @@ #define VULKAN_HPP_NO_CONSTRUCTORS #include "common/logging/log.h" +#include "video_core/common/pool_manager.h" #include "video_core/renderer_vulkan/pica_to_vulkan.h" #include "video_core/renderer_vulkan/vk_pipeline.h" #include "video_core/renderer_vulkan/vk_shader.h" @@ -186,10 +187,9 @@ PipelineOwner::~PipelineOwner() { } } -Pipeline::Pipeline(Instance& instance, CommandScheduler& scheduler, PipelineOwner& owner, - PipelineType type, PipelineInfo info, - vk::RenderPass renderpass, vk::PipelineCache cache) : PipelineBase(type, info), - instance(instance), scheduler(scheduler), owner(owner) { +Pipeline::Pipeline(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, PipelineOwner& owner, + PipelineType type, PipelineInfo info, vk::RenderPass renderpass, vk::PipelineCache cache) : + PipelineBase(type, info), instance(instance), scheduler(scheduler), pool_manager(pool_manager), owner(owner) { vk::Device device = instance.GetDevice(); @@ -382,6 +382,9 @@ Pipeline::~Pipeline() { device.destroyPipeline(pipeline); } +void Pipeline::Free() { + pool_manager.Free(this); +} void Pipeline::BindTexture(u32 group, u32 slot, TextureHandle handle) { Texture* texture = static_cast(handle.Get()); diff --git a/src/video_core/renderer_vulkan/vk_pipeline.h b/src/video_core/renderer_vulkan/vk_pipeline.h index e8fa68d65..bcb432c7d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_pipeline.h @@ -8,6 +8,10 @@ #include "video_core/common/pipeline.h" #include "video_core/renderer_vulkan/vk_common.h" +namespace VideoCore { +class PoolManager; +} + namespace VideoCore::Vulkan { class Instance; @@ -82,11 +86,12 @@ private: class Pipeline : public VideoCore::PipelineBase { public: - Pipeline(Instance& instance, CommandScheduler& scheduler, PipelineOwner& owner, - PipelineType type, PipelineInfo info, - vk::RenderPass renderpass, vk::PipelineCache cache); + Pipeline(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, PipelineOwner& owner, + PipelineType type, PipelineInfo info, vk::RenderPass renderpass, vk::PipelineCache cache); ~Pipeline() override; + void Free() override; + void BindTexture(u32 group, u32 slot, TextureHandle handle) override; void BindBuffer(u32 group, u32 slot, BufferHandle handle, u32 offset = 0, u32 range = WHOLE_SIZE, u32 view = 0) override; @@ -111,6 +116,9 @@ public: private: Instance& instance; CommandScheduler& scheduler; + PoolManager& pool_manager; + + // The owner of this pipeline PipelineOwner& owner; vk::Pipeline pipeline; }; diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp index 65c59ddb1..256be16b9 100644 --- a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp @@ -37,18 +37,14 @@ RenderpassCache::RenderpassCache(Instance& instance) : instance(instance) { vk::Format color_format = instance.GetFormatAlternative(color_formats[color]); vk::Format depth_stencil_format = instance.GetFormatAlternative(depth_stencil_formats[depth]); - if (color_format == vk::Format::eA8B8G8R8SintPack32) { - LOG_INFO(Render_Vulkan, ""); - } - // Construct both load and clear pass cached_renderpasses[color][depth][0] = CreateRenderPass(color_format, depth_stencil_format, vk::AttachmentLoadOp::eLoad, - vk::ImageLayout::eShaderReadOnlyOptimal, + vk::ImageLayout::eColorAttachmentOptimal, vk::ImageLayout::eColorAttachmentOptimal); cached_renderpasses[color][depth][1] = CreateRenderPass(color_format, depth_stencil_format, vk::AttachmentLoadOp::eClear, - vk::ImageLayout::eShaderReadOnlyOptimal, + vk::ImageLayout::eColorAttachmentOptimal, vk::ImageLayout::eColorAttachmentOptimal); } } @@ -85,7 +81,7 @@ void RenderpassCache::CreatePresentRenderpass(vk::Format format) { vk::RenderPass RenderpassCache::GetRenderpass(TextureFormat color, TextureFormat depth, bool is_clear) const { const u32 color_index = static_cast(color); - const u32 depth_index = static_cast(depth) - MAX_COLOR_FORMATS; + const u32 depth_index = (depth == TextureFormat::Undefined ? 0 : (static_cast(depth) - MAX_COLOR_FORMATS)); ASSERT(color_index < MAX_COLOR_FORMATS && depth_index < MAX_DEPTH_FORMATS); return cached_renderpasses[color_index][depth_index][is_clear]; @@ -127,7 +123,9 @@ vk::RenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format de .format = depth, .loadOp = load_op, .storeOp = vk::AttachmentStoreOp::eStore, - .initialLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + .stencilLoadOp = vk::AttachmentLoadOp::eLoad, + .stencilStoreOp = vk::AttachmentStoreOp::eStore, + .initialLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal, .finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal }; diff --git a/src/video_core/renderer_vulkan/vk_shader.cpp b/src/video_core/renderer_vulkan/vk_shader.cpp index 00d875002..6038abb68 100644 --- a/src/video_core/renderer_vulkan/vk_shader.cpp +++ b/src/video_core/renderer_vulkan/vk_shader.cpp @@ -5,6 +5,7 @@ #define VULKAN_HPP_NO_CONSTRUCTORS #include "common/assert.h" #include "common/logging/log.h" +#include "video_core/common/pool_manager.h" #include "video_core/renderer_vulkan/vk_shader.h" #include "video_core/renderer_vulkan/vk_instance.h" #include @@ -154,9 +155,9 @@ bool InitializeCompiler() { return true; } -Shader::Shader(Instance& instance, ShaderStage stage, std::string_view name, - std::string&& source) : - ShaderBase(stage, name, std::move(source)), instance(instance) { +Shader::Shader(Instance& instance, PoolManager& pool_manager, ShaderStage stage, std::string_view name, + std::string&& source) : ShaderBase(stage, name, std::move(source)), + instance(instance), pool_manager(pool_manager) { } Shader::~Shader() { @@ -164,6 +165,10 @@ Shader::~Shader() { device.destroyShaderModule(module); } +void Shader::Free() { + pool_manager.Free(this); +} + bool Shader::Compile(ShaderOptimization level) { if (!InitializeCompiler()) { return false; diff --git a/src/video_core/renderer_vulkan/vk_shader.h b/src/video_core/renderer_vulkan/vk_shader.h index d503ee55b..f475729b6 100644 --- a/src/video_core/renderer_vulkan/vk_shader.h +++ b/src/video_core/renderer_vulkan/vk_shader.h @@ -7,16 +7,22 @@ #include "video_core/common/shader.h" #include "video_core/renderer_vulkan/vk_common.h" +namespace VideoCore { +class PoolManager; +} + namespace VideoCore::Vulkan { class Instance; class Shader : public VideoCore::ShaderBase { public: - Shader(Instance& instance, ShaderStage stage, std::string_view name, + Shader(Instance& instance, PoolManager& pool_manager, ShaderStage stage, std::string_view name, std::string&& source); ~Shader() override; + void Free() override; + bool Compile(ShaderOptimization level) override; /// Returns the underlying vulkan shader module handle @@ -26,6 +32,7 @@ public: private: Instance& instance; + PoolManager& pool_manager; vk::ShaderModule module; }; diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index b5a4ada04..dd7c31373 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -3,22 +3,25 @@ // Refer to the license.txt file included. #define VULKAN_HPP_NO_CONSTRUCTORS -#include #include "common/logging/log.h" +#include "video_core/common/pool_manager.h" #include "video_core/renderer_vulkan/vk_swapchain.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_backend.h" #include "video_core/renderer_vulkan/vk_framebuffer.h" #include "video_core/renderer_vulkan/vk_texture.h" -#include "video_core/renderer_vulkan/vk_renderpass_cache.h" namespace VideoCore::Vulkan { Swapchain::Swapchain(Instance& instance, CommandScheduler& scheduler, RenderpassCache& renderpass_cache, - Backend* backend, vk::SurfaceKHR surface) : - backend(backend), instance(instance), scheduler(scheduler), - renderpass_cache(renderpass_cache), surface(surface) { + PoolManager& pool_manager, vk::SurfaceKHR surface) : instance(instance), scheduler(scheduler), + renderpass_cache(renderpass_cache), pool_manager(pool_manager), surface(surface) { + // Set the surface format early for RenderpassCache to create the present renderpass + Configure(0, 0); + + // Create the present renderpass + renderpass_cache.CreatePresentRenderpass(surface_format.format); } Swapchain::~Swapchain() { @@ -42,7 +45,7 @@ void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) { }; const bool exclusive = queue_family_indices[0] == queue_family_indices[1]; - const u32 queue_family_indices_count = exclusive ? 2u : 1u; + const u32 queue_family_indices_count = exclusive ? 1u : 2u; const vk::SharingMode sharing_mode = exclusive ? vk::SharingMode::eExclusive : vk::SharingMode::eConcurrent; @@ -81,9 +84,6 @@ void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) { render_finished = device.createSemaphore({}); } - // Create the present renderpass - renderpass_cache.CreatePresentRenderpass(surface_format.format); - // Create framebuffer and image views vk_images = device.getSwapchainImagesKHR(swapchain); @@ -102,16 +102,16 @@ void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) { framebuffers.clear(); framebuffers.resize(vk_images.size()); for (int i = 0; i < vk_images.size(); i++) { - textures[i] = MakeHandle(instance, scheduler, vk_images[i], - surface_format.format, image_info); + textures[i] = pool_manager.Allocate(instance, scheduler, pool_manager, vk_images[i], + surface_format.format, image_info); const FramebufferInfo framebuffer_info = { .color = textures[i] }; vk::RenderPass renderpass = renderpass_cache.GetPresentRenderpass(); - framebuffers[i] = MakeHandle(instance, scheduler, framebuffer_info, - renderpass, renderpass); + framebuffers[i] = pool_manager.Allocate(instance, scheduler, pool_manager, framebuffer_info, + renderpass, renderpass); } } diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index be3f13c86..68ed86a1d 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -9,6 +9,10 @@ #include "video_core/common/framebuffer.h" #include "video_core/renderer_vulkan/vk_common.h" +namespace VideoCore { +class PoolManager; +} + namespace VideoCore::Vulkan { class Instance; @@ -20,59 +24,59 @@ class RenderpassCache; class Swapchain { public: Swapchain(Instance& instance, CommandScheduler& scheduler, RenderpassCache& renderpass_cache, - Backend* backend, vk::SurfaceKHR surface); + PoolManager& pool_manager, vk::SurfaceKHR surface); ~Swapchain(); - /// Creates (or recreates) the swapchain with a given size. + // Creates (or recreates) the swapchain with a given size. void Create(u32 width, u32 height, bool vsync_enabled); - /// Acquire the next image in the swapchain. + // Acquires the next image in the swapchain. void AcquireNextImage(); - /// Present the current image and move to the next one + // Presents the current image and move to the next one void Present(); FramebufferHandle GetCurrentFramebuffer() const { return framebuffers[current_image]; } - /// Return current swapchain state - inline vk::Extent2D GetExtent() const { + // Returns current swapchain state + vk::Extent2D GetExtent() const { return extent; } - /// Return the swapchain surface - inline vk::SurfaceKHR GetSurface() const { + // Returns the swapchain surface + vk::SurfaceKHR GetSurface() const { return surface; } - /// Return the swapchain format - inline vk::SurfaceFormatKHR GetSurfaceFormat() const { + // Returns the swapchain format + vk::SurfaceFormatKHR GetSurfaceFormat() const { return surface_format; } - /// Return the Vulkan swapchain handle - inline vk::SwapchainKHR GetHandle() const { + // Returns the Vulkan swapchain handle + vk::SwapchainKHR GetHandle() const { return swapchain; } - /// Return the semaphore that will be signaled when vkAcquireNextImageKHR completes - inline vk::Semaphore GetAvailableSemaphore() const { + // Returns the semaphore that will be signaled when vkAcquireNextImageKHR completes + vk::Semaphore GetAvailableSemaphore() const { return image_available; } - /// Return the semaphore that will signal when the current image will be presented - inline vk::Semaphore GetPresentSemaphore() const { + // Returns the semaphore that will signal when the current image will be presented + vk::Semaphore GetPresentSemaphore() const { return render_finished; } - /// Return the current swapchain image - inline vk::Image GetCurrentImage() { + // Returns the current swapchain image + vk::Image GetCurrentImage() { return vk_images[current_image]; } - /// Returns true when the swapchain should be recreated - inline bool NeedsRecreation() const { + // Returns true when the swapchain should be recreated + bool NeedsRecreation() const { return is_suboptimal || is_outdated; } @@ -80,10 +84,10 @@ private: void Configure(u32 width, u32 height); private: - Backend* backend = nullptr; Instance& instance; CommandScheduler& scheduler; RenderpassCache& renderpass_cache; + PoolManager& pool_manager; vk::SwapchainKHR swapchain = VK_NULL_HANDLE; vk::SurfaceKHR surface = VK_NULL_HANDLE; diff --git a/src/video_core/renderer_vulkan/vk_task_scheduler.cpp b/src/video_core/renderer_vulkan/vk_task_scheduler.cpp index 25aae0248..3ae907844 100644 --- a/src/video_core/renderer_vulkan/vk_task_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_task_scheduler.cpp @@ -4,6 +4,7 @@ #define VULKAN_HPP_NO_CONSTRUCTORS #include "common/logging/log.h" +#include "video_core/common/pool_manager.h" #include "video_core/renderer_vulkan/vk_task_scheduler.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_buffer.h" @@ -16,7 +17,9 @@ constexpr BufferInfo STAGING_INFO = { .usage = BufferUsage::Staging }; -CommandScheduler::CommandScheduler(Instance& instance) : instance(instance) { +CommandScheduler::CommandScheduler(Instance& instance, PoolManager& pool_manager) : instance(instance), + pool_manager(pool_manager) { + vk::Device device = instance.GetDevice(); const vk::CommandPoolCreateInfo pool_info = { .flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer, @@ -43,7 +46,7 @@ CommandScheduler::CommandScheduler(Instance& instance) : instance(instance) { .upload_command_buffer = command_buffers[2 * i + 1], }; - commands[i].upload_buffer = std::make_unique(instance, *this, STAGING_INFO); + commands[i].upload_buffer = pool_manager.Allocate(instance, *this, pool_manager, STAGING_INFO); } const vk::CommandBufferBeginInfo begin_info = { @@ -103,9 +106,11 @@ void CommandScheduler::Synchronize() { completed_fence_counter = now_fence_counter; } -void CommandScheduler::Submit(bool wait_completion, - vk::Semaphore wait_semaphore, - vk::Semaphore signal_semaphore) { +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]; // End command buffers @@ -149,8 +154,8 @@ void CommandScheduler::Submit(bool wait_completion, SwitchSlot(); } -void CommandScheduler::Schedule(Deleter&& func) { - auto& command = commands[current_command]; +void CommandScheduler::Schedule(std::function&& func) { + CommandSlot& command = commands[current_command]; command.cleanups.push_back(func); } @@ -175,6 +180,9 @@ void CommandScheduler::SwitchSlot() { // Wait for the GPU to finish with all resources for this command. Synchronize(); + // Invoke the callback function + switch_callback(current_command); + const vk::CommandBufferBeginInfo begin_info = { .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit }; diff --git a/src/video_core/renderer_vulkan/vk_task_scheduler.h b/src/video_core/renderer_vulkan/vk_task_scheduler.h index 53f31d6b0..1b651d712 100644 --- a/src/video_core/renderer_vulkan/vk_task_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_task_scheduler.h @@ -8,59 +8,66 @@ #include #include #include "common/common_types.h" +#include "video_core/renderer_vulkan/vk_buffer.h" #include "video_core/renderer_vulkan/vk_common.h" +namespace VideoCore { +class PoolManager; +} + namespace VideoCore::Vulkan { constexpr u32 SCHEDULER_COMMAND_COUNT = 4; -using Deleter = std::function; - class Buffer; class Instance; class CommandScheduler { public: - CommandScheduler(Instance& instance); + CommandScheduler(Instance& instance, PoolManager& pool_manager); ~CommandScheduler(); - /// Block host until the current command completes execution + // Blocks the host until the current command completes execution void Synchronize(); - /// Defer operation until the current command completes execution - void Schedule(Deleter&& func); + // Sets a function to be called when the command slot is switched + void SetSwitchCallback(std::function callback); - /// Submits the current command to the graphics queue + // Defers operation until the current command completes execution + 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); - /// Returns the command buffer used for early upload operations. - /// This is useful for vertex/uniform buffer uploads that happen once per frame + // Returns the command buffer used for early upload operations. + // This is useful for vertex/uniform buffer uploads that happen once per frame vk::CommandBuffer GetUploadCommandBuffer(); - /// Returns the command buffer used for rendering - inline vk::CommandBuffer GetRenderCommandBuffer() const { + // Returns the command buffer used for rendering + vk::CommandBuffer GetRenderCommandBuffer() const { const CommandSlot& command = commands[current_command]; return command.render_command_buffer; } - /// Returns the upload buffer of the active command slot - inline Buffer& GetCommandUploadBuffer() { + // Returns the upload buffer of the active command slot + Buffer& GetCommandUploadBuffer() { CommandSlot& command = commands[current_command]; - return *command.upload_buffer; + return *static_cast(command.upload_buffer.Get()); } - /// Returns the index of the current command slot + // Returns the index of the current command slot inline u32 GetCurrentSlotIndex() const { return current_command; } private: - /// Activates the next command slot and optionally waits for its completion + // Activates the next command slot and optionally waits for its completion void SwitchSlot(); private: Instance& instance; + PoolManager& pool_manager; u64 next_fence_counter = 1; u64 completed_fence_counter = 0; @@ -70,12 +77,13 @@ private: vk::Fence fence = VK_NULL_HANDLE; vk::CommandBuffer render_command_buffer; vk::CommandBuffer upload_command_buffer; - std::unique_ptr upload_buffer; - std::vector cleanups; + BufferHandle upload_buffer; + std::vector> cleanups; }; vk::CommandPool command_pool = VK_NULL_HANDLE; std::array commands; + std::function switch_callback; u32 current_command = 0; }; diff --git a/src/video_core/renderer_vulkan/vk_texture.cpp b/src/video_core/renderer_vulkan/vk_texture.cpp index 6613dfe5f..60ed08e17 100644 --- a/src/video_core/renderer_vulkan/vk_texture.cpp +++ b/src/video_core/renderer_vulkan/vk_texture.cpp @@ -5,6 +5,7 @@ #define VULKAN_HPP_NO_CONSTRUCTORS #include "common/assert.h" #include "common/logging/log.h" +#include "video_core/common/pool_manager.h" #include "video_core/renderer_vulkan/pica_to_vulkan.h" #include "video_core/renderer_vulkan/vk_buffer.h" #include "video_core/renderer_vulkan/vk_texture.h" @@ -73,11 +74,9 @@ inline vk::ImageViewType ToVkImageViewType(TextureViewType view_type) { } } -Texture::Texture(Instance& instance, CommandScheduler& scheduler) : - instance(instance), scheduler(scheduler) {} - -Texture::Texture(Instance& instance, CommandScheduler& scheduler, const TextureInfo& info) : TextureBase(info), - instance(instance), scheduler(scheduler) { +Texture::Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, + const TextureInfo& info) : TextureBase(info), instance(instance), scheduler(scheduler), + pool_manager(pool_manager) { // Convert the input format to another that supports attachments advertised_format = ToVkFormat(info.format); @@ -118,7 +117,7 @@ Texture::Texture(Instance& instance, CommandScheduler& scheduler, const TextureI .image = image, .viewType = ToVkImageViewType(info.view_type), .format = internal_format, - .subresourceRange = {aspect, 0, info.levels, 0, 1} + .subresourceRange = {aspect, 0, 1, 0, 1} // Should this include the levels? }; // Create image view @@ -130,9 +129,9 @@ Texture::Texture(Instance& instance, CommandScheduler& scheduler, const TextureI Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal, 0, info.levels); } -Texture::Texture(Instance& instance, CommandScheduler& scheduler, vk::Image image, vk::Format format, - const TextureInfo& info) : TextureBase(info), instance(instance), - scheduler(scheduler), image(image), is_texture_owned(false) { +Texture::Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, + vk::Image image, vk::Format format, const TextureInfo& info) : TextureBase(info), + instance(instance), scheduler(scheduler), pool_manager(pool_manager), image(image), is_texture_owned(false) { advertised_format = internal_format = format; aspect = vk::ImageAspectFlagBits::eColor; @@ -167,6 +166,10 @@ Texture::~Texture() { } } +void Texture::Free() { + pool_manager.Free(this); +} + void Texture::Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout, u32 level, u32 level_count) { ASSERT(level + level_count < TEXTURE_MAX_LEVELS); @@ -458,8 +461,8 @@ void Texture::Download(Rect2D rectangle, u32 stride, std::span data, u32 lev } } -void Texture::BlitTo(TextureHandle dest, Rect2D source_rect, Rect2D dest_rect, u32 src_level, u32 dest_level, - u32 src_layer, u32 dest_layer) { +void Texture::BlitTo(TextureHandle dest, Common::Rectangle source_rect, Common::Rectangle dest_rect, + u32 src_level, u32 dest_level, u32 src_layer, u32 dest_layer) { Texture* dest_texture = static_cast(dest.Get()); @@ -469,15 +472,13 @@ void Texture::BlitTo(TextureHandle dest, Rect2D source_rect, Rect2D dest_rect, u dest_texture->Transition(command_buffer, vk::ImageLayout::eTransferDstOptimal); const std::array source_offsets = { - vk::Offset3D{source_rect.x, source_rect.y, 0}, - vk::Offset3D{static_cast(source_rect.x + source_rect.width), - static_cast(source_rect.y + source_rect.height), 1} + vk::Offset3D{static_cast(source_rect.left), static_cast(source_rect.bottom), 0}, + vk::Offset3D{static_cast(source_rect.right), static_cast(source_rect.top), 1} }; const std::array dest_offsets = { - vk::Offset3D{dest_rect.x, dest_rect.y, 0}, - vk::Offset3D{static_cast(dest_rect.x + dest_rect.width), - static_cast(dest_rect.y + dest_rect.height), 1} + vk::Offset3D{static_cast(dest_rect.left), static_cast(dest_rect.bottom), 0}, + vk::Offset3D{static_cast(dest_rect.right), static_cast(dest_rect.top), 1} }; const vk::ImageBlit blit_area = { @@ -665,8 +666,8 @@ void StagingTexture::Commit(u32 size) { vmaFlushAllocation(allocator, allocation, 0, size); } -Sampler::Sampler(Instance& instance, SamplerInfo info) : - SamplerBase(info), instance(instance) { +Sampler::Sampler(Instance& instance, PoolManager& pool_manager, SamplerInfo info) : + SamplerBase(info), instance(instance), pool_manager(pool_manager) { auto properties = instance.GetPhysicalDevice().getProperties(); const auto filtering = PicaToVK::TextureFilterMode(info.mag_filter, @@ -695,4 +696,8 @@ Sampler::~Sampler() { device.destroySampler(sampler); } -} // namespace Vulkan +void Sampler::Free() { + pool_manager.Free(this); +} + +} // namespace VideoCore::Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture.h b/src/video_core/renderer_vulkan/vk_texture.h index b6623cfc4..d05975ad6 100644 --- a/src/video_core/renderer_vulkan/vk_texture.h +++ b/src/video_core/renderer_vulkan/vk_texture.h @@ -7,10 +7,14 @@ #include "video_core/common/texture.h" #include "video_core/renderer_vulkan/vk_common.h" +namespace VideoCore { +class PoolManager; +} + namespace VideoCore::Vulkan { // PICA texture have at most 8 mipmap levels -constexpr u32 TEXTURE_MAX_LEVELS = 10; +constexpr u32 TEXTURE_MAX_LEVELS = 12; class Instance; class CommandScheduler; @@ -20,24 +24,24 @@ class CommandScheduler; */ class Texture : public VideoCore::TextureBase { public: - // Default constructor - Texture(Instance& instance, CommandScheduler& scheduler); - // Constructor for texture creation - Texture(Instance& instance, CommandScheduler& scheduler, const TextureInfo& info); - - // Constructor for swapchain images - Texture(Instance& instance, CommandScheduler& scheduler, vk::Image image, vk::Format format, + Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, const TextureInfo& info); + // Constructor for swapchain images + Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, + vk::Image image, vk::Format format, const TextureInfo& info); + ~Texture() override; + void Free() override; + void Upload(Rect2D rectangle, u32 stride, std::span data, u32 level = 0) override; void Download(Rect2D rectangle, u32 stride, std::span data, u32 level = 0) override; - void BlitTo(TextureHandle dest, Rect2D src_rectangle, Rect2D dest_rect, u32 src_level = 0, - u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) override; + void BlitTo(TextureHandle dest, Common::Rectangle src_rectangle, Common::Rectangle dest_rect, + u32 src_level = 0, u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) override; void CopyFrom(TextureHandle source) override; @@ -83,6 +87,7 @@ public: private: Instance& instance; CommandScheduler& scheduler; + PoolManager& pool_manager; // Vulkan texture handle vk::Image image = VK_NULL_HANDLE; @@ -103,19 +108,20 @@ private: */ class StagingTexture : public VideoCore::TextureBase { public: - StagingTexture(Instance& instance, CommandScheduler& scheduler, - const TextureInfo& info); + StagingTexture(Instance& instance, CommandScheduler& scheduler, const TextureInfo& info); ~StagingTexture(); - /// Flushes any writes made to texture memory + void Free() override {} + + // Flushes any writes made to texture memory void Commit(u32 size); - /// Returns a span of the mapped texture memory + // Returns a span of the mapped texture memory void* GetMappedPtr() { return mapped_ptr; } - /// Returns the staging image handle + // Returns the staging image handle vk::Image GetHandle() const { return image; } @@ -136,16 +142,19 @@ private: */ class Sampler : public VideoCore::SamplerBase { public: - Sampler(Instance& instance, SamplerInfo info); + Sampler(Instance& instance, PoolManager& pool_manager, SamplerInfo info); ~Sampler() override; - /// Returns the underlying vulkan sampler handle + void Free() override; + + // Returns the underlying vulkan sampler handle vk::Sampler GetHandle() const { return sampler; } private: Instance& instance; + PoolManager& pool_manager; vk::Sampler sampler; };