1
0
forked from 0ad/0ad

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.
This commit is contained in:
Vladislav Belov 2021-02-10 16:42:56 +00:00
parent 8ea4f3186c
commit 5e1b84ed64
4 changed files with 77 additions and 30 deletions

View File

@ -24,19 +24,45 @@
#include "graphics/ShaderManager.h" #include "graphics/ShaderManager.h"
#include "graphics/Terrain.h" #include "graphics/Terrain.h"
#include "graphics/TextureManager.h" #include "graphics/TextureManager.h"
#include "lib/allocators/DynamicArena.h"
#include "lib/allocators/STLAllocators.h"
#include "ps/CLogger.h" #include "ps/CLogger.h"
#include "ps/Game.h" #include "ps/Game.h"
#include "ps/Profile.h" #include "ps/Profile.h"
#include "renderer/Renderer.h" #include "renderer/Renderer.h"
#include "renderer/TerrainRenderer.h" #include "renderer/TerrainRenderer.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpWaterManager.h" #include "simulation2/components/ICmpWaterManager.h"
#include "simulation2/Simulation2.h"
#include <algorithm>
#include <vector>
// TODO: Currently each decal is a separate CDecalRData. We might want to use // 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 // 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 // case we should probably redesign this to batch them all together for more
// efficient rendering. // 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) CDecalRData::CDecalRData(CModelDecal* decal, CSimulation2* simulation)
: m_Decal(decal), m_IndexArray(GL_STATIC_DRAW), m_Array(GL_STATIC_DRAW), m_Simulation(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( void CDecalRData::RenderDecals(
std::vector<CDecalRData*>& decals, const CShaderDefines& context, ShadowMap* shadow) const std::vector<CDecalRData*>& decals, const CShaderDefines& context, ShadowMap* shadow)
{ {
PROFILE3("render terrain decals");
using Arena = Allocators::DynamicArena<512 * KiB>;
Arena arena;
using Batches = std::vector<SDecalBatch, ProxyAllocator<SDecalBatch, Arena>>;
Batches batches((Batches::allocator_type(arena)));
batches.reserve(decals.size());
CShaderDefines contextDecal = context; CShaderDefines contextDecal = context;
contextDecal.Add(str_DECAL, str_1); contextDecal.Add(str_DECAL, str_1);
glEnable(GL_BLEND); for (CDecalRData *decal : decals)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for (size_t i = 0; i < decals.size(); ++i)
{ {
CDecalRData *decal = decals[i];
CMaterial &material = decal->m_Decal->m_Decal.m_Material; 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"); LOGERROR("Terrain renderer failed to load shader effect.\n");
continue; continue;
@ -99,24 +130,46 @@ void CDecalRData::RenderDecals(
continue; 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(); const int numPasses = techBase->GetNumPasses();
for (int pass = 0; pass < numPasses; ++pass) for (int pass = 0; pass < numPasses; ++pass)
{ {
techBase->BeginPass(pass); techBase->BeginPass(pass);
TerrainRenderer::PrepareShader(techBase->GetShader(), shadow);
const CShaderProgramPtr& shader = techBase->GetShader(pass); 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(); CDecalRData* decal = itDecal->decal;
size_t samplersNum = samplers.size(); CMaterial& material = decal->m_Decal->m_Decal.m_Material;
for (size_t s = 0; s < samplersNum; ++s) const CMaterial::SamplersVector& samplers = material.GetSamplers();
{ for (const CMaterial::TextureSampler& sampler : samplers)
const CMaterial::TextureSampler& samp = samplers[s]; shader->BindTexture(sampler.Name, sampler.Sampler);
shader->BindTexture(samp.Name, samp.Sampler);
}
material.GetStaticUniforms().BindUniforms(shader); material.GetStaticUniforms().BindUniforms(shader);
@ -152,14 +205,14 @@ void CDecalRData::RenderDecals(
// bump stats // bump stats
g_Renderer.m_Stats.m_DrawCalls++; g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris += decal->m_IndexArray.GetNumVertices() / 3; g_Renderer.m_Stats.m_TerrainTris += decal->m_IndexArray.GetNumVertices() / 3;
CVertexBuffer::Unbind();
} }
techBase->EndPass(); techBase->EndPass();
} }
} }
CVertexBuffer::Unbind();
glDisable(GL_BLEND); glDisable(GL_BLEND);
} }

View File

@ -36,7 +36,7 @@ public:
void Update(CSimulation2* simulation); void Update(CSimulation2* simulation);
static void RenderDecals( static void RenderDecals(
std::vector<CDecalRData*>& decals, const CShaderDefines& context, ShadowMap* shadow); const std::vector<CDecalRData*>& decals, const CShaderDefines& context, ShadowMap* shadow);
CModelDecal* GetDecal() { return m_Decal; } CModelDecal* GetDecal() { return m_Decal; }

View File

@ -1147,6 +1147,8 @@ void CPatchRData::RenderOutline()
void CPatchRData::RenderSides(const std::vector<CPatchRData*>& patches, const CShaderProgramPtr& shader) void CPatchRData::RenderSides(const std::vector<CPatchRData*>& patches, const CShaderProgramPtr& shader)
{ {
PROFILE3("render terrain sides");
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
CVertexBuffer* lastVB = nullptr; CVertexBuffer* lastVB = nullptr;

View File

@ -291,27 +291,19 @@ void TerrainRenderer::RenderTerrainShader(const CShaderDefines& context, int cul
shaderSolid->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); shaderSolid->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
shaderSolid->Uniform(str_color, 0.0f, 0.0f, 0.0f, 1.0f); shaderSolid->Uniform(str_color, 0.0f, 0.0f, 0.0f, 1.0f);
PROFILE_START("render terrain sides");
CPatchRData::RenderSides(visiblePatches, shaderSolid); CPatchRData::RenderSides(visiblePatches, shaderSolid);
PROFILE_END("render terrain sides");
techSolid->EndPass(); techSolid->EndPass();
PROFILE_START("render terrain base");
CPatchRData::RenderBases(visiblePatches, context, shadow); CPatchRData::RenderBases(visiblePatches, context, shadow);
PROFILE_END("render terrain base");
// no need to write to the depth buffer a second time // no need to write to the depth buffer a second time
glDepthMask(0); glDepthMask(0);
// render blend passes for each patch // render blend passes for each patch
PROFILE_START("render terrain blends");
CPatchRData::RenderBlends(visiblePatches, context, shadow); CPatchRData::RenderBlends(visiblePatches, context, shadow);
PROFILE_END("render terrain blends");
PROFILE_START("render terrain decals");
CDecalRData::RenderDecals(visibleDecals, context, shadow); CDecalRData::RenderDecals(visibleDecals, context, shadow);
PROFILE_END("render terrain decals");
// restore OpenGL state // restore OpenGL state
g_Renderer.BindTexture(1, 0); g_Renderer.BindTexture(1, 0);