mirror of
https://github.com/citra-emu/citra.git
synced 2024-12-26 08:40:08 +00:00
vk_renderpass_cache: Add support for multi-sample renderpasses
A multi-sample render-pass will simply append additional attachments to the end of the attachment list to use as primary color+depth attachments and will resolve back into the original non-msaa attachments. Framebuffers must be sure to structure their attachments the same way.
This commit is contained in:
parent
85a2304b42
commit
89ffff3426
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <limits>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include "common/assert.h"
|
||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
@ -38,7 +39,7 @@ void RenderpassCache::BeginRendering(const Framebuffer* framebuffer,
|
||||
.framebuffer = framebuffer->Handle(),
|
||||
.render_pass = framebuffer->RenderPass(),
|
||||
.render_area = render_area,
|
||||
.clear = {},
|
||||
.clears = {},
|
||||
.do_clear = false,
|
||||
};
|
||||
images = framebuffer->Images();
|
||||
@ -58,8 +59,8 @@ void RenderpassCache::BeginRendering(const RenderPass& new_pass) {
|
||||
.renderPass = info.render_pass,
|
||||
.framebuffer = info.framebuffer,
|
||||
.renderArea = info.render_area,
|
||||
.clearValueCount = info.do_clear ? 1u : 0u,
|
||||
.pClearValues = &info.clear,
|
||||
.clearValueCount = info.do_clear ? 2u : 0u,
|
||||
.pClearValues = info.clears.data(),
|
||||
};
|
||||
cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline);
|
||||
});
|
||||
@ -124,7 +125,8 @@ void RenderpassCache::EndRendering() {
|
||||
}
|
||||
|
||||
vk::RenderPass RenderpassCache::GetRenderpass(VideoCore::PixelFormat color,
|
||||
VideoCore::PixelFormat depth, bool is_clear) {
|
||||
VideoCore::PixelFormat depth, bool is_clear,
|
||||
u8 sample_count) {
|
||||
std::scoped_lock lock{cache_mutex};
|
||||
|
||||
const u32 color_index =
|
||||
@ -136,22 +138,31 @@ vk::RenderPass RenderpassCache::GetRenderpass(VideoCore::PixelFormat color,
|
||||
ASSERT_MSG(color_index <= MAX_COLOR_FORMATS && depth_index <= MAX_DEPTH_FORMATS,
|
||||
"Invalid color index {} and/or depth_index {}", color_index, depth_index);
|
||||
|
||||
vk::UniqueRenderPass& renderpass = cached_renderpasses[color_index][depth_index][is_clear];
|
||||
ASSERT_MSG(sample_count && std::has_single_bit(sample_count) && sample_count <= MAX_SAMPLES,
|
||||
"Invalid sample count {}", static_cast<u32>(sample_count));
|
||||
|
||||
const u32 samples_index = static_cast<u32>(std::bit_width(sample_count) - 1);
|
||||
|
||||
vk::UniqueRenderPass& renderpass =
|
||||
cached_renderpasses[color_index][depth_index][samples_index][is_clear];
|
||||
if (!renderpass) {
|
||||
const vk::Format color_format = instance.GetTraits(color).native;
|
||||
const vk::Format depth_format = instance.GetTraits(depth).native;
|
||||
const vk::AttachmentLoadOp load_op =
|
||||
is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad;
|
||||
renderpass = CreateRenderPass(color_format, depth_format, load_op);
|
||||
renderpass = CreateRenderPass(color_format, depth_format, load_op,
|
||||
static_cast<vk::SampleCountFlagBits>(sample_count));
|
||||
}
|
||||
|
||||
return *renderpass;
|
||||
}
|
||||
|
||||
vk::UniqueRenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format depth,
|
||||
vk::AttachmentLoadOp load_op) const {
|
||||
u32 attachment_count = 0;
|
||||
std::array<vk::AttachmentDescription, 2> attachments;
|
||||
vk::AttachmentLoadOp load_op,
|
||||
vk::SampleCountFlagBits sample_count) const {
|
||||
|
||||
boost::container::static_vector<vk::AttachmentDescription, 4> attachments{};
|
||||
boost::container::static_vector<vk::AttachmentReference, 2> resolve_attachments{};
|
||||
|
||||
bool use_color = false;
|
||||
vk::AttachmentReference color_attachment_ref{};
|
||||
@ -159,18 +170,19 @@ vk::UniqueRenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::For
|
||||
vk::AttachmentReference depth_attachment_ref{};
|
||||
|
||||
if (color != vk::Format::eUndefined) {
|
||||
attachments[attachment_count] = vk::AttachmentDescription{
|
||||
attachments.emplace_back(vk::AttachmentDescription{
|
||||
.format = color,
|
||||
.samples = vk::SampleCountFlagBits::e1,
|
||||
.loadOp = load_op,
|
||||
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
|
||||
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
|
||||
.initialLayout = vk::ImageLayout::eGeneral,
|
||||
.finalLayout = vk::ImageLayout::eGeneral,
|
||||
};
|
||||
});
|
||||
|
||||
color_attachment_ref = vk::AttachmentReference{
|
||||
.attachment = attachment_count++,
|
||||
.attachment = static_cast<u32>(attachments.size() - 1),
|
||||
.layout = vk::ImageLayout::eGeneral,
|
||||
};
|
||||
|
||||
@ -178,36 +190,89 @@ vk::UniqueRenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::For
|
||||
}
|
||||
|
||||
if (depth != vk::Format::eUndefined) {
|
||||
attachments[attachment_count] = vk::AttachmentDescription{
|
||||
attachments.emplace_back(vk::AttachmentDescription{
|
||||
.format = depth,
|
||||
.samples = vk::SampleCountFlagBits::e1,
|
||||
.loadOp = load_op,
|
||||
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||
.stencilLoadOp = load_op,
|
||||
.stencilStoreOp = vk::AttachmentStoreOp::eStore,
|
||||
.initialLayout = vk::ImageLayout::eGeneral,
|
||||
.finalLayout = vk::ImageLayout::eGeneral,
|
||||
};
|
||||
});
|
||||
|
||||
depth_attachment_ref = vk::AttachmentReference{
|
||||
.attachment = attachment_count++,
|
||||
.attachment = static_cast<u32>(attachments.size() - 1),
|
||||
.layout = vk::ImageLayout::eGeneral,
|
||||
};
|
||||
|
||||
use_depth = true;
|
||||
}
|
||||
|
||||
// In the case of MSAA, each attachment gets an additional MSAA attachment that now becomes the
|
||||
// main attachment and the original attachments now get resolved into
|
||||
if (sample_count > vk::SampleCountFlagBits::e1) {
|
||||
if (color != vk::Format::eUndefined) {
|
||||
attachments.emplace_back(vk::AttachmentDescription{
|
||||
.format = color,
|
||||
.samples = sample_count,
|
||||
.loadOp = load_op,
|
||||
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
|
||||
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
|
||||
.initialLayout = vk::ImageLayout::eGeneral,
|
||||
.finalLayout = vk::ImageLayout::eGeneral,
|
||||
});
|
||||
|
||||
resolve_attachments.emplace_back(color_attachment_ref);
|
||||
|
||||
color_attachment_ref = vk::AttachmentReference{
|
||||
.attachment = static_cast<u32>(attachments.size() - 1),
|
||||
.layout = vk::ImageLayout::eGeneral,
|
||||
};
|
||||
}
|
||||
|
||||
if (depth != vk::Format::eUndefined) {
|
||||
attachments.emplace_back(vk::AttachmentDescription{
|
||||
.format = depth,
|
||||
.samples = sample_count,
|
||||
.loadOp = load_op,
|
||||
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||
.stencilLoadOp = load_op,
|
||||
.stencilStoreOp = vk::AttachmentStoreOp::eStore,
|
||||
.initialLayout = vk::ImageLayout::eGeneral,
|
||||
.finalLayout = vk::ImageLayout::eGeneral,
|
||||
});
|
||||
|
||||
resolve_attachments.emplace_back(depth_attachment_ref);
|
||||
|
||||
depth_attachment_ref = vk::AttachmentReference{
|
||||
.attachment = static_cast<u32>(attachments.size() - 1),
|
||||
.layout = vk::ImageLayout::eGeneral,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (color != vk::Format::eUndefined) {
|
||||
resolve_attachments.emplace_back(vk::AttachmentReference{VK_ATTACHMENT_UNUSED});
|
||||
}
|
||||
|
||||
if (depth != vk::Format::eUndefined) {
|
||||
resolve_attachments.emplace_back(vk::AttachmentReference{VK_ATTACHMENT_UNUSED});
|
||||
}
|
||||
}
|
||||
|
||||
const vk::SubpassDescription subpass = {
|
||||
.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
|
||||
.inputAttachmentCount = 0,
|
||||
.pInputAttachments = nullptr,
|
||||
.colorAttachmentCount = use_color ? 1u : 0u,
|
||||
.pColorAttachments = &color_attachment_ref,
|
||||
.pResolveAttachments = 0,
|
||||
.pResolveAttachments = resolve_attachments.data(),
|
||||
.pDepthStencilAttachment = use_depth ? &depth_attachment_ref : nullptr,
|
||||
};
|
||||
|
||||
const vk::RenderPassCreateInfo renderpass_info = {
|
||||
.attachmentCount = attachment_count,
|
||||
.attachmentCount = static_cast<u32>(attachments.size()),
|
||||
.pAttachments = attachments.data(),
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass,
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/math_util.h"
|
||||
@ -23,20 +24,22 @@ struct RenderPass {
|
||||
vk::Framebuffer framebuffer;
|
||||
vk::RenderPass render_pass;
|
||||
vk::Rect2D render_area;
|
||||
vk::ClearValue clear;
|
||||
std::array<vk::ClearValue, 2> clears;
|
||||
bool do_clear;
|
||||
|
||||
bool operator==(const RenderPass& other) const noexcept {
|
||||
return std::tie(framebuffer, render_pass, render_area, do_clear) ==
|
||||
std::tie(other.framebuffer, other.render_pass, other.render_area,
|
||||
other.do_clear) &&
|
||||
std::memcmp(&clear, &other.clear, sizeof(vk::ClearValue)) == 0;
|
||||
std::memcmp(&clears, &other.clears, sizeof(clears)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
class RenderpassCache {
|
||||
static constexpr std::size_t MAX_COLOR_FORMATS = 13;
|
||||
static constexpr std::size_t MAX_DEPTH_FORMATS = 4;
|
||||
static constexpr std::size_t MAX_SAMPLES = 8;
|
||||
static_assert(std::has_single_bit(MAX_SAMPLES));
|
||||
|
||||
public:
|
||||
explicit RenderpassCache(const Instance& instance, Scheduler& scheduler);
|
||||
@ -53,19 +56,21 @@ public:
|
||||
|
||||
/// Returns the renderpass associated with the color-depth format pair
|
||||
vk::RenderPass GetRenderpass(VideoCore::PixelFormat color, VideoCore::PixelFormat depth,
|
||||
bool is_clear);
|
||||
bool is_clear, u8 sample_count = 1);
|
||||
|
||||
private:
|
||||
/// Creates a renderpass configured appropriately and stores it in cached_renderpasses
|
||||
vk::UniqueRenderPass CreateRenderPass(vk::Format color, vk::Format depth,
|
||||
vk::AttachmentLoadOp load_op) const;
|
||||
vk::AttachmentLoadOp load_op,
|
||||
vk::SampleCountFlagBits sample_count) const;
|
||||
|
||||
private:
|
||||
const Instance& instance;
|
||||
Scheduler& scheduler;
|
||||
vk::UniqueRenderPass cached_renderpasses[MAX_COLOR_FORMATS + 1][MAX_DEPTH_FORMATS + 1][2];
|
||||
vk::UniqueRenderPass cached_renderpasses[MAX_COLOR_FORMATS + 1][MAX_DEPTH_FORMATS + 1]
|
||||
[std::bit_width(MAX_SAMPLES)][2];
|
||||
std::mutex cache_mutex;
|
||||
std::array<vk::Image, 2> images;
|
||||
std::array<vk::Image, 4> images;
|
||||
std::array<vk::ImageAspectFlags, 2> aspects;
|
||||
RenderPass pass{};
|
||||
u32 num_draws{};
|
||||
|
Loading…
Reference in New Issue
Block a user