mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
358 lines
14 KiB
C++
358 lines
14 KiB
C++
#include "graphics/host_gpu/renderer/framebufferCache.h"
|
|
|
|
#include "common/assert.h"
|
|
#include "common/logging/log.h"
|
|
#include "common/profiler.h"
|
|
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
|
|
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
|
#include "graphics/host_gpu/vulkanCommon.h"
|
|
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
|
|
namespace Libs::Graphics {
|
|
|
|
VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
|
|
uint32_t requested_color_count,
|
|
RenderDepthInfo& depth) {
|
|
KYTY_PROFILER_FUNCTION();
|
|
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
EXIT_IF(colors == nullptr);
|
|
EXIT_IF(requested_color_count > RENDER_COLOR_ATTACHMENTS_MAX);
|
|
|
|
bool with_depth = (depth.format != vk::Format::eUndefined && depth.vulkan_buffer != nullptr);
|
|
bool with_color[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
|
uint32_t color_count = 0;
|
|
VulkanImage* first_color = nullptr;
|
|
vk::Extent2D first_color_extent = {};
|
|
uint32_t attachment_samples = 0;
|
|
for (uint32_t i = 0; i < requested_color_count; i++) {
|
|
with_color[i] = (colors[i].vulkan_buffer != nullptr);
|
|
if (!with_color[i]) {
|
|
break;
|
|
}
|
|
if (first_color == nullptr) {
|
|
first_color = colors[i].vulkan_buffer;
|
|
first_color_extent = colors[i].extent;
|
|
attachment_samples = colors[i].samples;
|
|
} else if (colors[i].extent.width != first_color_extent.width ||
|
|
colors[i].extent.height != first_color_extent.height) {
|
|
LOGF("Framebuffer: temporary: dropping mismatched MRT%u attachment color0=%ux%u "
|
|
"color%u=%ux%u\n",
|
|
i, first_color_extent.width, first_color_extent.height, i, colors[i].extent.width,
|
|
colors[i].extent.height);
|
|
with_color[i] = false;
|
|
break;
|
|
}
|
|
if (colors[i].samples != attachment_samples ||
|
|
colors[i].vulkan_buffer->samples != colors[i].samples) {
|
|
EXIT("Framebuffer: mismatched color attachment samples at slot %u, expected=%u "
|
|
"requested=%u image=%u\n",
|
|
i, attachment_samples, colors[i].samples, colors[i].vulkan_buffer->samples);
|
|
}
|
|
color_count++;
|
|
}
|
|
if (with_depth) {
|
|
if (depth.samples != depth.vulkan_buffer->samples) {
|
|
EXIT("Framebuffer: depth attachment sample identity mismatch, requested=%u image=%u\n",
|
|
depth.samples, depth.vulkan_buffer->samples);
|
|
}
|
|
if (attachment_samples == 0) {
|
|
attachment_samples = depth.samples;
|
|
} else if (attachment_samples != depth.samples) {
|
|
EXIT(
|
|
"Framebuffer: mixed color/depth sample counts are unsupported, color=%u depth=%u\n",
|
|
attachment_samples, depth.samples);
|
|
}
|
|
}
|
|
if (!with_depth && color_count == 0) {
|
|
LOGF("Framebuffer: warning: no color or depth attachment\n");
|
|
return nullptr;
|
|
}
|
|
if (vulkan_sample_count(attachment_samples) == vk::SampleCountFlagBits {}) {
|
|
EXIT("Framebuffer: invalid attachment sample count %u\n", attachment_samples);
|
|
}
|
|
vk::ImageLayout color_layout[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
|
for (auto& layout: color_layout) {
|
|
layout = RENDER_COLOR_IMAGE_LAYOUT;
|
|
}
|
|
auto depth_layout = (with_depth ? depth_attachment_layout(depth)
|
|
: vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
|
auto depth_read_only =
|
|
(with_depth && depth_layout == vk::ImageLayout::eDepthStencilReadOnlyOptimal);
|
|
|
|
if (with_depth && first_color != nullptr &&
|
|
(first_color_extent.width != depth.vulkan_buffer->extent.width ||
|
|
first_color_extent.height != depth.vulkan_buffer->extent.height)) {
|
|
static std::atomic<uint32_t> log_count {0};
|
|
if (log_count.fetch_add(1, std::memory_order_relaxed) < 16) {
|
|
LOGF("Framebuffer: temporary: dropping mismatched PS5 depth attachment color=%ux%u "
|
|
"depth=%ux%u format=%s\n",
|
|
first_color_extent.width, first_color_extent.height,
|
|
depth.vulkan_buffer->extent.width, depth.vulkan_buffer->extent.height,
|
|
VulkanToString(depth.format).c_str());
|
|
}
|
|
depth.format = vk::Format::eUndefined;
|
|
depth.vulkan_buffer = nullptr;
|
|
depth.vulkan_view = nullptr;
|
|
depth.depth_test_enable = false;
|
|
depth.depth_write_enable = false;
|
|
depth.depth_bounds_test_enable = false;
|
|
depth.stencil_test_enable = false;
|
|
depth.depth_clear_enable = false;
|
|
depth.depth_load_clear_enable = false;
|
|
depth.stencil_clear_enable = false;
|
|
with_depth = false;
|
|
depth_layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
|
|
depth_read_only = false;
|
|
}
|
|
|
|
for (auto& f: m_framebuffers) {
|
|
bool color_match = (f.framebuffer != nullptr);
|
|
for (uint32_t i = 0; color_match && i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
|
|
const uint64_t image_id =
|
|
(i < color_count && with_color[i] ? colors[i].vulkan_buffer->memory.unique_id : 0);
|
|
color_match =
|
|
color_match && f.image_id[i] == image_id &&
|
|
f.color_view[i] ==
|
|
(i < color_count && with_color[i] ? colors[i].vulkan_view : nullptr) &&
|
|
f.color_clear_enable[i] ==
|
|
(i < color_count && with_color[i] && colors[i].color_clear_enable) &&
|
|
f.color_layout[i] == color_layout[i];
|
|
}
|
|
if (color_match && f.depth_id == (with_depth ? depth.vulkan_buffer->memory.unique_id : 0) &&
|
|
f.depth_view == (with_depth ? depth.vulkan_view : nullptr) &&
|
|
f.depth_clear_enable == depth.depth_load_clear_enable &&
|
|
f.stencil_clear_enable == depth.stencil_clear_enable &&
|
|
f.depth_read_only == depth_read_only) {
|
|
return f.framebuffer;
|
|
}
|
|
}
|
|
|
|
EXIT_NOT_IMPLEMENTED(with_depth && first_color != nullptr &&
|
|
(first_color_extent.width != depth.vulkan_buffer->extent.width ||
|
|
first_color_extent.height != depth.vulkan_buffer->extent.height));
|
|
|
|
if (first_color == nullptr) {
|
|
first_color_extent = depth.vulkan_buffer->extent;
|
|
}
|
|
|
|
auto* framebuffer = new VulkanFramebuffer;
|
|
framebuffer->render_pass = nullptr;
|
|
framebuffer->framebuffer = nullptr;
|
|
framebuffer->samples = attachment_samples;
|
|
for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
|
|
framebuffer->color_layout[i] = color_layout[i];
|
|
}
|
|
framebuffer->depth_layout = depth_layout;
|
|
|
|
vk::AttachmentDescription attachments[RENDER_COLOR_ATTACHMENTS_MAX + 1] = {};
|
|
for (uint32_t i = 0; i < color_count; i++) {
|
|
attachments[i].flags = {};
|
|
attachments[i].format = colors[i].format;
|
|
attachments[i].samples = vulkan_sample_count(attachment_samples);
|
|
attachments[i].loadOp = (colors[i].color_clear_enable ? vk::AttachmentLoadOp::eClear
|
|
: vk::AttachmentLoadOp::eLoad);
|
|
attachments[i].storeOp = vk::AttachmentStoreOp::eStore;
|
|
attachments[i].stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
|
|
attachments[i].stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
|
|
attachments[i].initialLayout = color_layout[i];
|
|
attachments[i].finalLayout = RENDER_COLOR_IMAGE_LAYOUT;
|
|
}
|
|
|
|
const uint32_t depth_attachment = color_count;
|
|
attachments[depth_attachment].flags = {};
|
|
attachments[depth_attachment].format = depth.format;
|
|
attachments[depth_attachment].samples = vulkan_sample_count(attachment_samples);
|
|
attachments[depth_attachment].loadOp =
|
|
(depth.depth_load_clear_enable ? vk::AttachmentLoadOp::eClear
|
|
: vk::AttachmentLoadOp::eLoad);
|
|
attachments[depth_attachment].storeOp = vk::AttachmentStoreOp::eStore;
|
|
attachments[depth_attachment].stencilLoadOp =
|
|
(depth.stencil_clear_enable ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad);
|
|
attachments[depth_attachment].stencilStoreOp = vk::AttachmentStoreOp::eStore;
|
|
attachments[depth_attachment].initialLayout = depth_layout;
|
|
attachments[depth_attachment].finalLayout = depth_layout;
|
|
|
|
vk::AttachmentReference color_attachment_ref[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
|
for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
|
|
color_attachment_ref[i].attachment = (i < color_count ? i : VK_ATTACHMENT_UNUSED);
|
|
color_attachment_ref[i].layout = RENDER_COLOR_IMAGE_LAYOUT;
|
|
}
|
|
|
|
vk::AttachmentReference depth_attachment_ref {};
|
|
depth_attachment_ref.attachment = depth_attachment;
|
|
depth_attachment_ref.layout = depth_layout;
|
|
|
|
vk::SubpassDescription subpass {};
|
|
subpass.flags = {};
|
|
subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
|
|
subpass.inputAttachmentCount = 0;
|
|
subpass.pInputAttachments = nullptr;
|
|
subpass.colorAttachmentCount = color_count;
|
|
subpass.pColorAttachments = (color_count > 0 ? color_attachment_ref : nullptr);
|
|
subpass.pResolveAttachments = nullptr;
|
|
subpass.pDepthStencilAttachment = (with_depth ? &depth_attachment_ref : nullptr);
|
|
subpass.preserveAttachmentCount = 0;
|
|
subpass.pPreserveAttachments = nullptr;
|
|
|
|
const auto attachment_stage_mask =
|
|
static_cast<vk::PipelineStageFlags>(vk::PipelineStageFlagBits::eColorAttachmentOutput |
|
|
vk::PipelineStageFlagBits::eEarlyFragmentTests |
|
|
vk::PipelineStageFlagBits::eLateFragmentTests);
|
|
const auto attachment_access_mask = static_cast<vk::AccessFlags>(
|
|
vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite |
|
|
vk::AccessFlagBits::eDepthStencilAttachmentRead |
|
|
vk::AccessFlagBits::eDepthStencilAttachmentWrite);
|
|
|
|
vk::SubpassDependency dependencies[2] = {};
|
|
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
|
|
dependencies[0].dstSubpass = 0;
|
|
dependencies[0].srcStageMask = vk::PipelineStageFlagBits::eAllCommands;
|
|
dependencies[0].dstStageMask = attachment_stage_mask;
|
|
dependencies[0].srcAccessMask =
|
|
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
|
|
dependencies[0].dstAccessMask = attachment_access_mask;
|
|
dependencies[0].dependencyFlags = vk::DependencyFlagBits::eByRegion;
|
|
|
|
dependencies[1].srcSubpass = 0;
|
|
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
|
|
dependencies[1].srcStageMask = attachment_stage_mask;
|
|
dependencies[1].dstStageMask = vk::PipelineStageFlagBits::eAllCommands;
|
|
dependencies[1].srcAccessMask = attachment_access_mask;
|
|
dependencies[1].dstAccessMask =
|
|
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
|
|
dependencies[1].dependencyFlags = vk::DependencyFlagBits::eByRegion;
|
|
|
|
vk::RenderPassCreateInfo render_pass_info {};
|
|
render_pass_info.sType = vk::StructureType::eRenderPassCreateInfo;
|
|
render_pass_info.pNext = nullptr;
|
|
render_pass_info.flags = {};
|
|
render_pass_info.attachmentCount = color_count + (with_depth ? 1u : 0u);
|
|
render_pass_info.pAttachments = attachments;
|
|
render_pass_info.subpassCount = 1;
|
|
render_pass_info.pSubpasses = &subpass;
|
|
render_pass_info.dependencyCount = 2;
|
|
render_pass_info.pDependencies = dependencies;
|
|
|
|
auto result =
|
|
m_graphics.device.createRenderPass(&render_pass_info, nullptr, &framebuffer->render_pass);
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
|
|
vk::Format color_formats[RENDER_COLOR_ATTACHMENTS_MAX] = {};
|
|
for (uint32_t i = 0; i < color_count; i++) {
|
|
color_formats[i] = colors[i].format;
|
|
}
|
|
framebuffer->render_pass_id = render_pass_compat_id(
|
|
color_count, color_formats, with_depth, depth.format, depth_layout, attachment_samples);
|
|
|
|
EXIT_NOT_IMPLEMENTED(framebuffer->render_pass == nullptr);
|
|
|
|
vk::ImageView views[RENDER_COLOR_ATTACHMENTS_MAX + 1] = {};
|
|
for (uint32_t i = 0; i < color_count; i++) {
|
|
if (colors[i].vulkan_view == nullptr) {
|
|
EXIT("Framebuffer: color attachment view is missing at slot %u\n", i);
|
|
}
|
|
views[i] = colors[i].vulkan_view;
|
|
}
|
|
if (with_depth) {
|
|
if (depth.vulkan_view == nullptr) {
|
|
EXIT("Framebuffer: depth attachment view is missing\n");
|
|
}
|
|
views[color_count] = depth.vulkan_view;
|
|
}
|
|
|
|
vk::FramebufferCreateInfo framebuffer_info {};
|
|
framebuffer_info.sType = vk::StructureType::eFramebufferCreateInfo;
|
|
framebuffer_info.pNext = nullptr;
|
|
framebuffer_info.flags = {};
|
|
framebuffer_info.renderPass = framebuffer->render_pass;
|
|
framebuffer_info.attachmentCount = color_count + (with_depth ? 1u : 0u);
|
|
framebuffer_info.pAttachments = views;
|
|
framebuffer_info.width = first_color_extent.width;
|
|
framebuffer_info.height = first_color_extent.height;
|
|
framebuffer_info.layers = 1;
|
|
|
|
result =
|
|
m_graphics.device.createFramebuffer(&framebuffer_info, nullptr, &framebuffer->framebuffer);
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
|
|
EXIT_NOT_IMPLEMENTED(framebuffer->framebuffer == nullptr);
|
|
|
|
Framebuffer fnew;
|
|
fnew.framebuffer = framebuffer;
|
|
for (uint32_t i = 0; i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
|
|
fnew.image_id[i] =
|
|
(i < color_count && with_color[i] ? colors[i].vulkan_buffer->memory.unique_id : 0);
|
|
fnew.color_view[i] = (i < color_count && with_color[i] ? colors[i].vulkan_view : nullptr);
|
|
fnew.color_clear_enable[i] =
|
|
(i < color_count && with_color[i] && colors[i].color_clear_enable);
|
|
fnew.color_layout[i] = color_layout[i];
|
|
}
|
|
fnew.depth_id = (with_depth ? depth.vulkan_buffer->memory.unique_id : 0);
|
|
fnew.depth_view = (with_depth ? depth.vulkan_view : nullptr);
|
|
fnew.depth_clear_enable = depth.depth_load_clear_enable;
|
|
fnew.stencil_clear_enable = depth.stencil_clear_enable;
|
|
fnew.depth_read_only = depth_read_only;
|
|
|
|
bool updated = false;
|
|
|
|
for (auto& f: m_framebuffers) {
|
|
if (f.framebuffer == nullptr) {
|
|
f = fnew;
|
|
updated = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!updated) {
|
|
m_framebuffers.push_back(fnew);
|
|
}
|
|
|
|
return framebuffer;
|
|
}
|
|
|
|
void FramebufferCache::FreeFramebufferByColor(VulkanImage& image) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
for (auto& f: m_framebuffers) {
|
|
bool uses_image = false;
|
|
for (auto image_id: f.image_id) {
|
|
if (image_id == image.memory.unique_id) {
|
|
uses_image = true;
|
|
break;
|
|
}
|
|
}
|
|
if (f.framebuffer != nullptr && uses_image) {
|
|
m_graphics.device.destroyFramebuffer(f.framebuffer->framebuffer, nullptr);
|
|
|
|
m_graphics.device.destroyRenderPass(f.framebuffer->render_pass, nullptr);
|
|
|
|
delete f.framebuffer;
|
|
|
|
f.framebuffer = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FramebufferCache::FreeFramebufferByDepth(DepthStencilVulkanImage& image) {
|
|
Common::LockGuard lock(m_mutex);
|
|
|
|
for (auto& f: m_framebuffers) {
|
|
if (f.framebuffer != nullptr && f.depth_id == image.memory.unique_id) {
|
|
m_graphics.device.destroyFramebuffer(f.framebuffer->framebuffer, nullptr);
|
|
|
|
m_graphics.device.destroyRenderPass(f.framebuffer->render_pass, nullptr);
|
|
|
|
delete f.framebuffer;
|
|
|
|
f.framebuffer = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace Libs::Graphics
|