From 609fa5c7d7e3245cfb5395077f489de65949719a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 18 Jan 2025 14:55:15 +1000 Subject: [PATCH] GPUDevice: Add sampler cache to base class Removes per-backend bookkeeping in D3D12 and Vulkan. --- src/util/d3d12_device.cpp | 11 +++++------ src/util/d3d12_device.h | 6 ------ src/util/d3d12_texture.cpp | 37 +++++++++---------------------------- src/util/gpu_device.cpp | 32 +++++++++++++++++++++++++++----- src/util/gpu_device.h | 14 ++++++++++---- src/util/vulkan_device.cpp | 15 ++++++++++----- src/util/vulkan_device.h | 6 +----- src/util/vulkan_texture.cpp | 31 ++++--------------------------- 8 files changed, 66 insertions(+), 86 deletions(-) diff --git a/src/util/d3d12_device.cpp b/src/util/d3d12_device.cpp index 7dd946c3f..2220feec0 100644 --- a/src/util/d3d12_device.cpp +++ b/src/util/d3d12_device.cpp @@ -317,7 +317,6 @@ void D3D12Device::DestroyDevice() m_main_swap_chain.reset(); DestroyDeferredObjects(m_current_fence_value); - DestroySamplers(); DestroyTimestampQuery(); DestroyBuffers(); DestroyDescriptorHeaps(); @@ -571,11 +570,11 @@ bool D3D12Device::CreateDescriptorHeaps(Error* error) m_device->CreateUnorderedAccessView(nullptr, nullptr, &null_uav_desc, m_null_uav_descriptor.cpu_handle); // Same for samplers. - m_point_sampler = GetSampler(GPUSampler::GetNearestConfig(), error); - if (!m_point_sampler) [[unlikely]] + GPUSampler* default_sampler = GetSampler(GPUSampler::GetNearestConfig(), error); + if (!default_sampler) [[unlikely]] return false; for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++) - m_current_samplers[i] = m_point_sampler; + m_current_samplers[i] = static_cast(default_sampler)->GetDescriptor(); return true; } @@ -2123,7 +2122,7 @@ void D3D12Device::SetTextureSampler(u32 slot, GPUTexture* texture, GPUSampler* s } const D3D12DescriptorHandle& handle = - sampler ? static_cast(sampler)->GetDescriptor() : m_point_sampler; + static_cast(sampler ? sampler : m_nearest_sampler)->GetDescriptor(); if (m_current_samplers[slot] != handle) { m_current_samplers[slot] = handle; @@ -2295,7 +2294,7 @@ void D3D12Device::RenderTextureMipmap(D3D12Texture* texture, u32 dst_level, u32 cmdlist->SetPipelineState(pipeline.Get()); cmdlist->SetGraphicsRootDescriptorTable(0, srv_handle); - cmdlist->SetGraphicsRootDescriptorTable(1, static_cast(m_linear_sampler.get())->GetDescriptor()); + cmdlist->SetGraphicsRootDescriptorTable(1, static_cast(m_linear_sampler)->GetDescriptor()); cmdlist->DrawInstanced(3, 1, 0, 0); cmdlist->EndRenderPass(); diff --git a/src/util/d3d12_device.h b/src/util/d3d12_device.h index 3b55d65fd..598e97716 100644 --- a/src/util/d3d12_device.h +++ b/src/util/d3d12_device.h @@ -246,8 +246,6 @@ private: }; static_assert(sizeof(PIPELINE_CACHE_HEADER) == 16); - using SamplerMap = std::unordered_map; - void GetPipelineCacheHeader(PIPELINE_CACHE_HEADER* hdr); void SetFeatures(D3D_FEATURE_LEVEL feature_level, FeatureMask disabled_features); @@ -261,8 +259,6 @@ private: void DestroyDescriptorHeaps(); bool CreateTimestampQuery(); void DestroyTimestampQuery(); - D3D12DescriptorHandle GetSampler(const GPUSampler::Config& config, Error* error); - void DestroySamplers(); void DestroyDeferredObjects(u64 fence_value); void RenderBlankFrame(D3D12SwapChain* swap_chain); @@ -320,7 +316,6 @@ private: D3D12DescriptorHeapManager m_sampler_heap_manager; D3D12DescriptorHandle m_null_srv_descriptor; D3D12DescriptorHandle m_null_uav_descriptor; - D3D12DescriptorHandle m_point_sampler; ComPtr m_timestamp_query_heap; ComPtr m_timestamp_query_buffer; @@ -342,7 +337,6 @@ private: u32 m_uniform_buffer_position = 0; bool m_in_render_pass = false; - SamplerMap m_sampler_map; ComPtr m_pipeline_library; // Which bindings/state has to be updated before the next draw. diff --git a/src/util/d3d12_texture.cpp b/src/util/d3d12_texture.cpp index dfcf16046..e6bc39d6c 100644 --- a/src/util/d3d12_texture.cpp +++ b/src/util/d3d12_texture.cpp @@ -699,7 +699,8 @@ D3D12Sampler::D3D12Sampler(D3D12DescriptorHandle descriptor) : m_descriptor(desc D3D12Sampler::~D3D12Sampler() { - // Cleaned up by main class. + D3D12Device& dev = D3D12Device::GetInstance(); + dev.DeferDescriptorDestruction(dev.GetSamplerHeapManager(), &m_descriptor); } #ifdef ENABLE_GPU_OBJECT_NAMES @@ -710,12 +711,8 @@ void D3D12Sampler::SetDebugName(std::string_view name) #endif -D3D12DescriptorHandle D3D12Device::GetSampler(const GPUSampler::Config& config, Error* error) +std::unique_ptr D3D12Device::CreateSampler(const GPUSampler::Config& config, Error* error /* = nullptr */) { - const auto it = m_sampler_map.find(config.key); - if (it != m_sampler_map.end()) - return it->second; - static constexpr std::array(GPUSampler::AddressMode::MaxCount)> ta = {{ D3D12_TEXTURE_ADDRESS_MODE_WRAP, // Repeat D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // ClampToEdge @@ -756,31 +753,15 @@ D3D12DescriptorHandle D3D12Device::GetSampler(const GPUSampler::Config& config, D3D12DescriptorHandle handle; if (m_sampler_heap_manager.Allocate(&handle)) [[likely]] - m_device->CreateSampler(&desc, handle); - else - Error::SetStringView(error, "Failed to allocate sampler handle."); - - m_sampler_map.emplace(config.key, handle); - return handle; -} - -void D3D12Device::DestroySamplers() -{ - for (auto& it : m_sampler_map) { - if (it.second) - m_sampler_heap_manager.Free(&it.second); + m_device->CreateSampler(&desc, handle); + return std::unique_ptr(new D3D12Sampler(handle)); } - m_sampler_map.clear(); -} - -std::unique_ptr D3D12Device::CreateSampler(const GPUSampler::Config& config, Error* error /* = nullptr */) -{ - const D3D12DescriptorHandle handle = GetSampler(config, error); - if (!handle) + else + { + Error::SetStringView(error, "Failed to allocate sampler handle."); return {}; - - return std::unique_ptr(new D3D12Sampler(handle)); + } } D3D12TextureBuffer::D3D12TextureBuffer(Format format, u32 size_in_elements) : GPUTextureBuffer(format, size_in_elements) diff --git a/src/util/gpu_device.cpp b/src/util/gpu_device.cpp index 2b4fcea85..78be75039 100644 --- a/src/util/gpu_device.cpp +++ b/src/util/gpu_device.cpp @@ -650,12 +650,14 @@ bool GPUDevice::GetPipelineCacheData(DynamicHeapArray* data, Error* error) bool GPUDevice::CreateResources(Error* error) { - if (!(m_nearest_sampler = CreateSampler(GPUSampler::GetNearestConfig(), error)) || - !(m_linear_sampler = CreateSampler(GPUSampler::GetLinearConfig(), error))) + if (!(m_nearest_sampler = GetSampler(GPUSampler::GetNearestConfig(), error)) || + !(m_linear_sampler = GetSampler(GPUSampler::GetLinearConfig(), error))) { Error::AddPrefix(error, "Failed to create samplers: "); return false; } + GL_OBJECT_NAME(m_nearest_sampler, "Nearest Sampler"); + GL_OBJECT_NAME(m_linear_sampler, "Nearest Sampler"); const RenderAPI render_api = GetRenderAPI(); ShaderGen shadergen(render_api, ShaderGen::GetShaderLanguageForAPI(render_api), m_features.dual_source_blend, @@ -717,8 +719,9 @@ void GPUDevice::DestroyResources() m_imgui_pipeline.reset(); - m_linear_sampler.reset(); - m_nearest_sampler.reset(); + m_linear_sampler = nullptr; + m_nearest_sampler = nullptr; + m_sampler_map.clear(); m_shader_cache.Close(); } @@ -773,7 +776,7 @@ void GPUDevice::RenderImGui(GPUSwapChain* swap_chain) clip = FlipToLowerLeft(clip, post_rotated_height); SetScissor(clip); - SetTextureSampler(0, reinterpret_cast(pcmd->TextureId), m_linear_sampler.get()); + SetTextureSampler(0, reinterpret_cast(pcmd->TextureId), m_linear_sampler); if (pcmd->UserCallback) [[unlikely]] { @@ -1015,6 +1018,25 @@ GSVector4i GPUDevice::FlipToLowerLeft(GSVector4i rc, s32 target_height) return rc; } +GPUSampler* GPUDevice::GetSampler(const GPUSampler::Config& config, Error* error /* = nullptr */) +{ + auto it = m_sampler_map.find(config.key); + if (it != m_sampler_map.end()) + { + if (!it->second) [[unlikely]] + Error::SetStringView(error, "Sampler previously failed creation."); + + return it->second.get(); + } + + std::unique_ptr sampler = g_gpu_device->CreateSampler(config, error); + if (sampler) + GL_OBJECT_NAME_FMT(sampler, "Sampler {:016X}", config.key); + + it = m_sampler_map.emplace(config.key, std::move(sampler)).first; + return it->second.get(); +} + bool GPUDevice::IsTexturePoolType(GPUTexture::Type type) { return (type == GPUTexture::Type::Texture); diff --git a/src/util/gpu_device.h b/src/util/gpu_device.h index eda9dd9d2..a8f132101 100644 --- a/src/util/gpu_device.h +++ b/src/util/gpu_device.h @@ -716,8 +716,8 @@ public: ALWAYS_INLINE GPUSwapChain* GetMainSwapChain() const { return m_main_swap_chain.get(); } ALWAYS_INLINE bool HasMainSwapChain() const { return static_cast(m_main_swap_chain); } - ALWAYS_INLINE GPUSampler* GetLinearSampler() const { return m_linear_sampler.get(); } - ALWAYS_INLINE GPUSampler* GetNearestSampler() const { return m_nearest_sampler.get(); } + ALWAYS_INLINE GPUSampler* GetLinearSampler() const { return m_linear_sampler; } + ALWAYS_INLINE GPUSampler* GetNearestSampler() const { return m_nearest_sampler; } ALWAYS_INLINE bool IsGPUTimingEnabled() const { return m_gpu_timing_enabled; } @@ -757,6 +757,8 @@ public: virtual std::unique_ptr CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements, Error* error = nullptr) = 0; + GPUSampler* GetSampler(const GPUSampler::Config& config, Error* error = nullptr); + // Texture pooling. std::unique_ptr FetchTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Flags flags, @@ -918,11 +920,11 @@ protected: u32 m_max_multisamples = 0; std::unique_ptr m_main_swap_chain; + GPUSampler* m_nearest_sampler = nullptr; + GPUSampler* m_linear_sampler = nullptr; GPUShaderCache m_shader_cache; - std::unique_ptr m_nearest_sampler; - std::unique_ptr m_linear_sampler; private: static constexpr u32 MAX_TEXTURE_POOL_SIZE = 125; @@ -957,6 +959,7 @@ private: }; using TexturePool = std::deque; + using SamplerMap = std::unordered_map>; #ifdef __APPLE__ // We have to define these in the base class, because they're in Objective C++. @@ -976,11 +979,14 @@ private: std::unique_ptr m_imgui_pipeline; std::unique_ptr m_imgui_font_texture; + SamplerMap m_sampler_map; + TexturePool m_texture_pool; TexturePool m_target_pool; size_t m_pool_vram_usage = 0; u32 m_texture_pool_counter = 0; + protected: static Statistics s_stats; diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index ace1138f9..b2a077e14 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -1604,6 +1604,12 @@ void VulkanDevice::DeferPersistentDescriptorSetDestruction(VkDescriptorSet objec m_cleanup_objects.emplace_back(GetCurrentFenceCounter(), [this, object]() { FreePersistentDescriptorSet(object); }); } +void VulkanDevice::DeferSamplerDestruction(VkSampler object) +{ + m_cleanup_objects.emplace_back(GetCurrentFenceCounter(), + [this, object]() { vkDestroySampler(m_device, object, nullptr); }); +} + VKAPI_ATTR VkBool32 VKAPI_CALL DebugMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, @@ -2064,7 +2070,6 @@ void VulkanDevice::DestroyDevice() m_cleanup_objects.clear(); DestroyPersistentDescriptorSets(); DestroyBuffers(); - DestroySamplers(); DestroyPersistentDescriptorPool(); DestroyPipelineLayouts(); @@ -2790,15 +2795,15 @@ bool VulkanDevice::CreateNullTexture(Error* error) Vulkan::SetObjectName(m_device, m_null_texture->GetView(), "Null texture view"); // Bind null texture and point sampler state to all. - const VkSampler point_sampler = GetSampler(GPUSampler::GetNearestConfig(), error); - if (point_sampler == VK_NULL_HANDLE) + GPUSampler* point_sampler = GetSampler(GPUSampler::GetNearestConfig(), error); + if (!point_sampler) { Error::AddPrefix(error, "Failed to get nearest sampler for init bind: "); return false; } for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++) - m_current_samplers[i] = point_sampler; + m_current_samplers[i] = static_cast(point_sampler)->GetSampler(); return true; } @@ -3568,7 +3573,7 @@ void VulkanDevice::SetInitialPipelineState() void VulkanDevice::SetTextureSampler(u32 slot, GPUTexture* texture, GPUSampler* sampler) { VulkanTexture* T = static_cast(texture); - const VkSampler vsampler = static_cast(sampler ? sampler : m_nearest_sampler.get())->GetSampler(); + const VkSampler vsampler = static_cast(sampler ? sampler : m_nearest_sampler)->GetSampler(); if (m_current_textures[slot] != T || m_current_samplers[slot] != vsampler) { m_current_textures[slot] = T; diff --git a/src/util/vulkan_device.h b/src/util/vulkan_device.h index a7a9f78ba..2445743d6 100644 --- a/src/util/vulkan_device.h +++ b/src/util/vulkan_device.h @@ -222,6 +222,7 @@ public: void DeferPipelineDestruction(VkPipeline object); void DeferBufferViewDestruction(VkBufferView object); void DeferPersistentDescriptorSetDestruction(VkDescriptorSet object); + void DeferSamplerDestruction(VkSampler object); // Wait for a fence to be completed. // Also invokes callbacks for completion. @@ -315,7 +316,6 @@ private: }; using CleanupObjectFunction = void (*)(VulkanDevice& dev, void* obj); - using SamplerMap = std::unordered_map; // Helper method to create a Vulkan instance. static VkInstance CreateVulkanInstance(const WindowInfo& wi, OptionalExtensions* oe, bool enable_debug_utils, @@ -363,8 +363,6 @@ private: void DestroyPipelineLayouts(); bool CreatePersistentDescriptorSets(); void DestroyPersistentDescriptorSets(); - VkSampler GetSampler(const GPUSampler::Config& config, Error* error = nullptr); - void DestroySamplers(); void RenderBlankFrame(VulkanSwapChain* swap_chain); @@ -457,8 +455,6 @@ private: VkDescriptorSet m_ubo_descriptor_set = VK_NULL_HANDLE; u32 m_uniform_buffer_position = 0; - SamplerMap m_sampler_map; - // Which bindings/state has to be updated before the next draw. u32 m_dirty_flags = ALL_DIRTY_STATE; diff --git a/src/util/vulkan_texture.cpp b/src/util/vulkan_texture.cpp index 4e3accaab..61ae293ae 100644 --- a/src/util/vulkan_texture.cpp +++ b/src/util/vulkan_texture.cpp @@ -776,7 +776,7 @@ VulkanSampler::VulkanSampler(VkSampler sampler) : m_sampler(sampler) VulkanSampler::~VulkanSampler() { - // Cleaned up by main class. + VulkanDevice::GetInstance().DeferSamplerDestruction(m_sampler); } #ifdef ENABLE_GPU_OBJECT_NAMES @@ -788,12 +788,8 @@ void VulkanSampler::SetDebugName(std::string_view name) #endif -VkSampler VulkanDevice::GetSampler(const GPUSampler::Config& config, Error* error) +std::unique_ptr VulkanDevice::CreateSampler(const GPUSampler::Config& config, Error* error /* = nullptr */) { - const auto it = m_sampler_map.find(config.key); - if (it != m_sampler_map.end()) - return it->second; - static constexpr std::array(GPUSampler::AddressMode::MaxCount)> ta = {{ VK_SAMPLER_ADDRESS_MODE_REPEAT, // Repeat VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // ClampToEdge @@ -867,29 +863,10 @@ VkSampler VulkanDevice::GetSampler(const GPUSampler::Config& config, Error* erro { LOG_VULKAN_ERROR(res, "vkCreateSampler() failed: "); Vulkan::SetErrorObject(error, "vkCreateSampler() failed: ", res); - } - - m_sampler_map.emplace(config.key, sampler); - return sampler; -} - -void VulkanDevice::DestroySamplers() -{ - for (auto& it : m_sampler_map) - { - if (it.second != VK_NULL_HANDLE) - vkDestroySampler(m_device, it.second, nullptr); - } - m_sampler_map.clear(); -} - -std::unique_ptr VulkanDevice::CreateSampler(const GPUSampler::Config& config, Error* error /* = nullptr */) -{ - const VkSampler vsampler = GetSampler(config, error); - if (vsampler == VK_NULL_HANDLE) return {}; + } - return std::unique_ptr(new VulkanSampler(vsampler)); + return std::unique_ptr(new VulkanSampler(sampler)); } VulkanTextureBuffer::VulkanTextureBuffer(Format format, u32 size_in_elements)