diff --git a/include/Nazara/Renderer/RenderPassCache.hpp b/include/Nazara/Renderer/RenderPassCache.hpp new file mode 100644 index 000000000..cdb11ce74 --- /dev/null +++ b/include/Nazara/Renderer/RenderPassCache.hpp @@ -0,0 +1,70 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_RENDERPASSCACHE_HPP +#define NAZARA_RENDERPASSCACHE_HPP + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class RenderDevice; + + class NAZARA_RENDERER_API RenderPassCache + { + public: + inline RenderPassCache(RenderDevice& device); + RenderPassCache(const RenderPassCache&) = delete; + RenderPassCache(RenderPassCache&&) noexcept = default; + ~RenderPassCache() = default; + + const std::shared_ptr& Get(const std::vector& attachments, const std::vector& subpassDescriptions, const std::vector& subpassDependencies) const; + + RenderPassCache& operator=(const RenderPassCache&) = delete; + RenderPassCache& operator=(RenderPassCache&&) noexcept = default; + + private: + struct RenderPassData + { + std::size_t attachmentCount; + std::size_t dependencyCount; + std::size_t descriptionCount; + const RenderPass::Attachment* attachments; + const RenderPass::SubpassDependency* subpassDependencies; + const RenderPass::SubpassDescription* subpassDescriptions; + }; + + using Key = std::variant, RenderPassData>; + + struct Hasher + { + template std::size_t operator()(const T& renderPass) const; + std::size_t operator()(const RenderPassData& renderPassData) const; + }; + + struct EqualityChecker + { + template bool operator()(const T1& lhs, const T2& rhs) const; + bool operator()(const RenderPassData& lhs, const RenderPassData& rhs) const; + }; + + static inline auto ToRenderPassData(const Key& key); + static inline RenderPassData ToRenderPassData(const std::shared_ptr& renderPass); + static inline const RenderPassData& ToRenderPassData(const RenderPassData& renderPassData); + + mutable std::unordered_map, Hasher, EqualityChecker> m_renderPasses; + RenderDevice& m_device; + }; +} + +#include + +#endif // NAZARA_RENDERPASS_HPP diff --git a/include/Nazara/Renderer/RenderPassCache.inl b/include/Nazara/Renderer/RenderPassCache.inl new file mode 100644 index 000000000..89d825078 --- /dev/null +++ b/include/Nazara/Renderer/RenderPassCache.inl @@ -0,0 +1,61 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Utility module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + inline RenderPassCache::RenderPassCache(RenderDevice& device) : + m_device(device) + { + } + + template + std::size_t RenderPassCache::Hasher::operator()(const T& key) const + { + return operator()(ToRenderPassData(key)); + } + + template + inline bool RenderPassCache::EqualityChecker::operator()(const T1& lhs, const T2& rhs) const + { + return operator()(ToRenderPassData(lhs), ToRenderPassData(rhs)); + } + + inline auto RenderPassCache::ToRenderPassData(const Key& key) + { + return std::visit([&](auto&& arg) + { + return ToRenderPassData(arg); + }, key); + } + + inline auto RenderPassCache::ToRenderPassData(const std::shared_ptr& renderPass) -> RenderPassData + { + const auto& attachments = renderPass->GetAttachments(); + const auto& subpassDeps = renderPass->GetSubpassDependencies(); + const auto& subpassDesc = renderPass->GetSubpassDescriptions(); + + RenderPassData data; + data.attachmentCount = attachments.size(); + data.attachments = attachments.data(); + + data.dependencyCount = subpassDeps.size(); + data.subpassDependencies = subpassDeps.data(); + + data.descriptionCount = subpassDesc.size(); + data.subpassDescriptions = subpassDesc.data(); + + return data; + } + + inline auto RenderPassCache::ToRenderPassData(const RenderPassData& renderPassData) -> const RenderPassData& + { + return renderPassData; + } +} + +#include diff --git a/src/Nazara/Renderer/RenderPassCache.cpp b/src/Nazara/Renderer/RenderPassCache.cpp new file mode 100644 index 000000000..08ff638d5 --- /dev/null +++ b/src/Nazara/Renderer/RenderPassCache.cpp @@ -0,0 +1,159 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz +{ + const std::shared_ptr& RenderPassCache::Get(const std::vector& attachments, const std::vector& subpassDescriptions, const std::vector& subpassDependencies) const + { + RenderPassData data; + data.attachmentCount = attachments.size(); + data.attachments = attachments.data(); + + data.dependencyCount = subpassDependencies.size(); + data.subpassDependencies = subpassDependencies.data(); + + data.descriptionCount = subpassDescriptions.size(); + data.subpassDescriptions = subpassDescriptions.data(); + + auto it = m_renderPasses.find(data); + if (it != m_renderPasses.end()) + return it->second; + + std::shared_ptr renderPass = m_device.InstantiateRenderPass(attachments, subpassDescriptions, subpassDependencies); + + return m_renderPasses.emplace(renderPass, renderPass).first->second; + } + + std::size_t RenderPassCache::Hasher::operator()(const RenderPassData& renderPassData) const + { + std::size_t seed = 0; + for (std::size_t i = 0; i < renderPassData.attachmentCount; ++i) + { + const auto& attachment = renderPassData.attachments[i]; + + HashCombine(seed, attachment.format); + HashCombine(seed, attachment.loadOp); + HashCombine(seed, attachment.stencilLoadOp); + HashCombine(seed, attachment.storeOp); + HashCombine(seed, attachment.stencilStoreOp); + HashCombine(seed, attachment.initialLayout); + HashCombine(seed, attachment.finalLayout); + } + + for (std::size_t i = 0; i < renderPassData.descriptionCount; ++i) + { + const auto& subpassDesc = renderPassData.subpassDescriptions[i]; + + auto CombineAttachment = [&](const RenderPass::AttachmentReference& attachmentReference) + { + HashCombine(seed, attachmentReference.attachmentIndex); + HashCombine(seed, attachmentReference.attachmentLayout); + }; + + for (const auto& colorAttachment : subpassDesc.colorAttachment) + CombineAttachment(colorAttachment); + + for (const auto& inputAttachment : subpassDesc.inputAttachments) + CombineAttachment(inputAttachment); + + if (subpassDesc.depthStencilAttachment) + CombineAttachment(*subpassDesc.depthStencilAttachment); + + for (const auto& attachmentIndex : subpassDesc.preserveAttachments) + HashCombine(seed, attachmentIndex); + } + + for (std::size_t i = 0; i < renderPassData.dependencyCount; ++i) + { + const auto& subpassDep = renderPassData.subpassDependencies[i]; + + HashCombine(seed, subpassDep.fromSubpassIndex); + HashCombine(seed, subpassDep.fromStages); + HashCombine(seed, subpassDep.fromAccessFlags); + + HashCombine(seed, subpassDep.toSubpassIndex); + HashCombine(seed, subpassDep.toStages); + HashCombine(seed, subpassDep.toAccessFlags); + + HashCombine(seed, subpassDep.tilable); + } + + return seed; + } + + bool RenderPassCache::EqualityChecker::operator()(const RenderPassData& lhs, const RenderPassData& rhs) const + { + if (lhs.attachmentCount != rhs.attachmentCount || + lhs.dependencyCount != rhs.dependencyCount || + lhs.descriptionCount != rhs.descriptionCount) + return false; + + for (std::size_t i = 0; i < lhs.attachmentCount; ++i) + { + const auto& lhsAttachment = lhs.attachments[i]; + const auto& rhsAttachment = rhs.attachments[i]; + + if (lhsAttachment.format != rhsAttachment.format || + lhsAttachment.loadOp != rhsAttachment.loadOp || + lhsAttachment.stencilLoadOp != rhsAttachment.stencilLoadOp || + lhsAttachment.storeOp != rhsAttachment.storeOp || + lhsAttachment.stencilStoreOp != rhsAttachment.stencilStoreOp || + lhsAttachment.initialLayout != rhsAttachment.initialLayout || + lhsAttachment.finalLayout != rhsAttachment.finalLayout) + return false; + } + + for (std::size_t i = 0; i < lhs.dependencyCount; ++i) + { + const auto& lhsDependency = lhs.subpassDependencies[i]; + const auto& rhsDependency = rhs.subpassDependencies[i]; + + if (lhsDependency.fromSubpassIndex != rhsDependency.fromSubpassIndex || + lhsDependency.fromStages != rhsDependency.fromStages || + lhsDependency.fromAccessFlags != rhsDependency.fromAccessFlags || + lhsDependency.toSubpassIndex != rhsDependency.toSubpassIndex || + lhsDependency.toStages != rhsDependency.toStages || + lhsDependency.toAccessFlags != rhsDependency.toAccessFlags || + lhsDependency.tilable != rhsDependency.tilable) + return false; + } + + for (std::size_t i = 0; i < lhs.descriptionCount; ++i) + { + const auto& lhsSubpassDesc = lhs.subpassDescriptions[i]; + const auto& rhsSubpassDesc = rhs.subpassDescriptions[i]; + + auto CompareAttachments = [&](const RenderPass::AttachmentReference& lhsAttachmentReference, const RenderPass::AttachmentReference& rhsAttachmentReference) + { + if (lhsAttachmentReference.attachmentIndex != rhsAttachmentReference.attachmentIndex) + return false; + + if (lhsAttachmentReference.attachmentLayout != rhsAttachmentReference.attachmentLayout) + return false; + + return true; + }; + + if (std::equal(lhsSubpassDesc.colorAttachment.begin(), lhsSubpassDesc.colorAttachment.end(), rhsSubpassDesc.colorAttachment.begin(), CompareAttachments) && + !std::equal(lhsSubpassDesc.inputAttachments.begin(), lhsSubpassDesc.inputAttachments.end(), rhsSubpassDesc.inputAttachments.begin(), CompareAttachments)) + return false; + + if (lhsSubpassDesc.depthStencilAttachment.has_value() != rhsSubpassDesc.depthStencilAttachment.has_value()) + return false; + + if (lhsSubpassDesc.depthStencilAttachment.has_value() && CompareAttachments(*lhsSubpassDesc.depthStencilAttachment, *rhsSubpassDesc.depthStencilAttachment)) + return false; + + if (lhsSubpassDesc.preserveAttachments != rhsSubpassDesc.preserveAttachments) + return false; + } + + return true; + } +}