mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-12-24 16:51:52 +01:00
Vulkan: Use cache for sampler objects
This commit is contained in:
parent
6aaad1eb83
commit
b53b223ba9
@ -124,6 +124,7 @@ typedef struct
|
|||||||
LattePerfStatCounter numGraphicPipelines;
|
LattePerfStatCounter numGraphicPipelines;
|
||||||
LattePerfStatCounter numImages;
|
LattePerfStatCounter numImages;
|
||||||
LattePerfStatCounter numImageViews;
|
LattePerfStatCounter numImageViews;
|
||||||
|
LattePerfStatCounter numSamplers;
|
||||||
LattePerfStatCounter numRenderPass;
|
LattePerfStatCounter numRenderPass;
|
||||||
LattePerfStatCounter numFramebuffer;
|
LattePerfStatCounter numFramebuffer;
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ public:
|
|||||||
|
|
||||||
virtual ~VKRMoveableRefCounter()
|
virtual ~VKRMoveableRefCounter()
|
||||||
{
|
{
|
||||||
cemu_assert_debug(refCount == 0);
|
cemu_assert_debug(m_refCount == 0);
|
||||||
|
|
||||||
// remove references
|
// remove references
|
||||||
#ifdef CEMU_DEBUG_ASSERT
|
#ifdef CEMU_DEBUG_ASSERT
|
||||||
@ -30,7 +30,11 @@ public:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
for (auto itr : refs)
|
for (auto itr : refs)
|
||||||
itr->ref->refCount--;
|
{
|
||||||
|
itr->ref->m_refCount--;
|
||||||
|
if (itr->ref->m_refCount == 0)
|
||||||
|
itr->ref->RefCountReachedZero();
|
||||||
|
}
|
||||||
refs.clear();
|
refs.clear();
|
||||||
delete selfRef;
|
delete selfRef;
|
||||||
selfRef = nullptr;
|
selfRef = nullptr;
|
||||||
@ -41,8 +45,8 @@ public:
|
|||||||
VKRMoveableRefCounter(VKRMoveableRefCounter&& rhs) noexcept
|
VKRMoveableRefCounter(VKRMoveableRefCounter&& rhs) noexcept
|
||||||
{
|
{
|
||||||
this->refs = std::move(rhs.refs);
|
this->refs = std::move(rhs.refs);
|
||||||
this->refCount = rhs.refCount;
|
this->m_refCount = rhs.m_refCount;
|
||||||
rhs.refCount = 0;
|
rhs.m_refCount = 0;
|
||||||
this->selfRef = rhs.selfRef;
|
this->selfRef = rhs.selfRef;
|
||||||
rhs.selfRef = nullptr;
|
rhs.selfRef = nullptr;
|
||||||
this->selfRef->ref = this;
|
this->selfRef->ref = this;
|
||||||
@ -57,7 +61,7 @@ public:
|
|||||||
void addRef(VKRMoveableRefCounter* refTarget)
|
void addRef(VKRMoveableRefCounter* refTarget)
|
||||||
{
|
{
|
||||||
this->refs.emplace_back(refTarget->selfRef);
|
this->refs.emplace_back(refTarget->selfRef);
|
||||||
refTarget->refCount++;
|
refTarget->m_refCount++;
|
||||||
|
|
||||||
#ifdef CEMU_DEBUG_ASSERT
|
#ifdef CEMU_DEBUG_ASSERT
|
||||||
// add reverse ref
|
// add reverse ref
|
||||||
@ -68,16 +72,23 @@ public:
|
|||||||
// methods to directly increment/decrement ref counter (for situations where no external object is available)
|
// methods to directly increment/decrement ref counter (for situations where no external object is available)
|
||||||
void incRef()
|
void incRef()
|
||||||
{
|
{
|
||||||
this->refCount++;
|
m_refCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void decRef()
|
void decRef()
|
||||||
{
|
{
|
||||||
this->refCount--;
|
m_refCount--;
|
||||||
|
if (m_refCount == 0)
|
||||||
|
RefCountReachedZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int refCount{};
|
virtual void RefCountReachedZero()
|
||||||
|
{
|
||||||
|
// does nothing by default
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_refCount{};
|
||||||
private:
|
private:
|
||||||
VKRMoveableRefCounterRef* selfRef;
|
VKRMoveableRefCounterRef* selfRef;
|
||||||
std::vector<VKRMoveableRefCounterRef*> refs;
|
std::vector<VKRMoveableRefCounterRef*> refs;
|
||||||
@ -88,7 +99,7 @@ private:
|
|||||||
void moveObj(VKRMoveableRefCounter&& rhs)
|
void moveObj(VKRMoveableRefCounter&& rhs)
|
||||||
{
|
{
|
||||||
this->refs = std::move(rhs.refs);
|
this->refs = std::move(rhs.refs);
|
||||||
this->refCount = rhs.refCount;
|
this->m_refCount = rhs.m_refCount;
|
||||||
this->selfRef = rhs.selfRef;
|
this->selfRef = rhs.selfRef;
|
||||||
this->selfRef->ref = this;
|
this->selfRef->ref = this;
|
||||||
}
|
}
|
||||||
@ -131,6 +142,25 @@ public:
|
|||||||
VkSampler m_textureDefaultSampler[2] = { VK_NULL_HANDLE, VK_NULL_HANDLE }; // relict from LatteTextureViewVk, get rid of it eventually
|
VkSampler m_textureDefaultSampler[2] = { VK_NULL_HANDLE, VK_NULL_HANDLE }; // relict from LatteTextureViewVk, get rid of it eventually
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class VKRObjectSampler : public VKRDestructibleObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VKRObjectSampler(VkSamplerCreateInfo* samplerInfo);
|
||||||
|
~VKRObjectSampler() override;
|
||||||
|
|
||||||
|
static VKRObjectSampler* GetOrCreateSampler(VkSamplerCreateInfo* samplerInfo);
|
||||||
|
static void DestroyCache();
|
||||||
|
|
||||||
|
void RefCountReachedZero() override; // sampler objects are destroyed when not referenced anymore
|
||||||
|
|
||||||
|
VkSampler GetSampler() const { return m_sampler; }
|
||||||
|
private:
|
||||||
|
static std::unordered_map<uint64, VKRObjectSampler*> s_samplerCache;
|
||||||
|
VkSampler m_sampler{ VK_NULL_HANDLE };
|
||||||
|
uint64 m_hash;
|
||||||
|
};
|
||||||
|
|
||||||
class VKRObjectRenderPass : public VKRDestructibleObject
|
class VKRObjectRenderPass : public VKRDestructibleObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -672,6 +672,8 @@ VulkanRenderer::~VulkanRenderer()
|
|||||||
if (m_commandPool != VK_NULL_HANDLE)
|
if (m_commandPool != VK_NULL_HANDLE)
|
||||||
vkDestroyCommandPool(m_logicalDevice, m_commandPool, nullptr);
|
vkDestroyCommandPool(m_logicalDevice, m_commandPool, nullptr);
|
||||||
|
|
||||||
|
VKRObjectSampler::DestroyCache();
|
||||||
|
|
||||||
// destroy debug callback
|
// destroy debug callback
|
||||||
if (m_debugCallback)
|
if (m_debugCallback)
|
||||||
{
|
{
|
||||||
@ -3707,6 +3709,7 @@ void VulkanRenderer::AppendOverlayDebugInfo()
|
|||||||
ImGui::Text("DS StorageBuf %u", performanceMonitor.vk.numDescriptorStorageBuffers.get());
|
ImGui::Text("DS StorageBuf %u", performanceMonitor.vk.numDescriptorStorageBuffers.get());
|
||||||
ImGui::Text("Images %u", performanceMonitor.vk.numImages.get());
|
ImGui::Text("Images %u", performanceMonitor.vk.numImages.get());
|
||||||
ImGui::Text("ImageView %u", performanceMonitor.vk.numImageViews.get());
|
ImGui::Text("ImageView %u", performanceMonitor.vk.numImageViews.get());
|
||||||
|
ImGui::Text("ImageSampler %u", performanceMonitor.vk.numSamplers.get());
|
||||||
ImGui::Text("RenderPass %u", performanceMonitor.vk.numRenderPass.get());
|
ImGui::Text("RenderPass %u", performanceMonitor.vk.numRenderPass.get());
|
||||||
ImGui::Text("Framebuffer %u", performanceMonitor.vk.numFramebuffer.get());
|
ImGui::Text("Framebuffer %u", performanceMonitor.vk.numFramebuffer.get());
|
||||||
m_spinlockDestructionQueue.lock();
|
m_spinlockDestructionQueue.lock();
|
||||||
@ -3752,7 +3755,7 @@ void VKRDestructibleObject::flagForCurrentCommandBuffer()
|
|||||||
|
|
||||||
bool VKRDestructibleObject::canDestroy()
|
bool VKRDestructibleObject::canDestroy()
|
||||||
{
|
{
|
||||||
if (refCount > 0)
|
if (m_refCount > 0)
|
||||||
return false;
|
return false;
|
||||||
return VulkanRenderer::GetInstance()->HasCommandBufferFinished(m_lastCmdBufferId);
|
return VulkanRenderer::GetInstance()->HasCommandBufferFinished(m_lastCmdBufferId);
|
||||||
}
|
}
|
||||||
@ -3793,6 +3796,111 @@ VKRObjectTextureView::~VKRObjectTextureView()
|
|||||||
performanceMonitor.vk.numImageViews.decrement();
|
performanceMonitor.vk.numImageViews.decrement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64 CalcHashSamplerCreateInfo(const VkSamplerCreateInfo& info)
|
||||||
|
{
|
||||||
|
uint64 h = 0xcbf29ce484222325ULL;
|
||||||
|
auto fnvHashCombine = [](uint64_t &h, auto val) {
|
||||||
|
using T = decltype(val);
|
||||||
|
static_assert(sizeof(T) <= 8);
|
||||||
|
uint64_t val64 = 0;
|
||||||
|
std::memcpy(&val64, &val, sizeof(val));
|
||||||
|
h ^= val64;
|
||||||
|
h *= 0x100000001b3ULL;
|
||||||
|
};
|
||||||
|
cemu_assert_debug(info.sType == VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO);
|
||||||
|
fnvHashCombine(h, info.flags);
|
||||||
|
fnvHashCombine(h, info.magFilter);
|
||||||
|
fnvHashCombine(h, info.minFilter);
|
||||||
|
fnvHashCombine(h, info.mipmapMode);
|
||||||
|
fnvHashCombine(h, info.addressModeU);
|
||||||
|
fnvHashCombine(h, info.addressModeV);
|
||||||
|
fnvHashCombine(h, info.addressModeW);
|
||||||
|
fnvHashCombine(h, info.mipLodBias);
|
||||||
|
fnvHashCombine(h, info.anisotropyEnable);
|
||||||
|
if(info.anisotropyEnable == VK_TRUE)
|
||||||
|
fnvHashCombine(h, info.maxAnisotropy);
|
||||||
|
fnvHashCombine(h, info.compareEnable);
|
||||||
|
if(info.compareEnable == VK_TRUE)
|
||||||
|
fnvHashCombine(h, info.compareOp);
|
||||||
|
fnvHashCombine(h, info.minLod);
|
||||||
|
fnvHashCombine(h, info.maxLod);
|
||||||
|
fnvHashCombine(h, info.borderColor);
|
||||||
|
fnvHashCombine(h, info.unnormalizedCoordinates);
|
||||||
|
// handle custom border color
|
||||||
|
VkBaseOutStructure* ext = (VkBaseOutStructure*)info.pNext;
|
||||||
|
while(ext)
|
||||||
|
{
|
||||||
|
if(ext->sType == VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT)
|
||||||
|
{
|
||||||
|
auto* extInfo = (VkSamplerCustomBorderColorCreateInfoEXT*)ext;
|
||||||
|
fnvHashCombine(h, extInfo->customBorderColor.uint32[0]);
|
||||||
|
fnvHashCombine(h, extInfo->customBorderColor.uint32[1]);
|
||||||
|
fnvHashCombine(h, extInfo->customBorderColor.uint32[2]);
|
||||||
|
fnvHashCombine(h, extInfo->customBorderColor.uint32[3]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cemu_assert_unimplemented();
|
||||||
|
}
|
||||||
|
ext = ext->pNext;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<uint64, VKRObjectSampler*> VKRObjectSampler::s_samplerCache;
|
||||||
|
|
||||||
|
VKRObjectSampler::VKRObjectSampler(VkSamplerCreateInfo* samplerInfo)
|
||||||
|
{
|
||||||
|
auto* vulkanRenderer = VulkanRenderer::GetInstance();
|
||||||
|
if (vkCreateSampler(vulkanRenderer->GetLogicalDevice(), samplerInfo, nullptr, &m_sampler) != VK_SUCCESS)
|
||||||
|
vulkanRenderer->UnrecoverableError("Failed to create texture sampler");
|
||||||
|
performanceMonitor.vk.numSamplers.increment();
|
||||||
|
m_hash = CalcHashSamplerCreateInfo(*samplerInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
VKRObjectSampler::~VKRObjectSampler()
|
||||||
|
{
|
||||||
|
vkDestroySampler(VulkanRenderer::GetInstance()->GetLogicalDevice(), m_sampler, nullptr);
|
||||||
|
performanceMonitor.vk.numSamplers.decrement();
|
||||||
|
// remove from cache
|
||||||
|
auto it = s_samplerCache.find(m_hash);
|
||||||
|
if(it != s_samplerCache.end())
|
||||||
|
s_samplerCache.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VKRObjectSampler::RefCountReachedZero()
|
||||||
|
{
|
||||||
|
VulkanRenderer::GetInstance()->ReleaseDestructibleObject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
VKRObjectSampler* VKRObjectSampler::GetOrCreateSampler(VkSamplerCreateInfo* samplerInfo)
|
||||||
|
{
|
||||||
|
auto* vulkanRenderer = VulkanRenderer::GetInstance();
|
||||||
|
uint64 hash = CalcHashSamplerCreateInfo(*samplerInfo);
|
||||||
|
auto it = s_samplerCache.find(hash);
|
||||||
|
if (it != s_samplerCache.end())
|
||||||
|
{
|
||||||
|
auto* sampler = it->second;
|
||||||
|
return sampler;
|
||||||
|
}
|
||||||
|
auto* sampler = new VKRObjectSampler(samplerInfo);
|
||||||
|
s_samplerCache[hash] = sampler;
|
||||||
|
return sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VKRObjectSampler::DestroyCache()
|
||||||
|
{
|
||||||
|
// assuming all other objects which depend on vkSampler are destroyed, this cache should also have been emptied already
|
||||||
|
// but just to be sure lets still clear the cache
|
||||||
|
cemu_assert_debug(s_samplerCache.empty());
|
||||||
|
for(auto& sampler : s_samplerCache)
|
||||||
|
{
|
||||||
|
cemu_assert_debug(sampler.second->m_refCount == 0);
|
||||||
|
delete sampler.second;
|
||||||
|
}
|
||||||
|
s_samplerCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
VKRObjectRenderPass::VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint32 colorAttachmentCount)
|
VKRObjectRenderPass::VKRObjectRenderPass(AttachmentInfo_t& attachmentInfo, sint32 colorAttachmentCount)
|
||||||
{
|
{
|
||||||
// generate helper hash for pipeline state
|
// generate helper hash for pipeline state
|
||||||
|
@ -727,7 +727,6 @@ VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo*
|
|||||||
|
|
||||||
VkSamplerCustomBorderColorCreateInfoEXT samplerCustomBorderColor{};
|
VkSamplerCustomBorderColorCreateInfoEXT samplerCustomBorderColor{};
|
||||||
|
|
||||||
VkSampler sampler;
|
|
||||||
VkSamplerCreateInfo samplerInfo{};
|
VkSamplerCreateInfo samplerInfo{};
|
||||||
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||||
|
|
||||||
@ -899,10 +898,9 @@ VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
VKRObjectSampler* samplerObj = VKRObjectSampler::GetOrCreateSampler(&samplerInfo);
|
||||||
if (vkCreateSampler(m_logicalDevice, &samplerInfo, nullptr, &sampler) != VK_SUCCESS)
|
vkObjDS->addRef(samplerObj);
|
||||||
UnrecoverableError("Failed to create texture sampler");
|
info.sampler = samplerObj->GetSampler();
|
||||||
info.sampler = sampler;
|
|
||||||
textureArray.emplace_back(info);
|
textureArray.emplace_back(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user