Files
strato/app/src/main/cpp/skyline/gpu/cache/renderpass_cache.cpp
PixelyIon 47bc3b4d99 Fix Render Pass Cache
The Vulkan render pass cache was fundamentally broken since it was designed around the Render Pass Compatibility clause due to being designed for framebuffer compatibility initially. As this scope was extended to a general render pass cache, the amount of data in the key was not extended to include everything it should have. This commit introduces the missing pieces in the RP cache and simplifies the underlying code in the process.
2022-05-01 20:31:36 +05:30

175 lines
7.6 KiB
C++

// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <boost/functional/hash.hpp>
#include <gpu.h>
#include "renderpass_cache.h"
namespace skyline::gpu::cache {
RenderPassCache::RenderPassCache(gpu::GPU &gpu) : gpu{gpu} {}
#define VEC_CPY(pointer, size) description.pointer, description.pointer + description.size
RenderPassCache::SubpassDescription::SubpassDescription(const vk::SubpassDescription &description)
: flags{description.flags},
pipelineBindPoint{description.pipelineBindPoint},
inputAttachments{VEC_CPY(pInputAttachments, inputAttachmentCount)},
colorAttachments{VEC_CPY(pColorAttachments, colorAttachmentCount)},
resolveAttachments{description.pResolveAttachments, description.pResolveAttachments + (description.pResolveAttachments ? description.colorAttachmentCount : 0)},
depthStencilAttachment{description.pDepthStencilAttachment ? *description.pDepthStencilAttachment : std::optional<vk::AttachmentReference>{}},
preserveAttachments{VEC_CPY(pPreserveAttachments, preserveAttachmentCount)} {}
#undef VEC_CPY
RenderPassCache::RenderPassMetadata::RenderPassMetadata(const vk::RenderPassCreateInfo &createInfo)
: attachments{createInfo.pAttachments, createInfo.pAttachments + createInfo.attachmentCount},
subpasses{createInfo.pSubpasses, createInfo.pSubpasses + createInfo.subpassCount} {}
#define HASH(x) boost::hash_combine(hash, x)
size_t RenderPassCache::RenderPassHash::operator()(const RenderPassMetadata &key) const {
size_t hash{};
HASH(key.attachments.size());
for (const auto &attachment : key.attachments) {
HASH(static_cast<VkAttachmentDescriptionFlags>(attachment.flags));
HASH(attachment.format);
HASH(attachment.samples);
HASH(attachment.loadOp);
HASH(attachment.storeOp);
HASH(attachment.stencilLoadOp);
HASH(attachment.stencilStoreOp);
HASH(attachment.initialLayout);
HASH(attachment.finalLayout);
}
HASH(key.subpasses.size());
for (const auto &subpass : key.subpasses) {
HASH(static_cast<VkSubpassDescriptionFlags>(subpass.flags));
HASH(subpass.pipelineBindPoint);
auto hashReferences{[&hash](const auto &references) {
HASH(references.size());
for (const auto &reference : references) {
HASH(reference.attachment);
HASH(reference.layout);
}
}};
hashReferences(subpass.inputAttachments);
hashReferences(subpass.colorAttachments);
hashReferences(subpass.resolveAttachments);
HASH(subpass.depthStencilAttachment.has_value());
if (subpass.depthStencilAttachment) {
HASH(subpass.depthStencilAttachment->attachment);
HASH(subpass.depthStencilAttachment->layout);
}
HASH(subpass.preserveAttachments.size());
for (const auto &index : subpass.preserveAttachments)
HASH(index);
}
return hash;
}
size_t RenderPassCache::RenderPassHash::operator()(const vk::RenderPassCreateInfo &key) const {
size_t hash{};
HASH(key.attachmentCount);
for (const auto &attachment : span<const vk::AttachmentDescription>{key.pAttachments, key.attachmentCount}) {
HASH(static_cast<VkAttachmentDescriptionFlags>(attachment.flags));
HASH(attachment.format);
HASH(attachment.samples);
HASH(attachment.loadOp);
HASH(attachment.storeOp);
HASH(attachment.stencilLoadOp);
HASH(attachment.stencilStoreOp);
HASH(attachment.initialLayout);
HASH(attachment.finalLayout);
}
HASH(key.subpassCount);
for (const auto &subpass : span<const vk::SubpassDescription>{key.pSubpasses, key.subpassCount}) {
HASH(static_cast<VkSubpassDescriptionFlags>(subpass.flags));
HASH(subpass.pipelineBindPoint);
auto hashReferences{[&hash](const vk::AttachmentReference *data, u32 count) {
HASH(count);
for (const auto &reference : span<const vk::AttachmentReference>{data, count}) {
HASH(reference.attachment);
HASH(reference.layout);
}
}};
hashReferences(subpass.pInputAttachments, subpass.inputAttachmentCount);
hashReferences(subpass.pColorAttachments, subpass.colorAttachmentCount);
if (subpass.pResolveAttachments)
hashReferences(subpass.pResolveAttachments, subpass.colorAttachmentCount);
HASH(subpass.pDepthStencilAttachment != nullptr);
if (subpass.pDepthStencilAttachment) {
HASH(subpass.pDepthStencilAttachment->attachment);
HASH(subpass.pDepthStencilAttachment->layout);
}
HASH(subpass.preserveAttachmentCount);
for (const auto &index : span<const u32>{subpass.pPreserveAttachments, subpass.preserveAttachmentCount})
HASH(index);
}
return hash;
}
#undef HASH
bool RenderPassCache::RenderPassEqual::operator()(const RenderPassMetadata &lhs, const RenderPassMetadata &rhs) const {
return lhs == rhs;
}
bool RenderPassCache::RenderPassEqual::operator()(const RenderPassMetadata &lhs, const vk::RenderPassCreateInfo &rhs) const {
#define RETF(condition) if (condition) { return false; }
#define RETARRNEQ(array, pointer, count) RETF(!std::equal(array.begin(), array.end(), pointer, pointer + count))
RETARRNEQ(lhs.attachments, rhs.pAttachments, rhs.attachmentCount)
RETF(lhs.subpasses.size() != rhs.subpassCount)
const vk::SubpassDescription *vkSubpass{rhs.pSubpasses};
for (const auto &subpass : lhs.subpasses) {
RETF(subpass.flags != vkSubpass->flags)
RETF(subpass.pipelineBindPoint != vkSubpass->pipelineBindPoint)
RETARRNEQ(subpass.inputAttachments, vkSubpass->pInputAttachments, vkSubpass->inputAttachmentCount)
RETARRNEQ(subpass.colorAttachments, vkSubpass->pColorAttachments, vkSubpass->colorAttachmentCount)
RETF(subpass.resolveAttachments.size() != (vkSubpass->pResolveAttachments ? vkSubpass->colorAttachmentCount : 0))
if (!subpass.resolveAttachments.empty())
RETARRNEQ(subpass.resolveAttachments, vkSubpass->pResolveAttachments, vkSubpass->colorAttachmentCount)
RETF(subpass.depthStencilAttachment.has_value() != (vkSubpass->pDepthStencilAttachment != nullptr))
if (subpass.depthStencilAttachment)
RETF(subpass.depthStencilAttachment->attachment != vkSubpass->pDepthStencilAttachment->attachment &&
subpass.depthStencilAttachment->layout != vkSubpass->pDepthStencilAttachment->layout)
RETARRNEQ(subpass.preserveAttachments, vkSubpass->pPreserveAttachments, vkSubpass->preserveAttachmentCount)
vkSubpass++;
}
#undef RETF
return true;
}
vk::RenderPass RenderPassCache::GetRenderPass(const vk::RenderPassCreateInfo &createInfo) {
std::scoped_lock lock{mutex};
auto it{renderPassCache.find(createInfo)};
if (it != renderPassCache.end())
return *it->second;
auto entryIt{renderPassCache.try_emplace(RenderPassMetadata{createInfo}, gpu.vkDevice, createInfo)};
return *entryIt.first->second;
}
}