Files
2024-02-26 19:00:48 +01:00

160 lines
4.7 KiB
C++

// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "SMAA.h"
#include "Engine/Content/Assets/Shader.h"
#include "Engine/Content/Content.h"
#include "Engine/Graphics/Graphics.h"
#include "Engine/Graphics/GPUContext.h"
#include "Engine/Graphics/RenderTargetPool.h"
#include "Engine/Graphics/RenderTask.h"
bool SMAA::setupResources()
{
// Load textures
if (_areaTex == nullptr)
{
_areaTex = Content::LoadAsyncInternal<Texture>(SMAA_AREA_TEX);
if (_areaTex == nullptr)
return true;
}
if (_searchTex == nullptr)
{
_searchTex = Content::LoadAsyncInternal<Texture>(SMAA_SEARCH_TEX);
if (_searchTex == nullptr)
return true;
}
// Check shader
if (_shader == nullptr)
{
// Create pipeline states
_psEdge.CreatePipelineStates();
_psBlend.CreatePipelineStates();
_psNeighbor = GPUDevice::Instance->CreatePipelineState();
// Load shader
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/SMAA"));
if (_shader == nullptr)
return true;
#if COMPILE_WITH_DEV_ENV
_shader.Get()->OnReloading.Bind<SMAA, &SMAA::OnShaderReloading>(this);
#endif
}
if (!_shader->IsLoaded())
{
return true;
}
const auto shader = _shader->GetShader();
// Validate shader constant buffer size
if (shader->GetCB(0)->GetSize() != sizeof(Data))
{
REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data);
return true;
}
// Create pipeline state
GPUPipelineState::Description psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
if (!_psEdge.IsValid())
{
psDesc.VS = shader->GetVS("VS_Edge");
if (_psEdge.Create(psDesc, shader, "PS_Edge"))
return true;
}
if (!_psBlend.IsValid())
{
psDesc.VS = shader->GetVS("VS_Blend");
if (_psBlend.Create(psDesc, shader, "PS_Blend"))
return true;
}
if (!_psNeighbor->IsValid())
{
psDesc.VS = shader->GetVS("VS_Neighbor");
psDesc.PS = shader->GetPS("PS_Neighbor");
if (_psNeighbor->Init(psDesc))
return true;
}
return false;
}
void SMAA::Dispose()
{
// Base
RendererPass::Dispose();
// Cleanup
_psEdge.Delete();
_psBlend.Delete();
SAFE_DELETE_GPU_RESOURCE(_psNeighbor);
_shader = nullptr;
_areaTex = nullptr;
_searchTex = nullptr;
}
void SMAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output)
{
auto context = GPUDevice::Instance->GetMainContext();
const auto qualityLevel = Math::Clamp(static_cast<int32>(Graphics::AAQuality), 0, static_cast<int32>(Quality::MAX) - 1);
// Ensure to have valid data
if (checkIfSkipPass())
{
// Resources are missing. Do not perform rendering, just copy input frame.
context->SetRenderTarget(output);
context->Draw(input);
return;
}
PROFILE_GPU_CPU("Subpixel Morphological Antialiasing");
// Get temporary targets
const auto tempDesc = GPUTextureDescription::New2D((int32)renderContext.View.ScreenSize.X, (int32)renderContext.View.ScreenSize.Y, PixelFormat::R8G8B8A8_UNorm);
auto edges = RenderTargetPool::Get(tempDesc);
auto weights = RenderTargetPool::Get(tempDesc);
RENDER_TARGET_POOL_SET_NAME(edges, "SMAA.Edges");
RENDER_TARGET_POOL_SET_NAME(weights,"SMAA.Weights");
// Bind constants
Data data;
data.RtSize.X = 1.0f / tempDesc.Width;
data.RtSize.Y = 1.0f / tempDesc.Height;
data.RtSize.Z = (float)tempDesc.Width;
data.RtSize.W = (float)tempDesc.Height;
const auto cb = _shader->GetShader()->GetCB(0);
context->UpdateCB(cb, &data);
context->BindCB(0, cb);
// Edge Detection
context->BindSR(0, input);
context->SetRenderTarget(*edges);
context->Clear(*edges, Color::Transparent);
context->SetState(_psEdge.Get(qualityLevel));
context->DrawFullscreenTriangle();
context->ResetRenderTarget();
// Blend Weights
context->BindSR(0, edges);
context->BindSR(1, _areaTex->GetTexture());
context->BindSR(2, _searchTex->GetTexture());
context->SetRenderTarget(*weights);
context->SetState(_psBlend.Get(qualityLevel));
context->DrawFullscreenTriangle();
context->ResetRenderTarget();
// Neighborhood Blending
context->BindSR(0, input);
context->BindSR(1, weights);
context->UnBindSR(2);
context->SetRenderTarget(output);
context->SetState(_psNeighbor);
context->DrawFullscreenTriangle();
// Cleanup
context->UnBindSR(0);
context->UnBindSR(1);
context->UnBindSR(2);
RenderTargetPool::Release(edges);
RenderTargetPool::Release(weights);
}