From d114575bd1d3cecaa1783c0e77ce2ee8dd8369d9 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Mon, 8 Apr 2019 00:47:55 +0200 Subject: [PATCH] - implement custom post process shaders for vulkan backend --- .../gl/shaders/gl_postprocessshader.cpp | 2 +- .../postprocessing/hw_postprocess.cpp | 201 ++++++++++++++++++ .../postprocessing/hw_postprocess.h | 40 +++- .../vulkan/renderer/vk_postprocess.cpp | 16 +- .../vulkan/renderer/vk_postprocess.h | 1 - 5 files changed, 250 insertions(+), 10 deletions(-) diff --git a/src/rendering/gl/shaders/gl_postprocessshader.cpp b/src/rendering/gl/shaders/gl_postprocessshader.cpp index d197e11cfa..611c802a43 100644 --- a/src/rendering/gl/shaders/gl_postprocessshader.cpp +++ b/src/rendering/gl/shaders/gl_postprocessshader.cpp @@ -32,7 +32,7 @@ #include "gl/shaders/gl_postprocessshaderinstance.h" #include "textures/bitmap.h" -CVAR(Bool, gl_custompost, true, 0) +EXTERN_CVAR(Bool, gl_custompost) namespace OpenGLRenderer { diff --git a/src/rendering/hwrenderer/postprocessing/hw_postprocess.cpp b/src/rendering/hwrenderer/postprocessing/hw_postprocess.cpp index d2e128849c..99e9b509a7 100644 --- a/src/rendering/hwrenderer/postprocessing/hw_postprocess.cpp +++ b/src/rendering/hwrenderer/postprocessing/hw_postprocess.cpp @@ -3,6 +3,7 @@ #include "hw_postprocess.h" #include "hwrenderer/utility/hw_cvars.h" #include "hwrenderer/postprocessing/hw_postprocess_cvars.h" +#include "hwrenderer/postprocessing/hw_postprocessshader.h" #include Postprocess hw_postprocess; @@ -805,3 +806,203 @@ void PPShadowMap::Update(PPRenderState *renderstate) renderstate->SetNoBlend(); renderstate->Draw(); } + +///////////////////////////////////////////////////////////////////////////// + +CVAR(Bool, gl_custompost, true, 0) + +void PPCustomShaders::Run(PPRenderState *renderstate, FString target) +{ + if (!gl_custompost) + return; + + CreateShaders(); + + for (auto &shader : mShaders) + { + if (shader->Desc->Target == target && shader->Desc->Enabled) + { + shader->Run(renderstate); + } + } +} + +void PPCustomShaders::CreateShaders() +{ + if (mShaders.size() == PostProcessShaders.Size()) + return; + + mShaders.clear(); + + for (unsigned int i = 0; i < PostProcessShaders.Size(); i++) + { + mShaders.push_back(std::make_unique(&PostProcessShaders[i])); + } +} + +///////////////////////////////////////////////////////////////////////////// + +PPCustomShaderInstance::PPCustomShaderInstance(PostProcessShader *desc) : Desc(desc) +{ + // Build an uniform block to be used as input + TMap::Iterator it(Desc->Uniforms); + TMap::Pair *pair; + size_t offset = 0; + while (it.NextPair(pair)) + { + FString type; + FString name = pair->Key; + + switch (pair->Value.Type) + { + case PostProcessUniformType::Float: AddUniformField(offset, name, UniformType::Float, sizeof(float)); break; + case PostProcessUniformType::Int: AddUniformField(offset, name, UniformType::Int, sizeof(int)); break; + case PostProcessUniformType::Vec2: AddUniformField(offset, name, UniformType::Vec2, sizeof(float) * 2); break; + case PostProcessUniformType::Vec3: AddUniformField(offset, name, UniformType::Vec3, sizeof(float) * 3); break; + default: break; + } + } + UniformStructSize = ((int)offset + 15) / 16 * 16; + + // Build the input textures + FString uniformTextures; + uniformTextures += "layout(binding=0) uniform sampler2D InputTexture;\n"; + + TMap::Iterator itTextures(Desc->Textures); + TMap::Pair *pairTextures; + int binding = 1; + while (itTextures.NextPair(pairTextures)) + { + uniformTextures.AppendFormat("layout(binding=%d) uniform sampler2D %s;\n", binding++, pairTextures->Key.GetChars()); + } + + // Setup pipeline + FString pipelineInOut; + pipelineInOut += "layout(location=0) in vec2 TexCoord;\n"; + pipelineInOut += "layout(location=0) out vec4 FragColor;\n"; + + FString prolog; + prolog += uniformTextures; + prolog += pipelineInOut; + + Shader = PPShader(Desc->ShaderLumpName, prolog, Fields); +} + +void PPCustomShaderInstance::Run(PPRenderState *renderstate) +{ + renderstate->Clear(); + renderstate->Shader = &Shader; + renderstate->Viewport = screen->mScreenViewport; + renderstate->SetNoBlend(); + renderstate->SetOutputNext(); + //renderstate->SetDebugName(Desc->ShaderLumpName.GetChars()); + + SetTextures(renderstate); + SetUniforms(renderstate); + + renderstate->Draw(); +} + +void PPCustomShaderInstance::SetTextures(PPRenderState *renderstate) +{ + renderstate->SetInputCurrent(0, PPFilterMode::Linear); + + int textureIndex = 1; + TMap::Iterator it(Desc->Textures); + TMap::Pair *pair; + while (it.NextPair(pair)) + { + FString name = pair->Value; + FTexture *tex = TexMan.GetTexture(TexMan.CheckForTexture(name, ETextureType::Any), true); + if (tex && tex->isValid()) + { + // Why does this completely circumvent the normal way of handling textures? + // This absolutely needs fixing because it will also circumvent any potential caching system that may get implemented. + // + // To do: fix the above problem by adding PPRenderState::SetInput(FTexture *tex) + + auto &pptex = Textures[tex]; + if (!pptex) + { + auto buffer = tex->CreateTexBuffer(0); + + std::shared_ptr data(new uint32_t[buffer.mWidth * buffer.mHeight], [](void *p) { delete[](uint32_t*)p; }); + + int count = buffer.mWidth * buffer.mHeight; + uint8_t *pixels = (uint8_t *)data.get(); + for (int i = 0; i < count; i++) + { + int pos = i << 2; + pixels[pos] = buffer.mBuffer[pos + 2]; + pixels[pos + 1] = buffer.mBuffer[pos + 1]; + pixels[pos + 2] = buffer.mBuffer[pos]; + pixels[pos + 3] = buffer.mBuffer[pos + 3]; + } + + pptex = std::make_unique(buffer.mWidth, buffer.mHeight, PixelFormat::Rgba8, data); + } + + renderstate->SetInputTexture(textureIndex, pptex.get(), PPFilterMode::Linear); + textureIndex++; + } + } +} + +void PPCustomShaderInstance::SetUniforms(PPRenderState *renderstate) +{ + TArray uniforms; + uniforms.Resize(UniformStructSize); + + TMap::Iterator it(Desc->Uniforms); + TMap::Pair *pair; + while (it.NextPair(pair)) + { + auto it2 = FieldOffset.find(pair->Key); + if (it2 != FieldOffset.end()) + { + uint8_t *dst = &uniforms[it2->second]; + float fValues[4]; + int iValues[4]; + switch (pair->Value.Type) + { + case PostProcessUniformType::Float: + fValues[0] = (float)pair->Value.Values[0]; + memcpy(dst, fValues, sizeof(float)); + break; + case PostProcessUniformType::Int: + iValues[0] = (int)pair->Value.Values[0]; + memcpy(dst, iValues, sizeof(int)); + break; + case PostProcessUniformType::Vec2: + fValues[0] = (float)pair->Value.Values[0]; + fValues[1] = (float)pair->Value.Values[1]; + memcpy(dst, fValues, sizeof(float) * 2); + break; + case PostProcessUniformType::Vec3: + fValues[0] = (float)pair->Value.Values[0]; + fValues[1] = (float)pair->Value.Values[1]; + fValues[2] = (float)pair->Value.Values[2]; + memcpy(dst, fValues, sizeof(float) * 3); + break; + default: + break; + } + } + } + + renderstate->Uniforms.Data = uniforms; +} + +void PPCustomShaderInstance::AddUniformField(size_t &offset, const FString &name, UniformType type, size_t fieldsize) +{ + size_t alignment = fieldsize; + offset = (offset + alignment - 1) / alignment * alignment; + + FieldOffset[name] = offset; + + auto name2 = std::make_unique(name); + FieldNames.push_back(std::move(name2)); + Fields.push_back({ name2->GetChars(), type, offset }); + + offset += fieldsize; +} diff --git a/src/rendering/hwrenderer/postprocessing/hw_postprocess.h b/src/rendering/hwrenderer/postprocessing/hw_postprocess.h index 9b1df90afa..aba625a7c0 100644 --- a/src/rendering/hwrenderer/postprocessing/hw_postprocess.h +++ b/src/rendering/hwrenderer/postprocessing/hw_postprocess.h @@ -2,6 +2,9 @@ #include "hwrenderer/data/shaderuniforms.h" #include +#include + +struct PostProcessShader; typedef FRenderStyle PPBlendMode; typedef IntRect PPViewport; @@ -292,7 +295,7 @@ public: class PPShader : public PPResource { public: - PPShader() { } + PPShader() = default; PPShader(const FString &fragment, const FString &defines, const std::vector &uniforms, int version = 330) : FragmentShader(fragment), Defines(defines), Uniforms(uniforms), Version(version) { } void ResetBackend() override { Backend.reset(); } @@ -777,6 +780,40 @@ private: PPShader ShadowMap = { "shaders/glsl/shadowmap.fp", "", ShadowMapUniforms::Desc() }; }; +class PPCustomShaderInstance +{ +public: + PPCustomShaderInstance(PostProcessShader *desc); + + void Run(PPRenderState *renderstate); + + PostProcessShader *Desc = nullptr; + +private: + void CreateShaders(); + void AddUniformField(size_t &offset, const FString &name, UniformType type, size_t fieldsize); + void SetTextures(PPRenderState *renderstate); + void SetUniforms(PPRenderState *renderstate); + + PPShader Shader; + int UniformStructSize = 0; + std::vector Fields; + std::vector> FieldNames; + std::map> Textures; + std::map FieldOffset; +}; + +class PPCustomShaders +{ +public: + void Run(PPRenderState *renderstate, FString target); + +private: + void CreateShaders(); + + std::vector> mShaders; +}; + ///////////////////////////////////////////////////////////////////////////// class Postprocess @@ -791,6 +828,7 @@ public: PPAmbientOcclusion ssao; PPPresent present; PPShadowMap shadowmap; + PPCustomShaders customShaders; }; extern Postprocess hw_postprocess; diff --git a/src/rendering/vulkan/renderer/vk_postprocess.cpp b/src/rendering/vulkan/renderer/vk_postprocess.cpp index 907ffcbb4c..fbdd1cb103 100644 --- a/src/rendering/vulkan/renderer/vk_postprocess.cpp +++ b/src/rendering/vulkan/renderer/vk_postprocess.cpp @@ -46,7 +46,7 @@ void VkPostprocess::PostProcessScene(int fixedcm, const std::function &a VkPPRenderState renderstate; hw_postprocess.exposure.Render(&renderstate, sceneWidth, sceneHeight); - //mCustomPostProcessShaders->Run("beforebloom"); + hw_postprocess.customShaders.Run(&renderstate, "beforebloom"); hw_postprocess.bloom.RenderBloom(&renderstate, sceneWidth, sceneHeight, fixedcm); SetActiveRenderTarget(); @@ -56,7 +56,7 @@ void VkPostprocess::PostProcessScene(int fixedcm, const std::function &a hw_postprocess.colormap.Render(&renderstate, fixedcm); hw_postprocess.lens.Render(&renderstate); hw_postprocess.fxaa.Render(&renderstate); - //mCustomPostProcessShaders->Run("scene"); + hw_postprocess.customShaders.Run(&renderstate, "scene"); } void VkPostprocess::BlitSceneToPostprocess() @@ -175,6 +175,9 @@ void VkPostprocess::DrawPresentTexture(const IntRect &box, bool applyGamma, bool { auto fb = GetVulkanFrameBuffer(); + VkPPRenderState renderstate; + hw_postprocess.customShaders.Run(&renderstate, "screen"); + PresentUniforms uniforms; if (!applyGamma /*|| framebuffer->IsHWGammaActive()*/) { @@ -200,7 +203,7 @@ void VkPostprocess::DrawPresentTexture(const IntRect &box, bool applyGamma, bool uniforms.InvGamma *= 2.2f; } - VkPPRenderState renderstate; + renderstate.Clear(); renderstate.Shader = &hw_postprocess.present.Present; renderstate.Uniforms.Set(uniforms); renderstate.Viewport = box; @@ -266,8 +269,6 @@ void VkPostprocess::UpdateShadowMap() void VkPostprocess::BeginFrame() { - mFrameDescriptorSets.clear(); - if (!mDescriptorPool) { DescriptorPoolBuilder builder; @@ -533,8 +534,9 @@ VulkanDescriptorSet *VkPPRenderState::GetInput(VkPPRenderPassSetup *passSetup, c write.updateSets(fb->device); imageTransition.execute(fb->GetDrawCommands()); - pp->mFrameDescriptorSets.push_back(std::move(descriptors)); - return pp->mFrameDescriptorSets.back().get(); + VulkanDescriptorSet *set = descriptors.get(); + fb->FrameDeleteList.Descriptors.push_back(std::move(descriptors)); + return set; } VulkanFramebuffer *VkPPRenderState::GetOutput(VkPPRenderPassSetup *passSetup, const PPOutput &output, int &framebufferWidth, int &framebufferHeight) diff --git a/src/rendering/vulkan/renderer/vk_postprocess.h b/src/rendering/vulkan/renderer/vk_postprocess.h index 50a5549f3d..ace4afdfe9 100644 --- a/src/rendering/vulkan/renderer/vk_postprocess.h +++ b/src/rendering/vulkan/renderer/vk_postprocess.h @@ -78,7 +78,6 @@ private: std::array, 16> mSamplers; std::map> mRenderPassSetup; std::unique_ptr mDescriptorPool; - std::vector> mFrameDescriptorSets; int mCurrentPipelineImage = 0; friend class VkPPRenderState;