Merge 'remotes/trunk'
[0ad.git] / source / renderer / PostprocManager.cpp
blobe27954f672033233cc8a4feff79cde4e1062903f
1 /* Copyright (C) 2023 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include "renderer/PostprocManager.h"
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/World.h"
33 #include "renderer/backend/IDevice.h"
34 #include "renderer/Renderer.h"
35 #include "renderer/RenderingOptions.h"
36 #include "tools/atlas/GameInterface/GameLoop.h"
38 #include <string_view>
40 namespace
43 void DrawFullscreenQuad(
44 Renderer::Backend::IVertexInputLayout* vertexInputLayout,
45 Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
47 float quadVerts[] =
49 1.0f, 1.0f,
50 -1.0f, 1.0f,
51 -1.0f, -1.0f,
53 -1.0f, -1.0f,
54 1.0f, -1.0f,
55 1.0f, 1.0f
57 const bool flip =
58 deviceCommandContext->GetDevice()->GetBackend() == Renderer::Backend::Backend::VULKAN;
59 const float bottomV = flip ? 1.0 : 0.0f;
60 const float topV = flip ? 0.0f : 1.0f;
61 float quadTex[] =
63 1.0f, topV,
64 0.0f, topV,
65 0.0f, bottomV,
67 0.0f, bottomV,
68 1.0f, bottomV,
69 1.0f, topV
72 deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
74 deviceCommandContext->SetVertexBufferData(
75 0, quadVerts, std::size(quadVerts) * sizeof(quadVerts[0]));
76 deviceCommandContext->SetVertexBufferData(
77 1, quadTex, std::size(quadTex) * sizeof(quadTex[0]));
79 deviceCommandContext->Draw(0, 6);
82 } // anonymous namespace
84 CPostprocManager::CPostprocManager(Renderer::Backend::IDevice* device)
85 : m_Device(device), m_IsInitialized(false), m_PostProcEffect(L"default"),
86 m_WhichBuffer(true), m_Sharpness(0.3f), m_UsingMultisampleBuffer(false),
87 m_MultisampleCount(0)
91 CPostprocManager::~CPostprocManager()
93 Cleanup();
96 bool CPostprocManager::IsEnabled() const
98 const bool isDepthStencilFormatPresent =
99 m_Device->GetPreferredDepthStencilFormat(
100 Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT, true, true)
101 != Renderer::Backend::Format::UNDEFINED;
102 return
103 g_RenderingOptions.GetPostProc() &&
104 m_Device->GetBackend() != Renderer::Backend::Backend::GL_ARB &&
105 isDepthStencilFormatPresent;
108 void CPostprocManager::Cleanup()
110 if (!m_IsInitialized) // Only cleanup if previously used
111 return;
113 m_CaptureFramebuffer.reset();
115 m_PingFramebuffer.reset();
116 m_PongFramebuffer.reset();
118 m_ColorTex1.reset();
119 m_ColorTex2.reset();
120 m_DepthTex.reset();
122 for (BlurScale& scale : m_BlurScales)
124 for (BlurScale::Step& step : scale.steps)
126 step.framebuffer.reset();
127 step.texture.reset();
132 void CPostprocManager::Initialize()
134 if (m_IsInitialized)
135 return;
137 const std::array<Renderer::Backend::SVertexAttributeFormat, 2> attributes{{
138 {Renderer::Backend::VertexAttributeStream::POSITION,
139 Renderer::Backend::Format::R32G32_SFLOAT, 0, sizeof(float) * 2,
140 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
141 {Renderer::Backend::VertexAttributeStream::UV0,
142 Renderer::Backend::Format::R32G32_SFLOAT, 0, sizeof(float) * 2,
143 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 1},
145 m_VertexInputLayout = g_Renderer.GetVertexInputLayout(attributes);
147 const uint32_t maxSamples = m_Device->GetCapabilities().maxSampleCount;
148 const uint32_t possibleSampleCounts[] = {2, 4, 8, 16};
149 std::copy_if(
150 std::begin(possibleSampleCounts), std::end(possibleSampleCounts),
151 std::back_inserter(m_AllowedSampleCounts),
152 [maxSamples](const uint32_t sampleCount) { return sampleCount <= maxSamples; } );
154 // The screen size starts out correct and then must be updated with Resize()
155 m_Width = g_Renderer.GetWidth();
156 m_Height = g_Renderer.GetHeight();
158 RecreateBuffers();
159 m_IsInitialized = true;
161 // Once we have initialised the buffers, we can update the techniques.
162 UpdateAntiAliasingTechnique();
163 UpdateSharpeningTechnique();
164 UpdateSharpnessFactor();
166 // This might happen after the map is loaded and the effect chosen
167 SetPostEffect(m_PostProcEffect);
170 void CPostprocManager::Resize()
172 m_Width = g_Renderer.GetWidth();
173 m_Height = g_Renderer.GetHeight();
175 // If the buffers were intialized, recreate them to the new size.
176 if (m_IsInitialized)
177 RecreateBuffers();
180 void CPostprocManager::RecreateBuffers()
182 Cleanup();
184 #define GEN_BUFFER_RGBA(name, w, h) \
185 name = m_Device->CreateTexture2D( \
186 "PostProc" #name, \
187 Renderer::Backend::ITexture::Usage::SAMPLED | \
188 Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT | \
189 Renderer::Backend::ITexture::Usage::TRANSFER_SRC | \
190 Renderer::Backend::ITexture::Usage::TRANSFER_DST, \
191 Renderer::Backend::Format::R8G8B8A8_UNORM, w, h, \
192 Renderer::Backend::Sampler::MakeDefaultSampler( \
193 Renderer::Backend::Sampler::Filter::LINEAR, \
194 Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE));
196 // Two fullscreen ping-pong textures.
197 GEN_BUFFER_RGBA(m_ColorTex1, m_Width, m_Height);
198 GEN_BUFFER_RGBA(m_ColorTex2, m_Width, m_Height);
200 // Textures for several blur sizes. It would be possible to reuse
201 // m_BlurTex2b, thus avoiding the need for m_BlurTex4b and m_BlurTex8b, though given
202 // that these are fairly small it's probably not worth complicating the coordinates passed
203 // to the blur helper functions.
204 uint32_t width = m_Width / 2, height = m_Height / 2;
205 for (BlurScale& scale : m_BlurScales)
207 for (BlurScale::Step& step : scale.steps)
209 GEN_BUFFER_RGBA(step.texture, width, height);
210 Renderer::Backend::SColorAttachment colorAttachment{};
211 colorAttachment.texture = step.texture.get();
212 colorAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::LOAD;
213 colorAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
214 colorAttachment.clearColor = CColor{0.0f, 0.0f, 0.0f, 0.0f};
215 step.framebuffer = m_Device->CreateFramebuffer(
216 "BlurScaleStepFramebuffer", &colorAttachment, nullptr);
218 width = std::max(1u, width / 2);
219 height = std::max(1u, height / 2);
222 #undef GEN_BUFFER_RGBA
224 // Allocate the Depth/Stencil texture.
225 m_DepthTex = m_Device->CreateTexture2D("PostProcDepthTexture",
226 Renderer::Backend::ITexture::Usage::SAMPLED |
227 Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT,
228 m_Device->GetPreferredDepthStencilFormat(
229 Renderer::Backend::ITexture::Usage::SAMPLED |
230 Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT,
231 true, true),
232 m_Width, m_Height,
233 Renderer::Backend::Sampler::MakeDefaultSampler(
234 Renderer::Backend::Sampler::Filter::LINEAR,
235 Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE));
237 // Set up the framebuffers with some initial textures.
238 Renderer::Backend::SColorAttachment colorAttachment{};
239 colorAttachment.texture = m_ColorTex1.get();
240 colorAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::DONT_CARE;
241 colorAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
242 colorAttachment.clearColor = CColor{0.0f, 0.0f, 0.0f, 0.0f};
244 Renderer::Backend::SDepthStencilAttachment depthStencilAttachment{};
245 depthStencilAttachment.texture = m_DepthTex.get();
246 depthStencilAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::CLEAR;
247 depthStencilAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
249 m_CaptureFramebuffer = m_Device->CreateFramebuffer("PostprocCaptureFramebuffer",
250 &colorAttachment, &depthStencilAttachment);
252 colorAttachment.texture = m_ColorTex1.get();
253 colorAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::LOAD;
254 colorAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
255 m_PingFramebuffer = m_Device->CreateFramebuffer("PostprocPingFramebuffer",
256 &colorAttachment, nullptr);
258 colorAttachment.texture = m_ColorTex2.get();
259 m_PongFramebuffer = m_Device->CreateFramebuffer("PostprocPongFramebuffer",
260 &colorAttachment, nullptr);
262 if (!m_CaptureFramebuffer || !m_PingFramebuffer || !m_PongFramebuffer)
264 LOGWARNING("Failed to create postproc framebuffers");
265 g_RenderingOptions.SetPostProc(false);
268 if (m_UsingMultisampleBuffer)
270 DestroyMultisampleBuffer();
271 CreateMultisampleBuffer();
276 void CPostprocManager::ApplyBlurDownscale2x(
277 Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
278 Renderer::Backend::IFramebuffer* framebuffer,
279 Renderer::Backend::ITexture* inTex, int inWidth, int inHeight)
281 deviceCommandContext->BeginFramebufferPass(framebuffer);
283 Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
284 viewportRect.width = inWidth / 2;
285 viewportRect.height = inHeight / 2;
286 deviceCommandContext->SetViewports(1, &viewportRect);
288 // Get bloom shader with instructions to simply copy texels.
289 CShaderDefines defines;
290 defines.Add(str_BLOOM_NOP, str_1);
291 CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_bloom, defines);
293 deviceCommandContext->SetGraphicsPipelineState(
294 tech->GetGraphicsPipelineState());
295 deviceCommandContext->BeginPass();
296 Renderer::Backend::IShaderProgram* shader = tech->GetShader();
298 deviceCommandContext->SetTexture(
299 shader->GetBindingSlot(str_renderedTex), inTex);
301 DrawFullscreenQuad(m_VertexInputLayout, deviceCommandContext);
303 deviceCommandContext->EndPass();
304 deviceCommandContext->EndFramebufferPass();
307 void CPostprocManager::ApplyBlurGauss(
308 Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
309 Renderer::Backend::ITexture* inTex,
310 Renderer::Backend::ITexture* tempTex,
311 Renderer::Backend::IFramebuffer* tempFramebuffer,
312 Renderer::Backend::IFramebuffer* outFramebuffer,
313 int inWidth, int inHeight)
315 deviceCommandContext->BeginFramebufferPass(tempFramebuffer);
317 Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
318 viewportRect.width = inWidth;
319 viewportRect.height = inHeight;
320 deviceCommandContext->SetViewports(1, &viewportRect);
322 // Get bloom shader, for a horizontal Gaussian blur pass.
323 CShaderDefines defines2;
324 defines2.Add(str_BLOOM_PASS_H, str_1);
325 CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_bloom, defines2);
327 deviceCommandContext->SetGraphicsPipelineState(
328 tech->GetGraphicsPipelineState());
329 deviceCommandContext->BeginPass();
330 Renderer::Backend::IShaderProgram* shader = tech->GetShader();
331 deviceCommandContext->SetTexture(
332 shader->GetBindingSlot(str_renderedTex), inTex);
333 deviceCommandContext->SetUniform(
334 shader->GetBindingSlot(str_texSize), inWidth, inHeight);
336 DrawFullscreenQuad(m_VertexInputLayout, deviceCommandContext);
338 deviceCommandContext->EndPass();
339 deviceCommandContext->EndFramebufferPass();
341 deviceCommandContext->BeginFramebufferPass(outFramebuffer);
343 deviceCommandContext->SetViewports(1, &viewportRect);
345 // Get bloom shader, for a vertical Gaussian blur pass.
346 CShaderDefines defines3;
347 defines3.Add(str_BLOOM_PASS_V, str_1);
348 tech = g_Renderer.GetShaderManager().LoadEffect(str_bloom, defines3);
349 deviceCommandContext->SetGraphicsPipelineState(
350 tech->GetGraphicsPipelineState());
352 deviceCommandContext->BeginPass();
353 shader = tech->GetShader();
355 // Our input texture to the shader is the output of the horizontal pass.
356 deviceCommandContext->SetTexture(
357 shader->GetBindingSlot(str_renderedTex), tempTex);
358 deviceCommandContext->SetUniform(
359 shader->GetBindingSlot(str_texSize), inWidth, inHeight);
361 DrawFullscreenQuad(m_VertexInputLayout, deviceCommandContext);
363 deviceCommandContext->EndPass();
364 deviceCommandContext->EndFramebufferPass();
367 void CPostprocManager::ApplyBlur(
368 Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
370 uint32_t width = m_Width, height = m_Height;
371 Renderer::Backend::ITexture* previousTexture =
372 (m_WhichBuffer ? m_ColorTex1 : m_ColorTex2).get();
374 for (BlurScale& scale : m_BlurScales)
376 ApplyBlurDownscale2x(deviceCommandContext, scale.steps[0].framebuffer.get(), previousTexture, width, height);
377 width /= 2;
378 height /= 2;
379 ApplyBlurGauss(deviceCommandContext, scale.steps[0].texture.get(),
380 scale.steps[1].texture.get(), scale.steps[1].framebuffer.get(),
381 scale.steps[0].framebuffer.get(), width, height);
385 Renderer::Backend::IFramebuffer* CPostprocManager::PrepareAndGetOutputFramebuffer()
387 ENSURE(m_IsInitialized);
389 // Leaves m_PingFbo selected for rendering; m_WhichBuffer stays true at this point.
391 m_WhichBuffer = true;
393 return m_UsingMultisampleBuffer ? m_MultisampleFramebuffer.get() : m_CaptureFramebuffer.get();
396 void CPostprocManager::BlitOutputFramebuffer(
397 Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
398 Renderer::Backend::IFramebuffer* destination)
400 ENSURE(m_IsInitialized);
402 GPU_SCOPED_LABEL(deviceCommandContext, "Copy postproc to backbuffer");
404 Renderer::Backend::IFramebuffer* source =
405 (m_WhichBuffer ? m_PingFramebuffer : m_PongFramebuffer).get();
407 // We blit to the backbuffer from the previous active buffer.
408 // We'll have upscaling/downscaling separately.
409 Renderer::Backend::IDeviceCommandContext::Rect region{};
410 region.width = std::min(source->GetWidth(), destination->GetWidth());
411 region.height = std::min(source->GetHeight(), destination->GetHeight());
412 deviceCommandContext->BlitFramebuffer(
413 source, destination, region, region,
414 Renderer::Backend::Sampler::Filter::NEAREST);
417 void CPostprocManager::ApplyEffect(
418 Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
419 const CShaderTechniquePtr& shaderTech, int pass)
421 // select the other FBO for rendering
422 Renderer::Backend::IFramebuffer* framebuffer =
423 (m_WhichBuffer ? m_PongFramebuffer : m_PingFramebuffer).get();
424 deviceCommandContext->BeginFramebufferPass(framebuffer);
426 Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
427 viewportRect.width = framebuffer->GetWidth();
428 viewportRect.height = framebuffer->GetHeight();
429 deviceCommandContext->SetViewports(1, &viewportRect);
431 deviceCommandContext->SetGraphicsPipelineState(
432 shaderTech->GetGraphicsPipelineState(pass));
433 deviceCommandContext->BeginPass();
434 Renderer::Backend::IShaderProgram* shader = shaderTech->GetShader(pass);
436 // Use the textures from the current FBO as input to the shader.
437 // We also bind a bunch of other textures and parameters, but since
438 // this only happens once per frame the overhead is negligible.
439 deviceCommandContext->SetTexture(
440 shader->GetBindingSlot(str_renderedTex),
441 m_WhichBuffer ? m_ColorTex1.get() : m_ColorTex2.get());
442 deviceCommandContext->SetTexture(
443 shader->GetBindingSlot(str_depthTex), m_DepthTex.get());
445 deviceCommandContext->SetTexture(
446 shader->GetBindingSlot(str_blurTex2), m_BlurScales[0].steps[0].texture.get());
447 deviceCommandContext->SetTexture(
448 shader->GetBindingSlot(str_blurTex4), m_BlurScales[1].steps[0].texture.get());
449 deviceCommandContext->SetTexture(
450 shader->GetBindingSlot(str_blurTex8), m_BlurScales[2].steps[0].texture.get());
452 deviceCommandContext->SetUniform(shader->GetBindingSlot(str_width), m_Width);
453 deviceCommandContext->SetUniform(shader->GetBindingSlot(str_height), m_Height);
454 deviceCommandContext->SetUniform(shader->GetBindingSlot(str_zNear), m_NearPlane);
455 deviceCommandContext->SetUniform(shader->GetBindingSlot(str_zFar), m_FarPlane);
457 deviceCommandContext->SetUniform(shader->GetBindingSlot(str_sharpness), m_Sharpness);
459 deviceCommandContext->SetUniform(shader->GetBindingSlot(str_brightness), g_LightEnv.m_Brightness);
460 deviceCommandContext->SetUniform(shader->GetBindingSlot(str_hdr), g_LightEnv.m_Contrast);
461 deviceCommandContext->SetUniform(shader->GetBindingSlot(str_saturation), g_LightEnv.m_Saturation);
462 deviceCommandContext->SetUniform(shader->GetBindingSlot(str_bloom), g_LightEnv.m_Bloom);
464 DrawFullscreenQuad(m_VertexInputLayout, deviceCommandContext);
466 deviceCommandContext->EndPass();
467 deviceCommandContext->EndFramebufferPass();
469 m_WhichBuffer = !m_WhichBuffer;
472 void CPostprocManager::ApplyPostproc(
473 Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
475 ENSURE(m_IsInitialized);
477 // Don't do anything if we are using the default effect and no AA.
478 const bool hasEffects = m_PostProcEffect != L"default";
479 const bool hasARB = m_Device->GetBackend() == Renderer::Backend::Backend::GL_ARB;
480 const bool hasAA = m_AATech && !hasARB;
481 const bool hasSharp = m_SharpTech && !hasARB;
482 if (!hasEffects && !hasAA && !hasSharp)
483 return;
485 GPU_SCOPED_LABEL(deviceCommandContext, "Render postproc");
487 if (hasEffects)
489 // First render blur textures. Note that this only happens ONLY ONCE, before any effects are applied!
490 // (This may need to change depending on future usage, however that will have a fps hit)
491 ApplyBlur(deviceCommandContext);
492 for (int pass = 0; pass < m_PostProcTech->GetNumPasses(); ++pass)
493 ApplyEffect(deviceCommandContext, m_PostProcTech, pass);
496 if (hasAA)
498 for (int pass = 0; pass < m_AATech->GetNumPasses(); ++pass)
499 ApplyEffect(deviceCommandContext, m_AATech, pass);
502 if (hasSharp)
504 for (int pass = 0; pass < m_SharpTech->GetNumPasses(); ++pass)
505 ApplyEffect(deviceCommandContext, m_SharpTech, pass);
510 // Generate list of available effect-sets
511 std::vector<CStrW> CPostprocManager::GetPostEffects()
513 std::vector<CStrW> effects;
515 const VfsPath folder(L"shaders/effects/postproc/");
517 VfsPaths pathnames;
518 if (vfs::GetPathnames(g_VFS, folder, 0, pathnames) < 0)
519 LOGERROR("Error finding Post effects in '%s'", folder.string8());
521 for (const VfsPath& path : pathnames)
522 if (path.Extension() == L".xml")
523 effects.push_back(path.Basename().string());
525 // Add the default "null" effect to the list.
526 effects.push_back(L"default");
528 sort(effects.begin(), effects.end());
530 return effects;
533 void CPostprocManager::SetPostEffect(const CStrW& name)
535 if (m_IsInitialized)
537 if (name != L"default")
539 CStrW n = L"postproc/" + name;
540 m_PostProcTech = g_Renderer.GetShaderManager().LoadEffect(CStrIntern(n.ToUTF8()));
544 m_PostProcEffect = name;
547 void CPostprocManager::UpdateAntiAliasingTechnique()
549 if (m_Device->GetBackend() == Renderer::Backend::Backend::GL_ARB || !m_IsInitialized)
550 return;
552 CStr newAAName;
553 CFG_GET_VAL("antialiasing", newAAName);
554 if (m_AAName == newAAName)
555 return;
556 m_AAName = newAAName;
557 m_AATech.reset();
559 if (m_UsingMultisampleBuffer)
561 m_UsingMultisampleBuffer = false;
562 DestroyMultisampleBuffer();
565 // We have to hardcode names in the engine, because anti-aliasing
566 // techinques strongly depend on the graphics pipeline.
567 // We might use enums in future though.
568 constexpr std::string_view msaaPrefix{"msaa"};
569 if (m_AAName == str_fxaa.string())
571 m_AATech = g_Renderer.GetShaderManager().LoadEffect(str_fxaa);
573 else if (m_AAName.size() > msaaPrefix.size() &&
574 std::string_view{m_AAName}.substr(0, msaaPrefix.size()) == msaaPrefix)
576 // We don't want to enable MSAA in Atlas, because it uses wxWidgets and its canvas.
577 if (g_AtlasGameLoop && g_AtlasGameLoop->running)
578 return;
579 if (!m_Device->GetCapabilities().multisampling || m_AllowedSampleCounts.empty())
581 LOGWARNING("MSAA is unsupported.");
582 return;
584 std::stringstream ss(m_AAName.substr(msaaPrefix.size()));
585 ss >> m_MultisampleCount;
586 if (std::find(std::begin(m_AllowedSampleCounts), std::end(m_AllowedSampleCounts), m_MultisampleCount) ==
587 std::end(m_AllowedSampleCounts))
589 m_MultisampleCount = std::min(4u, m_Device->GetCapabilities().maxSampleCount);
590 LOGWARNING("Wrong MSAA sample count: %s.", m_AAName.EscapeToPrintableASCII().c_str());
592 m_UsingMultisampleBuffer = true;
593 CreateMultisampleBuffer();
597 void CPostprocManager::UpdateSharpeningTechnique()
599 if (m_Device->GetBackend() == Renderer::Backend::Backend::GL_ARB || !m_IsInitialized)
600 return;
602 CStr newSharpName;
603 CFG_GET_VAL("sharpening", newSharpName);
604 if (m_SharpName == newSharpName)
605 return;
606 m_SharpName = newSharpName;
607 m_SharpTech.reset();
609 if (m_SharpName == "cas")
611 m_SharpTech = g_Renderer.GetShaderManager().LoadEffect(CStrIntern(m_SharpName));
615 void CPostprocManager::UpdateSharpnessFactor()
617 CFG_GET_VAL("sharpness", m_Sharpness);
620 void CPostprocManager::SetDepthBufferClipPlanes(float nearPlane, float farPlane)
622 m_NearPlane = nearPlane;
623 m_FarPlane = farPlane;
626 void CPostprocManager::CreateMultisampleBuffer()
628 m_MultisampleColorTex = m_Device->CreateTexture("PostProcColorMS",
629 Renderer::Backend::ITexture::Type::TEXTURE_2D_MULTISAMPLE,
630 Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT |
631 Renderer::Backend::ITexture::Usage::TRANSFER_SRC,
632 Renderer::Backend::Format::R8G8B8A8_UNORM, m_Width, m_Height,
633 Renderer::Backend::Sampler::MakeDefaultSampler(
634 Renderer::Backend::Sampler::Filter::LINEAR,
635 Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE), 1, m_MultisampleCount);
637 // Allocate the Depth/Stencil texture.
638 m_MultisampleDepthTex = m_Device->CreateTexture("PostProcDepthMS",
639 Renderer::Backend::ITexture::Type::TEXTURE_2D_MULTISAMPLE,
640 Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT |
641 Renderer::Backend::ITexture::Usage::TRANSFER_SRC,
642 m_Device->GetPreferredDepthStencilFormat(
643 Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT |
644 Renderer::Backend::ITexture::Usage::TRANSFER_SRC,
645 true, true),
646 m_Width, m_Height,
647 Renderer::Backend::Sampler::MakeDefaultSampler(
648 Renderer::Backend::Sampler::Filter::LINEAR,
649 Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE), 1, m_MultisampleCount);
651 // Set up the framebuffers with some initial textures.
652 Renderer::Backend::SColorAttachment colorAttachment{};
653 colorAttachment.texture = m_MultisampleColorTex.get();
654 colorAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::DONT_CARE;
655 colorAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
656 colorAttachment.clearColor = CColor{0.0f, 0.0f, 0.0f, 0.0f};
658 Renderer::Backend::SDepthStencilAttachment depthStencilAttachment{};
659 depthStencilAttachment.texture = m_MultisampleDepthTex.get();
660 depthStencilAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::CLEAR;
661 depthStencilAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
663 m_MultisampleFramebuffer = m_Device->CreateFramebuffer(
664 "PostprocMultisampleFramebuffer", &colorAttachment, &depthStencilAttachment);
666 if (!m_MultisampleFramebuffer)
668 LOGERROR("Failed to create postproc multisample framebuffer");
669 m_UsingMultisampleBuffer = false;
670 DestroyMultisampleBuffer();
674 void CPostprocManager::DestroyMultisampleBuffer()
676 if (m_UsingMultisampleBuffer)
677 return;
678 m_MultisampleFramebuffer.reset();
679 m_MultisampleColorTex.reset();
680 m_MultisampleDepthTex.reset();
683 bool CPostprocManager::IsMultisampleEnabled() const
685 return m_UsingMultisampleBuffer;
688 void CPostprocManager::ResolveMultisampleFramebuffer(
689 Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
691 if (!m_UsingMultisampleBuffer)
692 return;
694 GPU_SCOPED_LABEL(deviceCommandContext, "Resolve postproc multisample");
695 deviceCommandContext->ResolveFramebuffer(
696 m_MultisampleFramebuffer.get(), m_PingFramebuffer.get());