mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-12 19:06:09 -10:00
* FSR4 libs * Motion Vectors * FSR AA * FSR upscaling * Cleanup * Clang Tidy * Address Feedback * Improve canonical_prev handling * Clang Tidy again * Clean up prev_ submodel motion handling * Address Feedback
257 lines
7.9 KiB
C++
257 lines
7.9 KiB
C++
#include "VulkanPostProcessing.h"
|
|
|
|
#include <array>
|
|
|
|
#include "VulkanRenderer.h"
|
|
#include "VulkanTexture.h"
|
|
#include "graphics/util/uniform_structs.h"
|
|
#include "graphics/2d.h"
|
|
#include "graphics/opengl/SmaaAreaTex.h"
|
|
#include "graphics/opengl/SmaaSearchTex.h"
|
|
|
|
namespace graphics::vulkan {
|
|
|
|
|
|
// ===== SMAA Pipeline Implementation =====
|
|
//
|
|
// Mirrors gropenglpostprocessing.cpp's SMAA implementation: edge detection ->
|
|
// blending weight calculation -> neighborhood blending -> resolve. The shared
|
|
// SMAA.sdr algorithm body is untouched; only the thin wrapper shaders differ
|
|
// per-backend (see smaa-edge/blend/neighbour-*.sdr).
|
|
//
|
|
// Unlike OpenGL, Vulkan can't read and write the same attachment within one
|
|
// render pass, so neighborhood blending writes to Scene_luminance (safe to
|
|
// reuse as scratch: FXAA and SMAA are mutually exclusive via Gr_aa_mode) and
|
|
// a final resolve pass copies that back into Scene_ldr.
|
|
|
|
bool VulkanSMAA::init(PostProcessContext& ctx, const VulkanLDR& ldr)
|
|
{
|
|
if (m_initialized) {
|
|
return true;
|
|
}
|
|
|
|
if (!ldr.isInitialized()) {
|
|
return false;
|
|
}
|
|
|
|
m_ctx = &ctx;
|
|
m_ldr = &ldr;
|
|
|
|
auto* texMgr = getTextureManager();
|
|
if (!texMgr) {
|
|
return false;
|
|
}
|
|
|
|
if (!texMgr->createStaticTexture2D(AREATEX_WIDTH, AREATEX_HEIGHT, vk::Format::eR8G8Unorm,
|
|
areaTexBytes, sizeof(areaTexBytes), "SMAA Area Texture",
|
|
m_smaaAreaTexImage, m_smaaAreaTexView, m_smaaAreaTexAlloc)) {
|
|
nprintf(("vulkan", "VulkanSMAA: Failed to upload SMAA area texture!\n"));
|
|
return false;
|
|
}
|
|
|
|
if (!texMgr->createStaticTexture2D(SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, vk::Format::eR8Unorm,
|
|
searchTexBytes, sizeof(searchTexBytes), "SMAA Search Texture",
|
|
m_smaaSearchTexImage, m_smaaSearchTexView, m_smaaSearchTexAlloc)) {
|
|
nprintf(("vulkan", "VulkanSMAA: Failed to upload SMAA search texture!\n"));
|
|
shutdown();
|
|
return false;
|
|
}
|
|
|
|
if (!createTargets()) {
|
|
shutdown();
|
|
return false;
|
|
}
|
|
|
|
m_initialized = true;
|
|
nprintf(("vulkan", "VulkanSMAA: SMAA initialized (%ux%u)\n",
|
|
m_ctx->outputExtent.width, m_ctx->outputExtent.height));
|
|
return true;
|
|
}
|
|
|
|
bool VulkanSMAA::createTargets()
|
|
{
|
|
const vk::Format ldrFormat = m_ctx->ldrFormat;
|
|
const uint32_t w = m_ctx->outputExtent.width;
|
|
const uint32_t h = m_ctx->outputExtent.height;
|
|
|
|
if (!m_ctx->createImage(w, h, ldrFormat,
|
|
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled,
|
|
vk::ImageAspectFlagBits::eColor,
|
|
m_smaaEdges.image, m_smaaEdges.view, m_smaaEdges.allocation)) {
|
|
nprintf(("vulkan", "VulkanSMAA: Failed to create SMAA edges image!\n"));
|
|
return false;
|
|
}
|
|
m_smaaEdges.format = ldrFormat;
|
|
m_smaaEdges.width = w;
|
|
m_smaaEdges.height = h;
|
|
|
|
if (!m_ctx->createImage(w, h, ldrFormat,
|
|
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled,
|
|
vk::ImageAspectFlagBits::eColor,
|
|
m_smaaBlend.image, m_smaaBlend.view, m_smaaBlend.allocation)) {
|
|
nprintf(("vulkan", "VulkanSMAA: Failed to create SMAA blend image!\n"));
|
|
return false;
|
|
}
|
|
m_smaaBlend.format = ldrFormat;
|
|
m_smaaBlend.width = w;
|
|
m_smaaBlend.height = h;
|
|
|
|
// Framebuffers reuse the existing LDR render pass: same format, sample count,
|
|
// loadOp=eDontCare and finalLayout=eShaderReadOnlyOptimal, so it's compatible.
|
|
{
|
|
vk::FramebufferCreateInfo fbInfo;
|
|
fbInfo.renderPass = m_ldr->renderPass();
|
|
fbInfo.attachmentCount = 1;
|
|
fbInfo.width = w;
|
|
fbInfo.height = h;
|
|
fbInfo.layers = 1;
|
|
|
|
fbInfo.pAttachments = &m_smaaEdges.view;
|
|
try {
|
|
m_smaaEdgesFB = m_ctx->device.createFramebuffer(fbInfo);
|
|
} catch (const vk::SystemError& e) {
|
|
nprintf(("vulkan", "VulkanSMAA: Failed to create SMAA edges framebuffer: %s\n", e.what()));
|
|
return false;
|
|
}
|
|
|
|
fbInfo.pAttachments = &m_smaaBlend.view;
|
|
try {
|
|
m_smaaBlendFB = m_ctx->device.createFramebuffer(fbInfo);
|
|
} catch (const vk::SystemError& e) {
|
|
nprintf(("vulkan", "VulkanSMAA: Failed to create SMAA blend framebuffer: %s\n", e.what()));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void VulkanSMAA::destroyTargets()
|
|
{
|
|
if (m_smaaBlendFB) {
|
|
m_ctx->device.destroyFramebuffer(m_smaaBlendFB);
|
|
m_smaaBlendFB = nullptr;
|
|
}
|
|
if (m_smaaEdgesFB) {
|
|
m_ctx->device.destroyFramebuffer(m_smaaEdgesFB);
|
|
m_smaaEdgesFB = nullptr;
|
|
}
|
|
|
|
m_ctx->destroyTarget(m_smaaBlend);
|
|
m_ctx->destroyTarget(m_smaaEdges);
|
|
}
|
|
|
|
bool VulkanSMAA::resize()
|
|
{
|
|
if (!m_initialized) {
|
|
return true;
|
|
}
|
|
destroyTargets();
|
|
return createTargets();
|
|
}
|
|
|
|
void VulkanSMAA::shutdown()
|
|
{
|
|
if (!m_ctx || !m_ctx->device) {
|
|
return;
|
|
}
|
|
|
|
destroyTargets();
|
|
|
|
if (m_smaaSearchTexView) {
|
|
m_ctx->device.destroyImageView(m_smaaSearchTexView);
|
|
m_smaaSearchTexView = nullptr;
|
|
}
|
|
if (m_smaaSearchTexImage) {
|
|
m_ctx->device.destroyImage(m_smaaSearchTexImage);
|
|
m_smaaSearchTexImage = nullptr;
|
|
}
|
|
if (m_smaaSearchTexAlloc.isValid()) {
|
|
m_ctx->memoryManager->freeAllocation(m_smaaSearchTexAlloc);
|
|
}
|
|
|
|
if (m_smaaAreaTexView) {
|
|
m_ctx->device.destroyImageView(m_smaaAreaTexView);
|
|
m_smaaAreaTexView = nullptr;
|
|
}
|
|
if (m_smaaAreaTexImage) {
|
|
m_ctx->device.destroyImage(m_smaaAreaTexImage);
|
|
m_smaaAreaTexImage = nullptr;
|
|
}
|
|
if (m_smaaAreaTexAlloc.isValid()) {
|
|
m_ctx->memoryManager->freeAllocation(m_smaaAreaTexAlloc);
|
|
}
|
|
|
|
m_initialized = false;
|
|
}
|
|
|
|
void VulkanSMAA::execute(vk::CommandBuffer cmd)
|
|
{
|
|
if (!m_initialized || !m_ldr->isInitialized() || !gr_is_smaa_mode(Gr_aa_mode)) {
|
|
return;
|
|
}
|
|
|
|
GR_DEBUG_SCOPE("SMAA");
|
|
|
|
graphics::generic_data::smaa_data smaaData;
|
|
smaaData.smaa_rt_metrics.x = static_cast<float>(m_ctx->outputExtent.width);
|
|
smaaData.smaa_rt_metrics.y = static_cast<float>(m_ctx->outputExtent.height);
|
|
smaaData.pad[0] = 0.0f;
|
|
smaaData.pad[1] = 0.0f;
|
|
|
|
// Pass 1: edge detection. Detection proxy (Scene_ldr, or the tonemap-compressed
|
|
// version while HDR is active) -> Smaa_edges
|
|
{
|
|
GR_DEBUG_SCOPE("SMAA Detect Edges");
|
|
|
|
// The edge detection shader discards non-edge pixels, so Smaa_edges must be
|
|
// cleared every frame -- otherwise discarded pixels keep last frame's edges
|
|
// forever. Explicit clearColor (vkCmdClearAttachments) rather than relying on
|
|
// the render pass's own loadOp=eDontCare, which leaves those pixels undefined.
|
|
static const std::array<float, 4> kClearColor = {0.0f, 0.0f, 0.0f, 1.0f};
|
|
m_ctx->drawFullscreenTriangle(cmd, m_ldr->renderPass(),
|
|
m_smaaEdgesFB, m_ctx->outputExtent,
|
|
SDR_TYPE_POST_PROCESS_SMAA_EDGE,
|
|
m_ldr->getAADetectionView(), m_ctx->linearSampler,
|
|
&smaaData, sizeof(smaaData),
|
|
ALPHA_BLEND_NONE,
|
|
0, vk::SampleCountFlagBits::e1, false,
|
|
&kClearColor);
|
|
}
|
|
|
|
// Pass 2: blending weight calculation. Smaa_edges + area + search -> Smaa_blend
|
|
{
|
|
GR_DEBUG_SCOPE("SMAA Blending Weights calculation");
|
|
|
|
std::array<vk::ImageView, 3> views = {m_smaaEdges.view, m_smaaAreaTexView, m_smaaSearchTexView};
|
|
m_ctx->drawFullscreenTriangleMulti(cmd, m_ldr->renderPass(), m_smaaBlendFB, m_ctx->outputExtent,
|
|
SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT, views.data(), static_cast<uint32_t>(views.size()),
|
|
&smaaData, sizeof(smaaData));
|
|
}
|
|
|
|
// Pass 3: neighborhood blending. Scene_ldr (real, extended-range) + Smaa_blend ->
|
|
// Scene_luminance (scratch; safe to reuse since FXAA and SMAA are mutually exclusive).
|
|
{
|
|
GR_DEBUG_SCOPE("SMAA Neighborhood Blending");
|
|
|
|
std::array<vk::ImageView, 2> views = {m_ldr->ldrView(), m_smaaBlend.view};
|
|
m_ctx->drawFullscreenTriangleMulti(cmd, m_ldr->renderPass(), m_ldr->luminanceFramebuffer(), m_ctx->outputExtent,
|
|
SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING, views.data(), static_cast<uint32_t>(views.size()),
|
|
&smaaData, sizeof(smaaData));
|
|
}
|
|
|
|
// Pass 4: resolve. Scene_luminance -> Scene_ldr
|
|
{
|
|
GR_DEBUG_SCOPE("SMAA Resolve");
|
|
|
|
m_ctx->drawFullscreenTriangle(cmd, m_ldr->renderPass(),
|
|
m_ldr->ldrFramebuffer(), m_ctx->outputExtent,
|
|
SDR_TYPE_POST_PROCESS_SMAA_RESOLVE,
|
|
m_ldr->luminanceView(), m_ctx->linearSampler,
|
|
nullptr, 0,
|
|
ALPHA_BLEND_NONE);
|
|
}
|
|
}
|
|
|
|
} // namespace graphics::vulkan
|