LCOV - code coverage report
Current view: top level - source/renderer - PostprocManager.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 18 355 5.1 %
Date: 2023-01-19 00:18:29 Functions: 6 28 21.4 %

          Line data    Source code
       1             : /* Copyright (C) 2023 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "renderer/PostprocManager.h"
      21             : 
      22             : #include "graphics/GameView.h"
      23             : #include "graphics/LightEnv.h"
      24             : #include "graphics/ShaderManager.h"
      25             : #include "lib/bits.h"
      26             : #include "maths/MathUtil.h"
      27             : #include "ps/ConfigDB.h"
      28             : #include "ps/CLogger.h"
      29             : #include "ps/CStrInternStatic.h"
      30             : #include "ps/Filesystem.h"
      31             : #include "ps/Game.h"
      32             : #include "ps/VideoMode.h"
      33             : #include "ps/World.h"
      34             : #include "renderer/backend/IDevice.h"
      35             : #include "renderer/Renderer.h"
      36             : #include "renderer/RenderingOptions.h"
      37             : #include "tools/atlas/GameInterface/GameLoop.h"
      38             : 
      39             : #include <string_view>
      40             : 
      41             : namespace
      42             : {
      43             : 
      44           0 : void DrawFullscreenQuad(
      45             :     Renderer::Backend::IVertexInputLayout* vertexInputLayout,
      46             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
      47             : {
      48           0 :     float quadVerts[] =
      49             :     {
      50             :         1.0f, 1.0f,
      51             :         -1.0f, 1.0f,
      52             :         -1.0f, -1.0f,
      53             : 
      54             :         -1.0f, -1.0f,
      55             :         1.0f, -1.0f,
      56             :         1.0f, 1.0f
      57             :     };
      58             :     const bool flip =
      59           0 :         deviceCommandContext->GetDevice()->GetBackend() == Renderer::Backend::Backend::VULKAN;
      60           0 :     const float bottomV = flip ? 1.0 : 0.0f;
      61           0 :     const float topV = flip ? 0.0f : 1.0f;
      62           0 :     float quadTex[] =
      63             :     {
      64             :         1.0f, topV,
      65             :         0.0f, topV,
      66             :         0.0f, bottomV,
      67             : 
      68             :         0.0f, bottomV,
      69             :         1.0f, bottomV,
      70             :         1.0f, topV
      71           0 :     };
      72             : 
      73           0 :     deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
      74             : 
      75           0 :     deviceCommandContext->SetVertexBufferData(
      76           0 :         0, quadVerts, std::size(quadVerts) * sizeof(quadVerts[0]));
      77           0 :     deviceCommandContext->SetVertexBufferData(
      78           0 :         1, quadTex, std::size(quadTex) * sizeof(quadTex[0]));
      79             : 
      80           0 :     deviceCommandContext->Draw(0, 6);
      81           0 : }
      82             : 
      83             : } // anonymous namespace
      84             : 
      85           6 : CPostprocManager::CPostprocManager()
      86             :     : m_IsInitialized(false), m_PostProcEffect(L"default"), m_WhichBuffer(true),
      87           6 :     m_Sharpness(0.3f), m_UsingMultisampleBuffer(false), m_MultisampleCount(0)
      88             : {
      89           6 : }
      90             : 
      91          12 : CPostprocManager::~CPostprocManager()
      92             : {
      93           6 :     Cleanup();
      94           6 : }
      95             : 
      96           6 : bool CPostprocManager::IsEnabled() const
      97             : {
      98           6 :     Renderer::Backend::IDevice* device = g_VideoMode.GetBackendDevice();
      99             :     const bool isDepthStencilFormatPresent =
     100           6 :         device->GetPreferredDepthStencilFormat(
     101           6 :             Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT, true, true)
     102           6 :                 != Renderer::Backend::Format::UNDEFINED;
     103             :     return
     104           6 :         g_RenderingOptions.GetPostProc() &&
     105           6 :         device->GetBackend() != Renderer::Backend::Backend::GL_ARB &&
     106           6 :         isDepthStencilFormatPresent;
     107             : }
     108             : 
     109           6 : void CPostprocManager::Cleanup()
     110             : {
     111           6 :     if (!m_IsInitialized) // Only cleanup if previously used
     112           6 :         return;
     113             : 
     114           0 :     m_CaptureFramebuffer.reset();
     115             : 
     116           0 :     m_PingFramebuffer.reset();
     117           0 :     m_PongFramebuffer.reset();
     118             : 
     119           0 :     m_ColorTex1.reset();
     120           0 :     m_ColorTex2.reset();
     121           0 :     m_DepthTex.reset();
     122             : 
     123           0 :     for (BlurScale& scale : m_BlurScales)
     124             :     {
     125           0 :         for (BlurScale::Step& step : scale.steps)
     126             :         {
     127           0 :             step.framebuffer.reset();
     128           0 :             step.texture.reset();
     129             :         }
     130             :     }
     131             : }
     132             : 
     133           0 : void CPostprocManager::Initialize()
     134             : {
     135           0 :     if (m_IsInitialized)
     136           0 :         return;
     137             : 
     138           0 :     const std::array<Renderer::Backend::SVertexAttributeFormat, 2> attributes{{
     139             :         {Renderer::Backend::VertexAttributeStream::POSITION,
     140             :             Renderer::Backend::Format::R32G32_SFLOAT, 0, sizeof(float) * 2,
     141             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     142             :         {Renderer::Backend::VertexAttributeStream::UV0,
     143             :             Renderer::Backend::Format::R32G32_SFLOAT, 0, sizeof(float) * 2,
     144             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 1},
     145             :     }};
     146           0 :     m_VertexInputLayout = g_Renderer.GetVertexInputLayout(attributes);
     147             : 
     148           0 :     const uint32_t maxSamples = g_VideoMode.GetBackendDevice()->GetCapabilities().maxSampleCount;
     149           0 :     const uint32_t possibleSampleCounts[] = {2, 4, 8, 16};
     150           0 :     std::copy_if(
     151             :         std::begin(possibleSampleCounts), std::end(possibleSampleCounts),
     152             :         std::back_inserter(m_AllowedSampleCounts),
     153           0 :         [maxSamples](const uint32_t sampleCount) { return sampleCount <= maxSamples; } );
     154             : 
     155             :     // The screen size starts out correct and then must be updated with Resize()
     156           0 :     m_Width = g_Renderer.GetWidth();
     157           0 :     m_Height = g_Renderer.GetHeight();
     158             : 
     159           0 :     RecreateBuffers();
     160           0 :     m_IsInitialized = true;
     161             : 
     162             :     // Once we have initialised the buffers, we can update the techniques.
     163           0 :     UpdateAntiAliasingTechnique();
     164           0 :     UpdateSharpeningTechnique();
     165           0 :     UpdateSharpnessFactor();
     166             : 
     167             :     // This might happen after the map is loaded and the effect chosen
     168           0 :     SetPostEffect(m_PostProcEffect);
     169             : }
     170             : 
     171           0 : void CPostprocManager::Resize()
     172             : {
     173           0 :     m_Width = g_Renderer.GetWidth();
     174           0 :     m_Height = g_Renderer.GetHeight();
     175             : 
     176             :     // If the buffers were intialized, recreate them to the new size.
     177           0 :     if (m_IsInitialized)
     178           0 :         RecreateBuffers();
     179           0 : }
     180             : 
     181           0 : void CPostprocManager::RecreateBuffers()
     182             : {
     183           0 :     Cleanup();
     184             : 
     185           0 :     Renderer::Backend::IDevice* backendDevice = g_VideoMode.GetBackendDevice();
     186             : 
     187             :     #define GEN_BUFFER_RGBA(name, w, h) \
     188             :         name = backendDevice->CreateTexture2D( \
     189             :             "PostProc" #name, \
     190             :             Renderer::Backend::ITexture::Usage::SAMPLED | \
     191             :                 Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT | \
     192             :                 Renderer::Backend::ITexture::Usage::TRANSFER_SRC | \
     193             :                 Renderer::Backend::ITexture::Usage::TRANSFER_DST, \
     194             :             Renderer::Backend::Format::R8G8B8A8_UNORM, w, h, \
     195             :             Renderer::Backend::Sampler::MakeDefaultSampler( \
     196             :                 Renderer::Backend::Sampler::Filter::LINEAR, \
     197             :                 Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE));
     198             : 
     199             :     // Two fullscreen ping-pong textures.
     200           0 :     GEN_BUFFER_RGBA(m_ColorTex1, m_Width, m_Height);
     201           0 :     GEN_BUFFER_RGBA(m_ColorTex2, m_Width, m_Height);
     202             : 
     203             :     // Textures for several blur sizes. It would be possible to reuse
     204             :     // m_BlurTex2b, thus avoiding the need for m_BlurTex4b and m_BlurTex8b, though given
     205             :     // that these are fairly small it's probably not worth complicating the coordinates passed
     206             :     // to the blur helper functions.
     207           0 :     uint32_t width = m_Width / 2, height = m_Height / 2;
     208           0 :     for (BlurScale& scale : m_BlurScales)
     209             :     {
     210           0 :         for (BlurScale::Step& step : scale.steps)
     211             :         {
     212           0 :             GEN_BUFFER_RGBA(step.texture, width, height);
     213           0 :             Renderer::Backend::SColorAttachment colorAttachment{};
     214           0 :             colorAttachment.texture = step.texture.get();
     215           0 :             colorAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::LOAD;
     216           0 :             colorAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     217           0 :             colorAttachment.clearColor = CColor{0.0f, 0.0f, 0.0f, 0.0f};
     218           0 :             step.framebuffer = backendDevice->CreateFramebuffer(
     219           0 :                 "BlurScaleSteoFramebuffer", &colorAttachment, nullptr);
     220             :         }
     221           0 :         width /= 2;
     222           0 :         height /= 2;
     223             :     }
     224             : 
     225             :     #undef GEN_BUFFER_RGBA
     226             : 
     227             :     // Allocate the Depth/Stencil texture.
     228           0 :     m_DepthTex = backendDevice->CreateTexture2D("PostProcDepthTexture",
     229             :         Renderer::Backend::ITexture::Usage::SAMPLED |
     230             :             Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT,
     231             :         backendDevice->GetPreferredDepthStencilFormat(
     232             :             Renderer::Backend::ITexture::Usage::SAMPLED |
     233             :                 Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT,
     234           0 :             true, true),
     235           0 :         m_Width, m_Height,
     236           0 :         Renderer::Backend::Sampler::MakeDefaultSampler(
     237             :             Renderer::Backend::Sampler::Filter::LINEAR,
     238           0 :             Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE));
     239             : 
     240             :     // Set up the framebuffers with some initial textures.
     241           0 :     Renderer::Backend::SColorAttachment colorAttachment{};
     242           0 :     colorAttachment.texture = m_ColorTex1.get();
     243           0 :     colorAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::DONT_CARE;
     244           0 :     colorAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     245           0 :     colorAttachment.clearColor = CColor{0.0f, 0.0f, 0.0f, 0.0f};
     246             : 
     247           0 :     Renderer::Backend::SDepthStencilAttachment depthStencilAttachment{};
     248           0 :     depthStencilAttachment.texture = m_DepthTex.get();
     249           0 :     depthStencilAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::CLEAR;
     250           0 :     depthStencilAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     251             : 
     252           0 :     m_CaptureFramebuffer = backendDevice->CreateFramebuffer("PostprocCaptureFramebuffer",
     253           0 :         &colorAttachment, &depthStencilAttachment);
     254             : 
     255           0 :     colorAttachment.texture = m_ColorTex1.get();
     256           0 :     colorAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::LOAD;
     257           0 :     colorAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     258           0 :     m_PingFramebuffer = backendDevice->CreateFramebuffer("PostprocPingFramebuffer",
     259           0 :         &colorAttachment, nullptr);
     260             : 
     261           0 :     colorAttachment.texture = m_ColorTex2.get();
     262           0 :     m_PongFramebuffer = backendDevice->CreateFramebuffer("PostprocPongFramebuffer",
     263           0 :         &colorAttachment, nullptr);
     264             : 
     265           0 :     if (!m_CaptureFramebuffer || !m_PingFramebuffer || !m_PongFramebuffer)
     266             :     {
     267           0 :         LOGWARNING("Failed to create postproc framebuffers");
     268           0 :         g_RenderingOptions.SetPostProc(false);
     269             :     }
     270             : 
     271           0 :     if (m_UsingMultisampleBuffer)
     272             :     {
     273           0 :         DestroyMultisampleBuffer();
     274           0 :         CreateMultisampleBuffer();
     275             :     }
     276           0 : }
     277             : 
     278             : 
     279           0 : void CPostprocManager::ApplyBlurDownscale2x(
     280             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     281             :     Renderer::Backend::IFramebuffer* framebuffer,
     282             :     Renderer::Backend::ITexture* inTex, int inWidth, int inHeight)
     283             : {
     284           0 :     deviceCommandContext->BeginFramebufferPass(framebuffer);
     285             : 
     286           0 :     Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
     287           0 :     viewportRect.width = inWidth / 2;
     288           0 :     viewportRect.height = inHeight / 2;
     289           0 :     deviceCommandContext->SetViewports(1, &viewportRect);
     290             : 
     291             :     // Get bloom shader with instructions to simply copy texels.
     292           0 :     CShaderDefines defines;
     293           0 :     defines.Add(str_BLOOM_NOP, str_1);
     294           0 :     CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_bloom, defines);
     295             : 
     296           0 :     deviceCommandContext->SetGraphicsPipelineState(
     297           0 :         tech->GetGraphicsPipelineState());
     298           0 :     deviceCommandContext->BeginPass();
     299           0 :     Renderer::Backend::IShaderProgram* shader = tech->GetShader();
     300             : 
     301           0 :     deviceCommandContext->SetTexture(
     302           0 :         shader->GetBindingSlot(str_renderedTex), inTex);
     303             : 
     304           0 :     DrawFullscreenQuad(m_VertexInputLayout, deviceCommandContext);
     305             : 
     306           0 :     deviceCommandContext->EndPass();
     307           0 :     deviceCommandContext->EndFramebufferPass();
     308           0 : }
     309             : 
     310           0 : void CPostprocManager::ApplyBlurGauss(
     311             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     312             :     Renderer::Backend::ITexture* inTex,
     313             :     Renderer::Backend::ITexture* tempTex,
     314             :     Renderer::Backend::IFramebuffer* tempFramebuffer,
     315             :     Renderer::Backend::IFramebuffer* outFramebuffer,
     316             :     int inWidth, int inHeight)
     317             : {
     318           0 :     deviceCommandContext->BeginFramebufferPass(tempFramebuffer);
     319             : 
     320           0 :     Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
     321           0 :     viewportRect.width = inWidth;
     322           0 :     viewportRect.height = inHeight;
     323           0 :     deviceCommandContext->SetViewports(1, &viewportRect);
     324             : 
     325             :     // Get bloom shader, for a horizontal Gaussian blur pass.
     326           0 :     CShaderDefines defines2;
     327           0 :     defines2.Add(str_BLOOM_PASS_H, str_1);
     328           0 :     CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_bloom, defines2);
     329             : 
     330           0 :     deviceCommandContext->SetGraphicsPipelineState(
     331           0 :         tech->GetGraphicsPipelineState());
     332           0 :     deviceCommandContext->BeginPass();
     333           0 :     Renderer::Backend::IShaderProgram* shader = tech->GetShader();
     334           0 :     deviceCommandContext->SetTexture(
     335           0 :         shader->GetBindingSlot(str_renderedTex), inTex);
     336           0 :     deviceCommandContext->SetUniform(
     337           0 :         shader->GetBindingSlot(str_texSize), inWidth, inHeight);
     338             : 
     339           0 :     DrawFullscreenQuad(m_VertexInputLayout, deviceCommandContext);
     340             : 
     341           0 :     deviceCommandContext->EndPass();
     342           0 :     deviceCommandContext->EndFramebufferPass();
     343             : 
     344           0 :     deviceCommandContext->BeginFramebufferPass(outFramebuffer);
     345             : 
     346           0 :     deviceCommandContext->SetViewports(1, &viewportRect);
     347             : 
     348             :     // Get bloom shader, for a vertical Gaussian blur pass.
     349           0 :     CShaderDefines defines3;
     350           0 :     defines3.Add(str_BLOOM_PASS_V, str_1);
     351           0 :     tech = g_Renderer.GetShaderManager().LoadEffect(str_bloom, defines3);
     352           0 :     deviceCommandContext->SetGraphicsPipelineState(
     353           0 :         tech->GetGraphicsPipelineState());
     354             : 
     355           0 :     deviceCommandContext->BeginPass();
     356           0 :     shader = tech->GetShader();
     357             : 
     358             :     // Our input texture to the shader is the output of the horizontal pass.
     359           0 :     deviceCommandContext->SetTexture(
     360           0 :         shader->GetBindingSlot(str_renderedTex), tempTex);
     361           0 :     deviceCommandContext->SetUniform(
     362           0 :         shader->GetBindingSlot(str_texSize), inWidth, inHeight);
     363             : 
     364           0 :     DrawFullscreenQuad(m_VertexInputLayout, deviceCommandContext);
     365             : 
     366           0 :     deviceCommandContext->EndPass();
     367           0 :     deviceCommandContext->EndFramebufferPass();
     368           0 : }
     369             : 
     370           0 : void CPostprocManager::ApplyBlur(
     371             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     372             : {
     373           0 :     uint32_t width = m_Width, height = m_Height;
     374             :     Renderer::Backend::ITexture* previousTexture =
     375           0 :         (m_WhichBuffer ? m_ColorTex1 : m_ColorTex2).get();
     376             : 
     377           0 :     for (BlurScale& scale : m_BlurScales)
     378             :     {
     379           0 :         ApplyBlurDownscale2x(deviceCommandContext, scale.steps[0].framebuffer.get(), previousTexture, width, height);
     380           0 :         width /= 2;
     381           0 :         height /= 2;
     382           0 :         ApplyBlurGauss(deviceCommandContext, scale.steps[0].texture.get(),
     383           0 :             scale.steps[1].texture.get(), scale.steps[1].framebuffer.get(),
     384           0 :             scale.steps[0].framebuffer.get(), width, height);
     385             :     }
     386           0 : }
     387             : 
     388           0 : Renderer::Backend::IFramebuffer* CPostprocManager::PrepareAndGetOutputFramebuffer()
     389             : {
     390           0 :     ENSURE(m_IsInitialized);
     391             : 
     392             :     // Leaves m_PingFbo selected for rendering; m_WhichBuffer stays true at this point.
     393             : 
     394           0 :     m_WhichBuffer = true;
     395             : 
     396           0 :     return m_UsingMultisampleBuffer ? m_MultisampleFramebuffer.get() : m_CaptureFramebuffer.get();
     397             : }
     398             : 
     399           0 : void CPostprocManager::BlitOutputFramebuffer(
     400             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     401             :     Renderer::Backend::IFramebuffer* destination)
     402             : {
     403           0 :     ENSURE(m_IsInitialized);
     404             : 
     405           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Copy postproc to backbuffer");
     406             : 
     407             :     // We blit to the backbuffer from the previous active buffer.
     408           0 :     deviceCommandContext->BlitFramebuffer(
     409           0 :         destination, (m_WhichBuffer ? m_PingFramebuffer : m_PongFramebuffer).get());
     410           0 : }
     411             : 
     412           0 : void CPostprocManager::ApplyEffect(
     413             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     414             :     const CShaderTechniquePtr& shaderTech, int pass)
     415             : {
     416             :     // select the other FBO for rendering
     417             :     Renderer::Backend::IFramebuffer* framebuffer =
     418           0 :         (m_WhichBuffer ? m_PongFramebuffer : m_PingFramebuffer).get();
     419           0 :     deviceCommandContext->BeginFramebufferPass(framebuffer);
     420             : 
     421           0 :     Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
     422           0 :     viewportRect.width = framebuffer->GetWidth();
     423           0 :     viewportRect.height = framebuffer->GetHeight();
     424           0 :     deviceCommandContext->SetViewports(1, &viewportRect);
     425             : 
     426           0 :     deviceCommandContext->SetGraphicsPipelineState(
     427           0 :         shaderTech->GetGraphicsPipelineState(pass));
     428           0 :     deviceCommandContext->BeginPass();
     429           0 :     Renderer::Backend::IShaderProgram* shader = shaderTech->GetShader(pass);
     430             : 
     431             :     // Use the textures from the current FBO as input to the shader.
     432             :     // We also bind a bunch of other textures and parameters, but since
     433             :     // this only happens once per frame the overhead is negligible.
     434           0 :     deviceCommandContext->SetTexture(
     435           0 :         shader->GetBindingSlot(str_renderedTex),
     436           0 :         m_WhichBuffer ? m_ColorTex1.get() : m_ColorTex2.get());
     437           0 :     deviceCommandContext->SetTexture(
     438           0 :         shader->GetBindingSlot(str_depthTex), m_DepthTex.get());
     439             : 
     440           0 :     deviceCommandContext->SetTexture(
     441           0 :         shader->GetBindingSlot(str_blurTex2), m_BlurScales[0].steps[0].texture.get());
     442           0 :     deviceCommandContext->SetTexture(
     443           0 :         shader->GetBindingSlot(str_blurTex4), m_BlurScales[1].steps[0].texture.get());
     444           0 :     deviceCommandContext->SetTexture(
     445           0 :         shader->GetBindingSlot(str_blurTex8), m_BlurScales[2].steps[0].texture.get());
     446             : 
     447           0 :     deviceCommandContext->SetUniform(shader->GetBindingSlot(str_width), m_Width);
     448           0 :     deviceCommandContext->SetUniform(shader->GetBindingSlot(str_height), m_Height);
     449           0 :     deviceCommandContext->SetUniform(shader->GetBindingSlot(str_zNear), m_NearPlane);
     450           0 :     deviceCommandContext->SetUniform(shader->GetBindingSlot(str_zFar), m_FarPlane);
     451             : 
     452           0 :     deviceCommandContext->SetUniform(shader->GetBindingSlot(str_sharpness), m_Sharpness);
     453             : 
     454           0 :     deviceCommandContext->SetUniform(shader->GetBindingSlot(str_brightness), g_LightEnv.m_Brightness);
     455           0 :     deviceCommandContext->SetUniform(shader->GetBindingSlot(str_hdr), g_LightEnv.m_Contrast);
     456           0 :     deviceCommandContext->SetUniform(shader->GetBindingSlot(str_saturation), g_LightEnv.m_Saturation);
     457           0 :     deviceCommandContext->SetUniform(shader->GetBindingSlot(str_bloom), g_LightEnv.m_Bloom);
     458             : 
     459           0 :     DrawFullscreenQuad(m_VertexInputLayout, deviceCommandContext);
     460             : 
     461           0 :     deviceCommandContext->EndPass();
     462           0 :     deviceCommandContext->EndFramebufferPass();
     463             : 
     464           0 :     m_WhichBuffer = !m_WhichBuffer;
     465           0 : }
     466             : 
     467           0 : void CPostprocManager::ApplyPostproc(
     468             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     469             : {
     470           0 :     ENSURE(m_IsInitialized);
     471             : 
     472             :     // Don't do anything if we are using the default effect and no AA.
     473           0 :     const bool hasEffects = m_PostProcEffect != L"default";
     474           0 :     const bool hasARB = g_VideoMode.GetBackendDevice()->GetBackend() == Renderer::Backend::Backend::GL_ARB;
     475           0 :     const bool hasAA = m_AATech && !hasARB;
     476           0 :     const bool hasSharp = m_SharpTech && !hasARB;
     477           0 :     if (!hasEffects && !hasAA && !hasSharp)
     478           0 :         return;
     479             : 
     480           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render postproc");
     481             : 
     482           0 :     if (hasEffects)
     483             :     {
     484             :         // First render blur textures. Note that this only happens ONLY ONCE, before any effects are applied!
     485             :         // (This may need to change depending on future usage, however that will have a fps hit)
     486           0 :         ApplyBlur(deviceCommandContext);
     487           0 :         for (int pass = 0; pass < m_PostProcTech->GetNumPasses(); ++pass)
     488           0 :             ApplyEffect(deviceCommandContext, m_PostProcTech, pass);
     489             :     }
     490             : 
     491           0 :     if (hasAA)
     492             :     {
     493           0 :         for (int pass = 0; pass < m_AATech->GetNumPasses(); ++pass)
     494           0 :             ApplyEffect(deviceCommandContext, m_AATech, pass);
     495             :     }
     496             : 
     497           0 :     if (hasSharp)
     498             :     {
     499           0 :         for (int pass = 0; pass < m_SharpTech->GetNumPasses(); ++pass)
     500           0 :             ApplyEffect(deviceCommandContext, m_SharpTech, pass);
     501             :     }
     502             : }
     503             : 
     504             : 
     505             : // Generate list of available effect-sets
     506           0 : std::vector<CStrW> CPostprocManager::GetPostEffects()
     507             : {
     508           0 :     std::vector<CStrW> effects;
     509             : 
     510           0 :     const VfsPath folder(L"shaders/effects/postproc/");
     511             : 
     512           0 :     VfsPaths pathnames;
     513           0 :     if (vfs::GetPathnames(g_VFS, folder, 0, pathnames) < 0)
     514           0 :         LOGERROR("Error finding Post effects in '%s'", folder.string8());
     515             : 
     516           0 :     for (const VfsPath& path : pathnames)
     517           0 :         if (path.Extension() == L".xml")
     518           0 :             effects.push_back(path.Basename().string());
     519             : 
     520             :     // Add the default "null" effect to the list.
     521           0 :     effects.push_back(L"default");
     522             : 
     523           0 :     sort(effects.begin(), effects.end());
     524             : 
     525           0 :     return effects;
     526             : }
     527             : 
     528           0 : void CPostprocManager::SetPostEffect(const CStrW& name)
     529             : {
     530           0 :     if (m_IsInitialized)
     531             :     {
     532           0 :         if (name != L"default")
     533             :         {
     534           0 :             CStrW n = L"postproc/" + name;
     535           0 :             m_PostProcTech = g_Renderer.GetShaderManager().LoadEffect(CStrIntern(n.ToUTF8()));
     536             :         }
     537             :     }
     538             : 
     539           0 :     m_PostProcEffect = name;
     540           0 : }
     541             : 
     542           0 : void CPostprocManager::UpdateAntiAliasingTechnique()
     543             : {
     544           0 :     Renderer::Backend::IDevice* device = g_VideoMode.GetBackendDevice();
     545           0 :     if (device->GetBackend() == Renderer::Backend::Backend::GL_ARB || !m_IsInitialized)
     546           0 :         return;
     547             : 
     548           0 :     CStr newAAName;
     549           0 :     CFG_GET_VAL("antialiasing", newAAName);
     550           0 :     if (m_AAName == newAAName)
     551           0 :         return;
     552           0 :     m_AAName = newAAName;
     553           0 :     m_AATech.reset();
     554             : 
     555           0 :     if (m_UsingMultisampleBuffer)
     556             :     {
     557           0 :         m_UsingMultisampleBuffer = false;
     558           0 :         DestroyMultisampleBuffer();
     559             :     }
     560             : 
     561             :     // We have to hardcode names in the engine, because anti-aliasing
     562             :     // techinques strongly depend on the graphics pipeline.
     563             :     // We might use enums in future though.
     564           0 :     constexpr std::string_view msaaPrefix{"msaa"};
     565           0 :     if (m_AAName == "fxaa")
     566             :     {
     567           0 :         m_AATech = g_Renderer.GetShaderManager().LoadEffect(CStrIntern("fxaa"));
     568             :     }
     569           0 :     else if (m_AAName.size() > msaaPrefix.size() &&
     570           0 :         std::string_view{m_AAName}.substr(0, msaaPrefix.size()) == msaaPrefix)
     571             :     {
     572             :         // We don't want to enable MSAA in Atlas, because it uses wxWidgets and its canvas.
     573           0 :         if (g_AtlasGameLoop && g_AtlasGameLoop->running)
     574           0 :             return;
     575           0 :         if (!device->GetCapabilities().multisampling || m_AllowedSampleCounts.empty())
     576             :         {
     577           0 :             LOGWARNING("MSAA is unsupported.");
     578           0 :             return;
     579             :         }
     580           0 :         std::stringstream ss(m_AAName.substr(msaaPrefix.size()));
     581           0 :         ss >> m_MultisampleCount;
     582           0 :         if (std::find(std::begin(m_AllowedSampleCounts), std::end(m_AllowedSampleCounts), m_MultisampleCount) ==
     583           0 :                 std::end(m_AllowedSampleCounts))
     584             :         {
     585           0 :             m_MultisampleCount = std::min(4u, device->GetCapabilities().maxSampleCount);
     586           0 :             LOGWARNING("Wrong MSAA sample count: %s.", m_AAName.EscapeToPrintableASCII().c_str());
     587             :         }
     588           0 :         m_UsingMultisampleBuffer = true;
     589           0 :         CreateMultisampleBuffer();
     590             :     }
     591             : }
     592             : 
     593           0 : void CPostprocManager::UpdateSharpeningTechnique()
     594             : {
     595           0 :     if (g_VideoMode.GetBackendDevice()->GetBackend() == Renderer::Backend::Backend::GL_ARB || !m_IsInitialized)
     596           0 :         return;
     597             : 
     598           0 :     CStr newSharpName;
     599           0 :     CFG_GET_VAL("sharpening", newSharpName);
     600           0 :     if (m_SharpName == newSharpName)
     601           0 :         return;
     602           0 :     m_SharpName = newSharpName;
     603           0 :     m_SharpTech.reset();
     604             : 
     605           0 :     if (m_SharpName == "cas")
     606             :     {
     607           0 :         m_SharpTech = g_Renderer.GetShaderManager().LoadEffect(CStrIntern(m_SharpName));
     608             :     }
     609             : }
     610             : 
     611           0 : void CPostprocManager::UpdateSharpnessFactor()
     612             : {
     613           0 :     CFG_GET_VAL("sharpness", m_Sharpness);
     614           0 : }
     615             : 
     616           0 : void CPostprocManager::SetDepthBufferClipPlanes(float nearPlane, float farPlane)
     617             : {
     618           0 :     m_NearPlane = nearPlane;
     619           0 :     m_FarPlane = farPlane;
     620           0 : }
     621             : 
     622           0 : void CPostprocManager::CreateMultisampleBuffer()
     623             : {
     624           0 :     Renderer::Backend::IDevice* backendDevice = g_VideoMode.GetBackendDevice();
     625             : 
     626           0 :     m_MultisampleColorTex = backendDevice->CreateTexture("PostProcColorMS",
     627             :         Renderer::Backend::ITexture::Type::TEXTURE_2D_MULTISAMPLE,
     628             :         Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT |
     629             :             Renderer::Backend::ITexture::Usage::TRANSFER_SRC,
     630           0 :         Renderer::Backend::Format::R8G8B8A8_UNORM, m_Width, m_Height,
     631           0 :         Renderer::Backend::Sampler::MakeDefaultSampler(
     632             :             Renderer::Backend::Sampler::Filter::LINEAR,
     633           0 :             Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE), 1, m_MultisampleCount);
     634             : 
     635             :     // Allocate the Depth/Stencil texture.
     636           0 :     m_MultisampleDepthTex = backendDevice->CreateTexture("PostProcDepthMS",
     637             :         Renderer::Backend::ITexture::Type::TEXTURE_2D_MULTISAMPLE,
     638             :         Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT |
     639             :             Renderer::Backend::ITexture::Usage::TRANSFER_SRC,
     640             :         backendDevice->GetPreferredDepthStencilFormat(
     641             :             Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT |
     642             :                 Renderer::Backend::ITexture::Usage::TRANSFER_SRC,
     643           0 :             true, true),
     644           0 :         m_Width, m_Height,
     645           0 :         Renderer::Backend::Sampler::MakeDefaultSampler(
     646             :             Renderer::Backend::Sampler::Filter::LINEAR,
     647           0 :             Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE), 1, m_MultisampleCount);
     648             : 
     649             :     // Set up the framebuffers with some initial textures.
     650           0 :     Renderer::Backend::SColorAttachment colorAttachment{};
     651           0 :     colorAttachment.texture = m_MultisampleColorTex.get();
     652           0 :     colorAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::DONT_CARE;
     653           0 :     colorAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     654           0 :     colorAttachment.clearColor = CColor{0.0f, 0.0f, 0.0f, 0.0f};
     655             : 
     656           0 :     Renderer::Backend::SDepthStencilAttachment depthStencilAttachment{};
     657           0 :     depthStencilAttachment.texture = m_MultisampleDepthTex.get();
     658           0 :     depthStencilAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::CLEAR;
     659           0 :     depthStencilAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     660             : 
     661           0 :     m_MultisampleFramebuffer = backendDevice->CreateFramebuffer(
     662           0 :         "PostprocMultisampleFramebuffer", &colorAttachment, &depthStencilAttachment);
     663             : 
     664           0 :     if (!m_MultisampleFramebuffer)
     665             :     {
     666           0 :         LOGERROR("Failed to create postproc multisample framebuffer");
     667           0 :         m_UsingMultisampleBuffer = false;
     668           0 :         DestroyMultisampleBuffer();
     669             :     }
     670           0 : }
     671             : 
     672           0 : void CPostprocManager::DestroyMultisampleBuffer()
     673             : {
     674           0 :     if (m_UsingMultisampleBuffer)
     675           0 :         return;
     676           0 :     m_MultisampleFramebuffer.reset();
     677           0 :     m_MultisampleColorTex.reset();
     678           0 :     m_MultisampleDepthTex.reset();
     679             : }
     680             : 
     681           0 : bool CPostprocManager::IsMultisampleEnabled() const
     682             : {
     683           0 :     return m_UsingMultisampleBuffer;
     684             : }
     685             : 
     686           0 : void CPostprocManager::ResolveMultisampleFramebuffer(
     687             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     688             : {
     689           0 :     if (!m_UsingMultisampleBuffer)
     690           0 :         return;
     691             : 
     692           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Resolve postproc multisample");
     693           0 :     deviceCommandContext->BlitFramebuffer(
     694           0 :         m_PingFramebuffer.get(), m_MultisampleFramebuffer.get());
     695           3 : }

Generated by: LCOV version 1.13