GPUDevice: Add sampler cache to base class

Removes per-backend bookkeeping in D3D12 and Vulkan.
This commit is contained in:
Stenzek 2025-01-18 14:55:15 +10:00
parent da13579356
commit 609fa5c7d7
No known key found for this signature in database
8 changed files with 66 additions and 86 deletions

View File

@ -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<D3D12Sampler*>(default_sampler)->GetDescriptor();
return true;
}
@ -2123,7 +2122,7 @@ void D3D12Device::SetTextureSampler(u32 slot, GPUTexture* texture, GPUSampler* s
}
const D3D12DescriptorHandle& handle =
sampler ? static_cast<D3D12Sampler*>(sampler)->GetDescriptor() : m_point_sampler;
static_cast<D3D12Sampler*>(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<D3D12Sampler*>(m_linear_sampler.get())->GetDescriptor());
cmdlist->SetGraphicsRootDescriptorTable(1, static_cast<D3D12Sampler*>(m_linear_sampler)->GetDescriptor());
cmdlist->DrawInstanced(3, 1, 0, 0);
cmdlist->EndRenderPass();

View File

@ -246,8 +246,6 @@ private:
};
static_assert(sizeof(PIPELINE_CACHE_HEADER) == 16);
using SamplerMap = std::unordered_map<u64, D3D12DescriptorHandle>;
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<ID3D12QueryHeap> m_timestamp_query_heap;
ComPtr<ID3D12Resource> 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<ID3D12PipelineLibrary> m_pipeline_library;
// Which bindings/state has to be updated before the next draw.

View File

@ -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<GPUSampler> 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<D3D12_TEXTURE_ADDRESS_MODE, static_cast<u8>(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<GPUSampler>(new D3D12Sampler(handle));
}
m_sampler_map.clear();
}
std::unique_ptr<GPUSampler> 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<GPUSampler>(new D3D12Sampler(handle));
}
}
D3D12TextureBuffer::D3D12TextureBuffer(Format format, u32 size_in_elements) : GPUTextureBuffer(format, size_in_elements)

View File

@ -650,12 +650,14 @@ bool GPUDevice::GetPipelineCacheData(DynamicHeapArray<u8>* 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<GPUTexture*>(pcmd->TextureId), m_linear_sampler.get());
SetTextureSampler(0, reinterpret_cast<GPUTexture*>(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<GPUSampler> 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);

View File

@ -716,8 +716,8 @@ public:
ALWAYS_INLINE GPUSwapChain* GetMainSwapChain() const { return m_main_swap_chain.get(); }
ALWAYS_INLINE bool HasMainSwapChain() const { return static_cast<bool>(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<GPUTextureBuffer> 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<GPUTexture> 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<GPUSwapChain> m_main_swap_chain;
GPUSampler* m_nearest_sampler = nullptr;
GPUSampler* m_linear_sampler = nullptr;
GPUShaderCache m_shader_cache;
std::unique_ptr<GPUSampler> m_nearest_sampler;
std::unique_ptr<GPUSampler> m_linear_sampler;
private:
static constexpr u32 MAX_TEXTURE_POOL_SIZE = 125;
@ -957,6 +959,7 @@ private:
};
using TexturePool = std::deque<TexturePoolEntry>;
using SamplerMap = std::unordered_map<u64, std::unique_ptr<GPUSampler>>;
#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<GPUPipeline> m_imgui_pipeline;
std::unique_ptr<GPUTexture> 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;

View File

@ -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<VulkanSampler*>(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<VulkanTexture*>(texture);
const VkSampler vsampler = static_cast<VulkanSampler*>(sampler ? sampler : m_nearest_sampler.get())->GetSampler();
const VkSampler vsampler = static_cast<VulkanSampler*>(sampler ? sampler : m_nearest_sampler)->GetSampler();
if (m_current_textures[slot] != T || m_current_samplers[slot] != vsampler)
{
m_current_textures[slot] = T;

View File

@ -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<u64, VkSampler>;
// 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;

View File

@ -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<GPUSampler> 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<VkSamplerAddressMode, static_cast<u8>(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<GPUSampler> 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<GPUSampler>(new VulkanSampler(vsampler));
return std::unique_ptr<GPUSampler>(new VulkanSampler(sampler));
}
VulkanTextureBuffer::VulkanTextureBuffer(Format format, u32 size_in_elements)