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/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 <algorithm>
#include <vector>
// 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<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;
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);
}

View File

@ -36,7 +36,7 @@ public:
void Update(CSimulation2* simulation);
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; }

View File

@ -1147,6 +1147,8 @@ void CPatchRData::RenderOutline()
void CPatchRData::RenderSides(const std::vector<CPatchRData*>& patches, const CShaderProgramPtr& shader)
{
PROFILE3("render terrain sides");
glDisable(GL_CULL_FACE);
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_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);