/* ** Vulkan backend ** Copyright (c) 2016-2020 Magnus Norddahl ** ** This software is provided 'as-is', without any express or implied ** warranty. In no event will the authors be held liable for any damages ** arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, ** including commercial applications, and to alter it and redistribute it ** freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not ** claim that you wrote the original software. If you use this software ** in a product, an acknowledgment in the product documentation would be ** appreciated but is not required. ** 2. Altered source versions must be plainly marked as such, and must not be ** misrepresented as being the original software. ** 3. This notice may not be removed or altered from any source distribution. ** */ #include "vk_postprocess.h" #include "vulkan/shaders/vk_shader.h" #include "vulkan/system/vk_builders.h" #include "vulkan/system/vk_framebuffer.h" #include "vulkan/system/vk_hwbuffer.h" #include "vulkan/system/vk_swapchain.h" #include "vulkan/system/vk_commandbuffer.h" #include "vulkan/renderer/vk_renderstate.h" #include "vulkan/renderer/vk_pprenderstate.h" #include "vulkan/shaders/vk_ppshader.h" #include "vulkan/textures/vk_pptexture.h" #include "vulkan/textures/vk_renderbuffers.h" #include "vulkan/textures/vk_imagetransition.h" #include "vulkan/textures/vk_texture.h" #include "hw_cvars.h" #include "hwrenderer/postprocessing/hw_postprocess.h" #include "hwrenderer/postprocessing/hw_postprocess_cvars.h" #include "hw_vrmodes.h" #include "flatvertices.h" #include "r_videoscale.h" EXTERN_CVAR(Int, gl_dither_bpc) VkPostprocess::VkPostprocess(VulkanFrameBuffer* fb) : fb(fb) { } VkPostprocess::~VkPostprocess() { } void VkPostprocess::SetActiveRenderTarget() { auto buffers = fb->GetBuffers(); VkImageTransition() .AddImage(&buffers->PipelineImage[mCurrentPipelineImage], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, false) .AddImage(&buffers->PipelineDepthStencil, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false) .Execute(fb->GetCommands()->GetDrawCommands()); fb->GetRenderState()->SetRenderTarget(&buffers->PipelineImage[mCurrentPipelineImage], buffers->PipelineDepthStencil.View.get(), buffers->GetWidth(), buffers->GetHeight(), VK_FORMAT_R16G16B16A16_SFLOAT, VK_SAMPLE_COUNT_1_BIT); } void VkPostprocess::PostProcessScene(int fixedcm, float flash, const std::function &afterBloomDrawEndScene2D) { int sceneWidth = fb->GetBuffers()->GetSceneWidth(); int sceneHeight = fb->GetBuffers()->GetSceneHeight(); VkPPRenderState renderstate(fb); hw_postprocess.Pass1(&renderstate, fixedcm, sceneWidth, sceneHeight); SetActiveRenderTarget(); afterBloomDrawEndScene2D(); hw_postprocess.Pass2(&renderstate, fixedcm, flash, sceneWidth, sceneHeight); } void VkPostprocess::BlitSceneToPostprocess() { fb->GetRenderState()->EndRenderPass(); auto buffers = fb->GetBuffers(); auto cmdbuffer = fb->GetCommands()->GetDrawCommands(); mCurrentPipelineImage = 0; VkImageTransition() .AddImage(&buffers->SceneColor, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, false) .AddImage(&buffers->PipelineImage[mCurrentPipelineImage], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, true) .Execute(fb->GetCommands()->GetDrawCommands()); if (buffers->GetSceneSamples() != VK_SAMPLE_COUNT_1_BIT) { auto sceneColor = buffers->SceneColor.Image.get(); VkImageResolve resolve = {}; resolve.srcOffset = { 0, 0, 0 }; resolve.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; resolve.srcSubresource.mipLevel = 0; resolve.srcSubresource.baseArrayLayer = 0; resolve.srcSubresource.layerCount = 1; resolve.dstOffset = { 0, 0, 0 }; resolve.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; resolve.dstSubresource.mipLevel = 0; resolve.dstSubresource.baseArrayLayer = 0; resolve.dstSubresource.layerCount = 1; resolve.extent = { (uint32_t)sceneColor->width, (uint32_t)sceneColor->height, 1 }; cmdbuffer->resolveImage( sceneColor->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffers->PipelineImage[mCurrentPipelineImage].Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &resolve); } else { auto sceneColor = buffers->SceneColor.Image.get(); VkImageBlit blit = {}; blit.srcOffsets[0] = { 0, 0, 0 }; blit.srcOffsets[1] = { sceneColor->width, sceneColor->height, 1 }; blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; blit.srcSubresource.mipLevel = 0; blit.srcSubresource.baseArrayLayer = 0; blit.srcSubresource.layerCount = 1; blit.dstOffsets[0] = { 0, 0, 0 }; blit.dstOffsets[1] = { sceneColor->width, sceneColor->height, 1 }; blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; blit.dstSubresource.mipLevel = 0; blit.dstSubresource.baseArrayLayer = 0; blit.dstSubresource.layerCount = 1; cmdbuffer->blitImage( sceneColor->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffers->PipelineImage[mCurrentPipelineImage].Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_NEAREST); } } void VkPostprocess::ImageTransitionScene(bool undefinedSrcLayout) { auto buffers = fb->GetBuffers(); VkImageTransition() .AddImage(&buffers->SceneColor, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, undefinedSrcLayout) .AddImage(&buffers->SceneFog, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, undefinedSrcLayout) .AddImage(&buffers->SceneNormal, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, undefinedSrcLayout) .AddImage(&buffers->SceneDepthStencil, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, undefinedSrcLayout) .Execute(fb->GetCommands()->GetDrawCommands()); } void VkPostprocess::BlitCurrentToImage(VkTextureImage *dstimage, VkImageLayout finallayout) { fb->GetRenderState()->EndRenderPass(); auto srcimage = &fb->GetBuffers()->PipelineImage[mCurrentPipelineImage]; auto cmdbuffer = fb->GetCommands()->GetDrawCommands(); VkImageTransition() .AddImage(srcimage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, false) .AddImage(dstimage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, true) .Execute(cmdbuffer); VkImageBlit blit = {}; blit.srcOffsets[0] = { 0, 0, 0 }; blit.srcOffsets[1] = { srcimage->Image->width, srcimage->Image->height, 1 }; blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; blit.srcSubresource.mipLevel = 0; blit.srcSubresource.baseArrayLayer = 0; blit.srcSubresource.layerCount = 1; blit.dstOffsets[0] = { 0, 0, 0 }; blit.dstOffsets[1] = { dstimage->Image->width, dstimage->Image->height, 1 }; blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; blit.dstSubresource.mipLevel = 0; blit.dstSubresource.baseArrayLayer = 0; blit.dstSubresource.layerCount = 1; cmdbuffer->blitImage( srcimage->Image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstimage->Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_NEAREST); VkImageTransition() .AddImage(dstimage, finallayout, false) .Execute(cmdbuffer); } void VkPostprocess::DrawPresentTexture(const IntRect &box, bool applyGamma, bool screenshot) { VkPPRenderState renderstate(fb); if (!screenshot) // Already applied as we are actually copying the last frame here (GetScreenshotBuffer is called after swap) hw_postprocess.customShaders.Run(&renderstate, "screen"); PresentUniforms uniforms; if (!applyGamma) { uniforms.InvGamma = 1.0f; uniforms.Contrast = 1.0f; uniforms.Brightness = 0.0f; uniforms.Saturation = 1.0f; } else { uniforms.InvGamma = 1.0f / clamp(vid_gamma, 0.1f, 4.f); uniforms.Contrast = clamp(vid_contrast, 0.1f, 3.f); uniforms.Brightness = clamp(vid_brightness, -0.8f, 0.8f); uniforms.Saturation = clamp(vid_saturation, -15.0f, 15.f); uniforms.GrayFormula = static_cast(gl_satformula); } uniforms.ColorScale = (gl_dither_bpc == -1) ? 255.0f : (float)((1 << gl_dither_bpc) - 1); if (screenshot) { uniforms.Scale = { screen->mScreenViewport.width / (float)fb->GetBuffers()->GetWidth(), screen->mScreenViewport.height / (float)fb->GetBuffers()->GetHeight() }; uniforms.Offset = { 0.0f, 0.0f }; } else { uniforms.Scale = { screen->mScreenViewport.width / (float)fb->GetBuffers()->GetWidth(), -screen->mScreenViewport.height / (float)fb->GetBuffers()->GetHeight() }; uniforms.Offset = { 0.0f, 1.0f }; } if (applyGamma && fb->GetCommands()->swapChain->IsHdrModeActive() && !screenshot) { uniforms.HdrMode = 1; } else { uniforms.HdrMode = 0; } renderstate.Clear(); renderstate.Shader = &hw_postprocess.present.Present; renderstate.Uniforms.Set(uniforms); renderstate.Viewport = box; renderstate.SetInputCurrent(0, ViewportLinearScale() ? PPFilterMode::Linear : PPFilterMode::Nearest); renderstate.SetInputTexture(1, &hw_postprocess.present.Dither, PPFilterMode::Nearest, PPWrapMode::Repeat); if (screenshot) renderstate.SetOutputNext(); else renderstate.SetOutputSwapChain(); renderstate.SetNoBlend(); renderstate.Draw(); } void VkPostprocess::AmbientOccludeScene(float m5) { int sceneWidth = fb->GetBuffers()->GetSceneWidth(); int sceneHeight = fb->GetBuffers()->GetSceneHeight(); VkPPRenderState renderstate(fb); hw_postprocess.ssao.Render(&renderstate, m5, sceneWidth, sceneHeight); ImageTransitionScene(false); } void VkPostprocess::BlurScene(float gameinfobluramount) { int sceneWidth = fb->GetBuffers()->GetSceneWidth(); int sceneHeight = fb->GetBuffers()->GetSceneHeight(); VkPPRenderState renderstate(fb); auto vrmode = VRMode::GetVRMode(true); int eyeCount = vrmode->mEyeCount; for (int i = 0; i < eyeCount; ++i) { hw_postprocess.bloom.RenderBlur(&renderstate, sceneWidth, sceneHeight, gameinfobluramount); if (eyeCount - i > 1) NextEye(eyeCount); } } void VkPostprocess::ClearTonemapPalette() { hw_postprocess.tonemap.ClearTonemapPalette(); } void VkPostprocess::UpdateShadowMap() { if (screen->mShadowMap.PerformUpdate()) { VkPPRenderState renderstate(fb); hw_postprocess.shadowmap.Update(&renderstate); VkImageTransition() .AddImage(&fb->GetTextureManager()->Shadowmap, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, false) .Execute(fb->GetCommands()->GetDrawCommands()); screen->mShadowMap.FinishUpdate(); } } void VkPostprocess::NextEye(int eyeCount) { }