Index: ps/trunk/source/renderer/DecalRData.h =================================================================== --- ps/trunk/source/renderer/DecalRData.h +++ ps/trunk/source/renderer/DecalRData.h @@ -36,7 +36,7 @@ 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; } Index: ps/trunk/source/renderer/DecalRData.cpp =================================================================== --- ps/trunk/source/renderer/DecalRData.cpp +++ ps/trunk/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::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 @@ 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 @@ // 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); } Index: ps/trunk/source/renderer/PatchRData.cpp =================================================================== --- ps/trunk/source/renderer/PatchRData.cpp +++ ps/trunk/source/renderer/PatchRData.cpp @@ -1147,6 +1147,8 @@ void CPatchRData::RenderSides(const std::vector& patches, const CShaderProgramPtr& shader) { + PROFILE3("render terrain sides"); + glDisable(GL_CULL_FACE); CVertexBuffer* lastVB = nullptr; Index: ps/trunk/source/renderer/TerrainRenderer.cpp =================================================================== --- ps/trunk/source/renderer/TerrainRenderer.cpp +++ ps/trunk/source/renderer/TerrainRenderer.cpp @@ -291,27 +291,19 @@ 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);