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"
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"
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>
43 void DrawFullscreenQuad(
44 Renderer::Backend::IVertexInputLayout
* vertexInputLayout
,
45 Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
)
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
;
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),
91 CPostprocManager::~CPostprocManager()
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
;
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
113 m_CaptureFramebuffer
.reset();
115 m_PingFramebuffer
.reset();
116 m_PongFramebuffer
.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()
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};
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();
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.
180 void CPostprocManager::RecreateBuffers()
184 #define GEN_BUFFER_RGBA(name, w, h) \
185 name = m_Device->CreateTexture2D( \
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
,
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
);
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
)
485 GPU_SCOPED_LABEL(deviceCommandContext
, "Render postproc");
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
);
498 for (int pass
= 0; pass
< m_AATech
->GetNumPasses(); ++pass
)
499 ApplyEffect(deviceCommandContext
, m_AATech
, pass
);
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/");
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());
533 void CPostprocManager::SetPostEffect(const CStrW
& name
)
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
)
553 CFG_GET_VAL("antialiasing", newAAName
);
554 if (m_AAName
== newAAName
)
556 m_AAName
= newAAName
;
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
)
579 if (!m_Device
->GetCapabilities().multisampling
|| m_AllowedSampleCounts
.empty())
581 LOGWARNING("MSAA is unsupported.");
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
)
603 CFG_GET_VAL("sharpening", newSharpName
);
604 if (m_SharpName
== newSharpName
)
606 m_SharpName
= newSharpName
;
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
,
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
)
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
)
694 GPU_SCOPED_LABEL(deviceCommandContext
, "Resolve postproc multisample");
695 deviceCommandContext
->ResolveFramebuffer(
696 m_MultisampleFramebuffer
.get(), m_PingFramebuffer
.get());