From 5e1b84ed6429074cc9638bc12cee16d4ceb0cfe4 Mon Sep 17 00:00:00 2001 From: vladislavbelov Date: Wed, 10 Feb 2021 16:42:56 +0000 Subject: [PATCH] Batches decals and reduces the number of state changes. Reviewed By: wraitii Tested By: Langbart Differential Revision: https://code.wildfiregames.com/D3545 This was SVN commit r24867. --- source/renderer/DecalRData.cpp | 95 ++++++++++++++++++++++------- source/renderer/DecalRData.h | 2 +- source/renderer/PatchRData.cpp | 2 + source/renderer/TerrainRenderer.cpp | 8 --- 4 files changed, 77 insertions(+), 30 deletions(-) diff --git a/source/renderer/DecalRData.cpp b/source/renderer/DecalRData.cpp index 0cdff60352..55f52b77fc 100644 --- a/source/renderer/DecalRData.cpp +++ b/source/renderer/DecalRData.cpp @@ -24,19 +24,45 @@ #include "graphics/ShaderManager.h" #include "graphics/Terrain.h" #include "graphics/TextureManager.h" +#include "lib/allocators/DynamicArena.h" +#include "lib/allocators/STLAllocators.h" #include "ps/CLogger.h" #include "ps/Game.h" #include "ps/Profile.h" #include "renderer/Renderer.h" #include "renderer/TerrainRenderer.h" -#include "simulation2/Simulation2.h" #include "simulation2/components/ICmpWaterManager.h" +#include "simulation2/Simulation2.h" + +#include +#include // TODO: Currently each decal is a separate CDecalRData. We might want to use // lots of decals for special effects like shadows, footprints, etc, in which // case we should probably redesign this to batch them all together for more // efficient rendering. +namespace +{ + +struct SDecalBatch +{ + CDecalRData* decal; + CShaderTechniquePtr shaderTech; +}; + +struct SDecalBatchComparator +{ + bool operator()(const SDecalBatch& lhs, const SDecalBatch& rhs) const + { + if (lhs.shaderTech != rhs.shaderTech) + return lhs.shaderTech < rhs.shaderTech; + return lhs.decal < rhs.decal; + } +}; + +} // anonymous namespace + CDecalRData::CDecalRData(CModelDecal* decal, CSimulation2* simulation) : m_Decal(decal), m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW), m_Simulation(simulation) { @@ -70,21 +96,26 @@ void CDecalRData::Update(CSimulation2* simulation) } void CDecalRData::RenderDecals( - std::vector& decals, const CShaderDefines& context, ShadowMap* shadow) + const std::vector& decals, const CShaderDefines& context, ShadowMap* shadow) { + PROFILE3("render terrain decals"); + + using Arena = Allocators::DynamicArena<512 * KiB>; + + Arena arena; + + using Batches = std::vector>; + Batches batches((Batches::allocator_type(arena))); + batches.reserve(decals.size()); + CShaderDefines contextDecal = context; contextDecal.Add(str_DECAL, str_1); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - for (size_t i = 0; i < decals.size(); ++i) + for (CDecalRData *decal : decals) { - CDecalRData *decal = decals[i]; - CMaterial &material = decal->m_Decal->m_Decal.m_Material; - if (material.GetShaderEffect().length() == 0) + if (material.GetShaderEffect().empty()) { LOGERROR("Terrain renderer failed to load shader effect.\n"); continue; @@ -99,24 +130,46 @@ void CDecalRData::RenderDecals( continue; } + if (material.GetSamplers().empty()) + continue; + + SDecalBatch batch; + batch.decal = decal; + batch.shaderTech = techBase; + + batches.emplace_back(std::move(batch)); + } + + if (batches.empty()) + return; + + std::sort(batches.begin(), batches.end(), SDecalBatchComparator()); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + for (auto itTechBegin = batches.begin(), itTechEnd = batches.begin(); itTechBegin != batches.end(); itTechBegin = itTechEnd) + { + while (itTechEnd != batches.end() && itTechBegin->shaderTech == itTechEnd->shaderTech) + ++itTechEnd; + + const CShaderTechniquePtr& techBase = itTechBegin->shaderTech; const int numPasses = techBase->GetNumPasses(); + for (int pass = 0; pass < numPasses; ++pass) { techBase->BeginPass(pass); - TerrainRenderer::PrepareShader(techBase->GetShader(), shadow); - const CShaderProgramPtr& shader = techBase->GetShader(pass); + TerrainRenderer::PrepareShader(shader, shadow); - if (material.GetSamplers().size() != 0) + for (auto itDecal = itTechBegin; itDecal != itTechEnd; ++itDecal) { - const CMaterial::SamplersVector& samplers = material.GetSamplers(); - size_t samplersNum = samplers.size(); + CDecalRData* decal = itDecal->decal; + CMaterial& material = decal->m_Decal->m_Decal.m_Material; - for (size_t s = 0; s < samplersNum; ++s) - { - const CMaterial::TextureSampler& samp = samplers[s]; - shader->BindTexture(samp.Name, samp.Sampler); - } + const CMaterial::SamplersVector& samplers = material.GetSamplers(); + for (const CMaterial::TextureSampler& sampler : samplers) + shader->BindTexture(sampler.Name, sampler.Sampler); material.GetStaticUniforms().BindUniforms(shader); @@ -152,14 +205,14 @@ void CDecalRData::RenderDecals( // bump stats g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_TerrainTris += decal->m_IndexArray.GetNumVertices() / 3; - - CVertexBuffer::Unbind(); } techBase->EndPass(); } } + CVertexBuffer::Unbind(); + glDisable(GL_BLEND); } diff --git a/source/renderer/DecalRData.h b/source/renderer/DecalRData.h index be1e13c9a0..76817e865f 100644 --- a/source/renderer/DecalRData.h +++ b/source/renderer/DecalRData.h @@ -36,7 +36,7 @@ public: void Update(CSimulation2* simulation); static void RenderDecals( - std::vector& decals, const CShaderDefines& context, ShadowMap* shadow); + const std::vector& decals, const CShaderDefines& context, ShadowMap* shadow); CModelDecal* GetDecal() { return m_Decal; } diff --git a/source/renderer/PatchRData.cpp b/source/renderer/PatchRData.cpp index fbe19e4743..49a6184933 100644 --- a/source/renderer/PatchRData.cpp +++ b/source/renderer/PatchRData.cpp @@ -1147,6 +1147,8 @@ void CPatchRData::RenderOutline() void CPatchRData::RenderSides(const std::vector& patches, const CShaderProgramPtr& shader) { + PROFILE3("render terrain sides"); + glDisable(GL_CULL_FACE); CVertexBuffer* lastVB = nullptr; diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp index 071e14c2f0..0eba90d40f 100644 --- a/source/renderer/TerrainRenderer.cpp +++ b/source/renderer/TerrainRenderer.cpp @@ -291,27 +291,19 @@ void TerrainRenderer::RenderTerrainShader(const CShaderDefines& context, int cul shaderSolid->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); shaderSolid->Uniform(str_color, 0.0f, 0.0f, 0.0f, 1.0f); - PROFILE_START("render terrain sides"); CPatchRData::RenderSides(visiblePatches, shaderSolid); - PROFILE_END("render terrain sides"); techSolid->EndPass(); - PROFILE_START("render terrain base"); CPatchRData::RenderBases(visiblePatches, context, shadow); - PROFILE_END("render terrain base"); // no need to write to the depth buffer a second time glDepthMask(0); // render blend passes for each patch - PROFILE_START("render terrain blends"); CPatchRData::RenderBlends(visiblePatches, context, shadow); - PROFILE_END("render terrain blends"); - PROFILE_START("render terrain decals"); CDecalRData::RenderDecals(visibleDecals, context, shadow); - PROFILE_END("render terrain decals"); // restore OpenGL state g_Renderer.BindTexture(1, 0);