From 5aeff9aff5c8d9aa21d839560744af883b877b99 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Sun, 5 Jan 2020 17:32:08 -0300 Subject: [PATCH] vk_renderpass_cache: Initial implementation The renderpass cache is used to avoid creating renderpasses on each draw. The hashed structure is not currently optimized. --- src/video_core/CMakeLists.txt | 2 + .../renderer_vulkan/vk_renderpass_cache.cpp | 100 ++++++++++++++++++ .../renderer_vulkan/vk_renderpass_cache.h | 97 +++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 src/video_core/renderer_vulkan/vk_renderpass_cache.cpp create mode 100644 src/video_core/renderer_vulkan/vk_renderpass_cache.h diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 47290cbcb5..c80171fe60 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -163,6 +163,8 @@ if (ENABLE_VULKAN) renderer_vulkan/vk_image.h renderer_vulkan/vk_memory_manager.cpp renderer_vulkan/vk_memory_manager.h + renderer_vulkan/vk_renderpass_cache.cpp + renderer_vulkan/vk_renderpass_cache.h renderer_vulkan/vk_resource_manager.cpp renderer_vulkan/vk_resource_manager.h renderer_vulkan/vk_sampler_cache.cpp diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp new file mode 100644 index 0000000000..93f5d7ba0f --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp @@ -0,0 +1,100 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include + +#include "video_core/engines/maxwell_3d.h" +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/renderer_vulkan/maxwell_to_vk.h" +#include "video_core/renderer_vulkan/vk_device.h" +#include "video_core/renderer_vulkan/vk_renderpass_cache.h" + +namespace Vulkan { + +VKRenderPassCache::VKRenderPassCache(const VKDevice& device) : device{device} {} + +VKRenderPassCache::~VKRenderPassCache() = default; + +vk::RenderPass VKRenderPassCache::GetRenderPass(const RenderPassParams& params) { + const auto [pair, is_cache_miss] = cache.try_emplace(params); + auto& entry = pair->second; + if (is_cache_miss) { + entry = CreateRenderPass(params); + } + return *entry; +} + +UniqueRenderPass VKRenderPassCache::CreateRenderPass(const RenderPassParams& params) const { + std::vector descriptors; + std::vector color_references; + + for (std::size_t rt = 0; rt < params.color_attachments.size(); ++rt) { + const auto attachment = params.color_attachments[rt]; + const auto format = + MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, attachment.pixel_format); + ASSERT_MSG(format.attachable, "Trying to attach a non-attachable format with format={}", + static_cast(attachment.pixel_format)); + + // TODO(Rodrigo): Add eMayAlias when it's needed. + const auto color_layout = attachment.is_texception + ? vk::ImageLayout::eGeneral + : vk::ImageLayout::eColorAttachmentOptimal; + descriptors.emplace_back(vk::AttachmentDescriptionFlagBits::eMayAlias, format.format, + vk::SampleCountFlagBits::e1, vk::AttachmentLoadOp::eLoad, + vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, + vk::AttachmentStoreOp::eDontCare, color_layout, color_layout); + color_references.emplace_back(static_cast(rt), color_layout); + } + + vk::AttachmentReference zeta_attachment_ref; + if (params.has_zeta) { + const auto format = + MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, params.zeta_pixel_format); + ASSERT_MSG(format.attachable, "Trying to attach a non-attachable format with format={}", + static_cast(params.zeta_pixel_format)); + + const auto zeta_layout = params.zeta_texception + ? vk::ImageLayout::eGeneral + : vk::ImageLayout::eDepthStencilAttachmentOptimal; + descriptors.emplace_back(vk::AttachmentDescriptionFlags{}, format.format, + vk::SampleCountFlagBits::e1, vk::AttachmentLoadOp::eLoad, + vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eLoad, + vk::AttachmentStoreOp::eStore, zeta_layout, zeta_layout); + zeta_attachment_ref = + vk::AttachmentReference(static_cast(params.color_attachments.size()), zeta_layout); + } + + const vk::SubpassDescription subpass_description( + {}, vk::PipelineBindPoint::eGraphics, 0, nullptr, static_cast(color_references.size()), + color_references.data(), nullptr, params.has_zeta ? &zeta_attachment_ref : nullptr, 0, + nullptr); + + vk::AccessFlags access; + vk::PipelineStageFlags stage; + if (!color_references.empty()) { + access |= + vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite; + stage |= vk::PipelineStageFlagBits::eColorAttachmentOutput; + } + + if (params.has_zeta) { + access |= vk::AccessFlagBits::eDepthStencilAttachmentRead | + vk::AccessFlagBits::eDepthStencilAttachmentWrite; + stage |= vk::PipelineStageFlagBits::eLateFragmentTests; + } + + const vk::SubpassDependency subpass_dependency(VK_SUBPASS_EXTERNAL, 0, stage, stage, {}, access, + {}); + + const vk::RenderPassCreateInfo create_info({}, static_cast(descriptors.size()), + descriptors.data(), 1, &subpass_description, 1, + &subpass_dependency); + + const auto dev = device.GetLogical(); + const auto& dld = device.GetDispatchLoader(); + return dev.createRenderPassUnique(create_info, nullptr, dld); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.h b/src/video_core/renderer_vulkan/vk_renderpass_cache.h new file mode 100644 index 0000000000..b49b2db48a --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.h @@ -0,0 +1,97 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include +#include + +#include "video_core/engines/maxwell_3d.h" +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/surface.h" + +namespace Vulkan { + +class VKDevice; + +// TODO(Rodrigo): Optimize this structure for faster hashing + +struct RenderPassParams { + struct ColorAttachment { + u32 index = 0; + VideoCore::Surface::PixelFormat pixel_format = VideoCore::Surface::PixelFormat::Invalid; + bool is_texception = false; + + std::size_t Hash() const noexcept { + return static_cast(pixel_format) | + static_cast(is_texception) << 6 | + static_cast(index) << 7; + } + + bool operator==(const ColorAttachment& rhs) const noexcept { + return std::tie(index, pixel_format, is_texception) == + std::tie(rhs.index, rhs.pixel_format, rhs.is_texception); + } + }; + + boost::container::static_vector + color_attachments{}; + // TODO(Rodrigo): Unify has_zeta into zeta_pixel_format and zeta_component_type. + VideoCore::Surface::PixelFormat zeta_pixel_format = VideoCore::Surface::PixelFormat::Invalid; + bool has_zeta = false; + bool zeta_texception = false; + + std::size_t Hash() const noexcept { + std::size_t hash = 0; + for (const auto& rt : color_attachments) { + boost::hash_combine(hash, rt.Hash()); + } + boost::hash_combine(hash, zeta_pixel_format); + boost::hash_combine(hash, has_zeta); + boost::hash_combine(hash, zeta_texception); + return hash; + } + + bool operator==(const RenderPassParams& rhs) const { + return std::tie(color_attachments, zeta_pixel_format, has_zeta, zeta_texception) == + std::tie(rhs.color_attachments, rhs.zeta_pixel_format, rhs.has_zeta, + rhs.zeta_texception); + } +}; + +} // namespace Vulkan + +namespace std { + +template <> +struct hash { + std::size_t operator()(const Vulkan::RenderPassParams& k) const noexcept { + return k.Hash(); + } +}; + +} // namespace std + +namespace Vulkan { + +class VKRenderPassCache final { +public: + explicit VKRenderPassCache(const VKDevice& device); + ~VKRenderPassCache(); + + vk::RenderPass GetRenderPass(const RenderPassParams& params); + +private: + UniqueRenderPass CreateRenderPass(const RenderPassParams& params) const; + + const VKDevice& device; + std::unordered_map cache; +}; + +} // namespace Vulkan