Index: binaries/data/mods/public/shaders/glsl/model_common.vs
===================================================================
--- binaries/data/mods/public/shaders/glsl/model_common.vs
+++ binaries/data/mods/public/shaders/glsl/model_common.vs
@@ -12,7 +12,6 @@
uniform vec3 sunDir;
uniform vec3 sunColor;
#endif
-uniform mat4 instancingTransform;
#if USE_WIND
uniform vec4 sim_time;
@@ -56,6 +55,9 @@
attribute vec4 a_skinWeights;
#endif
+#if USE_INSTANCING
+attribute mat4 a_instancing;
+#endif
vec4 fakeCos(vec4 x)
{
@@ -77,16 +79,16 @@
n += vec3(m * vec4(a_normal, 0.0)) * a_skinWeights[i];
}
}
- vec4 position = instancingTransform * vec4(p, 1.0);
- mat3 normalMatrix = mat3(instancingTransform[0].xyz, instancingTransform[1].xyz, instancingTransform[2].xyz);
+ vec4 position = a_instancing * vec4(p, 1.0);
+ mat3 normalMatrix = mat3(a_instancing[0].xyz, a_instancing[1].xyz, a_instancing[2].xyz);
vec3 normal = normalMatrix * normalize(n);
#if (USE_NORMAL_MAP || USE_PARALLAX)
vec3 tangent = normalMatrix * a_tangent.xyz;
#endif
#else
#if (USE_INSTANCING)
- vec4 position = instancingTransform * vec4(a_vertex, 1.0);
- mat3 normalMatrix = mat3(instancingTransform[0].xyz, instancingTransform[1].xyz, instancingTransform[2].xyz);
+ vec4 position = a_instancing * vec4(a_vertex, 1.0);
+ mat3 normalMatrix = mat3(a_instancing[0].xyz, a_instancing[1].xyz, a_instancing[2].xyz);
vec3 normal = normalMatrix * a_normal;
#if (USE_NORMAL_MAP || USE_PARALLAX)
vec3 tangent = normalMatrix * a_tangent.xyz;
@@ -102,7 +104,7 @@
vec2 wind = windData.xy;
// fractional part of model position, clamped to >.4
- vec4 modelPos = instancingTransform[3];
+ vec4 modelPos = a_instancing[3];
modelPos = fract(modelPos);
modelPos = clamp(modelPos, 0.4, 1.0);
@@ -113,7 +115,7 @@
// these determine the speed of the wind's "cosine" waves.
cosVec.w = 0.0;
cosVec.x = sim_time.x * modelPos[0] + position.x;
- cosVec.y = sim_time.x * modelPos[2] / 3.0 + instancingTransform[3][0];
+ cosVec.y = sim_time.x * modelPos[2] / 3.0 + a_instancing[3][0];
cosVec.z = sim_time.x * abswind / 4.0 + position.z;
// calculate "cosines" in parallel, using a smoothed triangle wave
Index: binaries/data/mods/public/shaders/glsl/model_common.xml
===================================================================
--- binaries/data/mods/public/shaders/glsl/model_common.xml
+++ binaries/data/mods/public/shaders/glsl/model_common.xml
@@ -5,14 +5,18 @@
-
+
-
+
-
+
+
+
+
+
Index: binaries/data/mods/public/shaders/glsl/model_solid.xml
===================================================================
--- binaries/data/mods/public/shaders/glsl/model_solid.xml
+++ binaries/data/mods/public/shaders/glsl/model_solid.xml
@@ -6,6 +6,10 @@
+
+
+
+
Index: binaries/data/mods/public/shaders/glsl/model_solid_player.xml
===================================================================
--- binaries/data/mods/public/shaders/glsl/model_solid_player.xml
+++ binaries/data/mods/public/shaders/glsl/model_solid_player.xml
@@ -6,6 +6,10 @@
+
+
+
+
Index: binaries/data/mods/public/shaders/glsl/model_solid_tex.xml
===================================================================
--- binaries/data/mods/public/shaders/glsl/model_solid_tex.xml
+++ binaries/data/mods/public/shaders/glsl/model_solid_tex.xml
@@ -8,6 +8,10 @@
+
+
+
+
Index: binaries/data/mods/public/shaders/glsl/model_water.vs
===================================================================
--- binaries/data/mods/public/shaders/glsl/model_water.vs
+++ binaries/data/mods/public/shaders/glsl/model_water.vs
@@ -7,19 +7,17 @@
uniform vec3 cameraPos;
uniform vec3 sunDir;
uniform vec3 sunColor;
-uniform mat4 instancingTransform;
uniform float sim_time;
uniform vec2 translation;
attribute vec3 a_vertex;
attribute vec3 a_normal;
-#if USE_INSTANCING
- attribute vec4 a_tangent;
-#endif
attribute vec2 a_uv0;
attribute vec2 a_uv1;
+attribute mat4 a_instancing;
+
varying vec4 worldPos;
varying vec4 v_tex;
@@ -31,7 +29,7 @@
void main()
{
- worldPos = instancingTransform * vec4(a_vertex, 1.0);
+ worldPos = a_instancing * vec4(a_vertex, 1.0);
v_tex.xy = (a_uv0 + worldPos.xz) / 5.0 + sim_time * translation;
Index: binaries/data/mods/public/shaders/glsl/model_water.xml
===================================================================
--- binaries/data/mods/public/shaders/glsl/model_water.xml
+++ binaries/data/mods/public/shaders/glsl/model_water.xml
@@ -5,12 +5,15 @@
-
+
-
-
+
+
+
+
+
Index: binaries/data/mods/public/shaders/glsl/model_waterfall.vs
===================================================================
--- binaries/data/mods/public/shaders/glsl/model_waterfall.vs
+++ binaries/data/mods/public/shaders/glsl/model_waterfall.vs
@@ -7,7 +7,6 @@
uniform vec3 cameraPos;
uniform vec3 sunDir;
uniform vec3 sunColor;
-uniform mat4 instancingTransform;
uniform float sim_time;
uniform vec2 translation;
@@ -17,6 +16,8 @@
attribute vec2 a_uv0;
attribute vec2 a_uv1;
+attribute mat4 a_instancing;
+
varying vec4 worldPos;
varying vec4 v_tex;
varying vec3 v_half;
@@ -26,7 +27,7 @@
void main()
{
- worldPos = instancingTransform * vec4(a_vertex, 1.0);
+ worldPos = a_instancing * vec4(a_vertex, 1.0);
v_tex.xy = a_uv0 + sim_time * translation;
v_transp = a_uv1.x;
@@ -39,7 +40,7 @@
vec3 sunVec = -sunDir;
v_half = normalize(sunVec + normalize(eyeVec));
- mat3 normalMatrix = mat3(instancingTransform[0].xyz, instancingTransform[1].xyz, instancingTransform[2].xyz);
+ mat3 normalMatrix = mat3(a_instancing[0].xyz, a_instancing[1].xyz, a_instancing[2].xyz);
v_normal = normalMatrix * a_normal;
v_lighting = max(0.0, dot(v_normal, -sunDir)) * sunColor;
Index: binaries/data/mods/public/shaders/glsl/model_waterfall.xml
===================================================================
--- binaries/data/mods/public/shaders/glsl/model_waterfall.xml
+++ binaries/data/mods/public/shaders/glsl/model_waterfall.xml
@@ -5,12 +5,15 @@
-
+
-
-
+
+
+
+
+
Index: source/graphics/Model.h
===================================================================
--- source/graphics/Model.h
+++ source/graphics/Model.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020 Wildfire Games.
+/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -112,6 +112,8 @@
// get the currently playing animation, if any
CSkeletonAnim* GetAnimation() const { return m_Anim; }
+ float GetAnimTime() const { return m_AnimTime; }
+
// set the animation state to be the same as from another; both models should
// be compatible types (same type of skeleton)
void CopyAnimationFrom(CModel* source);
Index: source/graphics/ShaderProgram.h
===================================================================
--- source/graphics/ShaderProgram.h
+++ source/graphics/ShaderProgram.h
@@ -189,7 +189,7 @@
virtual void TexCoordPointer(GLenum texture, GLint size, GLenum type, GLsizei stride, const void* pointer);
virtual void VertexAttribPointer(attrib_id_t id, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
virtual void VertexAttribIPointer(attrib_id_t id, GLint size, GLenum type, GLsizei stride, const void* pointer);
-
+ virtual void VertexAttribDivisor(attrib_id_t id, GLuint divisor);
/**
* Checks that all the required vertex attributes have been set.
* Call this before calling glDrawArrays/glDrawElements etc to avoid potential crashes.
Index: source/graphics/ShaderProgram.cpp
===================================================================
--- source/graphics/ShaderProgram.cpp
+++ source/graphics/ShaderProgram.cpp
@@ -676,6 +676,16 @@
}
}
+ virtual void VertexAttribDivisor(attrib_id_t id, GLuint divisor)
+ {
+ std::map::iterator it = m_VertexAttribs.find(id);
+ if (it != m_VertexAttribs.end())
+ {
+ pglVertexAttribDivisorARB(it->second, divisor);
+ }
+ }
+
+
virtual std::vector GetFileDependencies() const
{
return m_FileDependencies;
@@ -832,6 +842,11 @@
debug_warn("Shader type doesn't support VertexAttribIPointer");
}
+void CShaderProgram::VertexAttribDivisor(attrib_id_t UNUSED(id), GLuint UNUSED(divisor))
+{
+ debug_warn("Shader type doesn't support VertexAttribDivisor");
+}
+
#if CONFIG2_GLES
// These should all be overridden by CShaderProgramGLSL
Index: source/lib/external_libraries/glext_funcs.h
===================================================================
--- source/lib/external_libraries/glext_funcs.h
+++ source/lib/external_libraries/glext_funcs.h
@@ -364,6 +364,10 @@
FUNC2(void, glBindFragDataLocationEXT, glBindFragDataLocation, "3.0", (GLuint program, GLuint colorNumber, const char *name))
FUNC2(GLint, glGetFragDataLocationEXT, glGetFragDataLocation, "3.0", (GLuint program, const char *name))
+// GL_ARB_draw_instanced / GL 3.3
+FUNC2(void, glDrawElementsInstancedARB, glDrawElementsInstanced, "3.3", (GLenum mode, GLsizei count, GLenum type, const GLvoid * indices, GLsizei primcount))
+FUNC2(void, glVertexAttribDivisorARB, glVertexAttribDivisor, "3.3", (GLuint index, GLuint divisor))
+
// GL_ARB_occlusion_query / GL1.5:
FUNC2(void, glGenQueriesARB, glGenQueries, "1.5", (GLsizei n, GLuint *ids))
FUNC2(void, glDeleteQueriesARB, glDeleteQueries, "1.5", (GLsizei n, const GLuint *ids))
Index: source/ps/CStrInternStatic.h
===================================================================
--- source/ps/CStrInternStatic.h
+++ source/ps/CStrInternStatic.h
@@ -67,6 +67,10 @@
X(WATERTYPE_LAKE)
X2(_emptystring, "")
X(a_apexPosition)
+X(a_instancing)
+X(a_instancing1)
+X(a_instancing2)
+X(a_instancing3)
X(a_otherPosition)
X(a_retreatPosition)
X(a_skinJoints)
Index: source/ps/GameSetup/GameSetup.cpp
===================================================================
--- source/ps/GameSetup/GameSetup.cpp
+++ source/ps/GameSetup/GameSetup.cpp
@@ -337,6 +337,7 @@
g_Renderer.EndFrame();
PROFILE2_ATTR("draw calls: %d", (int)g_Renderer.GetStats().m_DrawCalls);
+ PROFILE2_ATTR("saved draw calls: %d", (int)g_Renderer.GetStats().m_SavedDrawCalls);
PROFILE2_ATTR("terrain tris: %d", (int)g_Renderer.GetStats().m_TerrainTris);
PROFILE2_ATTR("water tris: %d", (int)g_Renderer.GetStats().m_WaterTris);
PROFILE2_ATTR("model tris: %d", (int)g_Renderer.GetStats().m_ModelTris);
Index: source/renderer/HWLightingModelRenderer.h
===================================================================
--- source/renderer/HWLightingModelRenderer.h
+++ source/renderer/HWLightingModelRenderer.h
@@ -40,10 +40,13 @@
CModelRData* CreateModelData(const void* key, CModel* model);
void UpdateModelData(CModel* model, CModelRData* data, int updateflags);
- void BeginPass(int streamflags);
+ bool CanInstance() const { return false; }
+
+ void BeginPass(const CShaderProgramPtr& shader, int streamflags);
void EndPass(int streamflags);
void PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def);
- void RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model, CModelRData* data);
+ void RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model);
+ void RenderModels(const CShaderProgramPtr& shader, int streamflags, const std::vector& models);
protected:
struct ShaderModelRendererInternals;
Index: source/renderer/HWLightingModelRenderer.cpp
===================================================================
--- source/renderer/HWLightingModelRenderer.cpp
+++ source/renderer/HWLightingModelRenderer.cpp
@@ -174,7 +174,7 @@
// Setup one rendering pass
-void ShaderModelVertexRenderer::BeginPass(int streamflags)
+void ShaderModelVertexRenderer::BeginPass(const CShaderProgramPtr& UNUSED(shader), int streamflags)
{
ENSURE(streamflags == (streamflags & (STREAM_POS | STREAM_UV0 | STREAM_UV1 | STREAM_NORMAL)));
}
@@ -205,10 +205,10 @@
// Render one model
-void ShaderModelVertexRenderer::RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model, CModelRData* data)
+void ShaderModelVertexRenderer::RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model)
{
const CModelDefPtr& mdldef = model->GetModelDef();
- ShaderModel* shadermodel = static_cast(data);
+ ShaderModel* shadermodel = static_cast(model->GetRenderData());
u8* base = shadermodel->m_Array.Bind();
GLsizei stride = (GLsizei)shadermodel->m_Array.GetStride();
@@ -242,3 +242,8 @@
g_Renderer.m_Stats.m_ModelTris += numFaces;
}
+void ShaderModelVertexRenderer::RenderModels(const CShaderProgramPtr& shader, int streamflags, const std::vector& models)
+{
+ for (CModel* model : models)
+ RenderModel(shader, streamflags, model);
+}
Index: source/renderer/InstancingModelRenderer.h
===================================================================
--- source/renderer/InstancingModelRenderer.h
+++ source/renderer/InstancingModelRenderer.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2012 Wildfire Games.
+/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -35,17 +35,19 @@
class InstancingModelRenderer : public ModelVertexRenderer
{
public:
- InstancingModelRenderer(bool gpuSkinning, bool calculateTangents);
+ InstancingModelRenderer(bool gpuSkinning, bool calculateTangents, bool realInstancing);
~InstancingModelRenderer();
// Implementations
CModelRData* CreateModelData(const void* key, CModel* model);
void UpdateModelData(CModel* model, CModelRData* data, int updateflags);
- void BeginPass(int streamflags);
+ void BeginPass(const CShaderProgramPtr& shader, int streamflags);
void EndPass(int streamflags);
void PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def);
- void RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model, CModelRData* data);
+ bool CanInstance() const;
+ void RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model);
+ void RenderModels(const CShaderProgramPtr& shader, int streamflags, const std::vector& models);
protected:
InstancingModelRendererInternals* m;
Index: source/renderer/InstancingModelRenderer.cpp
===================================================================
--- source/renderer/InstancingModelRenderer.cpp
+++ source/renderer/InstancingModelRenderer.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020 Wildfire Games.
+/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -239,32 +239,47 @@
struct InstancingModelRendererInternals
{
bool gpuSkinning;
-
bool calculateTangents;
+ bool realInstancing;
/// Previously prepared modeldef
IModelDef* imodeldef;
/// Index base for imodeldef
u8* imodeldefIndexBase;
-};
+ // Used in fake instancing.
+ CShaderProgram::Binding bindingInstancingTransform;
+ // Used in real instancing.
+ GLuint buff;
+ std::vector transforms;
+};
// Construction and Destruction
-InstancingModelRenderer::InstancingModelRenderer(bool gpuSkinning, bool calculateTangents)
+InstancingModelRenderer::InstancingModelRenderer(bool gpuSkinning, bool calculateTangents, bool realInstancing)
{
m = new InstancingModelRendererInternals;
m->gpuSkinning = gpuSkinning;
m->calculateTangents = calculateTangents;
+ m->realInstancing = realInstancing;
m->imodeldef = 0;
+ m->transforms.reserve(64);
+
+ pglGenBuffersARB(1, &m->buff);
}
InstancingModelRenderer::~InstancingModelRenderer()
{
+ pglDeleteBuffersARB(1, &m->buff);
delete m;
}
+bool InstancingModelRenderer::CanInstance() const
+{
+ return m->realInstancing;
+}
+
// Build modeldef data if necessary - we have no per-CModel data
CModelRData* InstancingModelRenderer::CreateModelData(const void* key, CModel* model)
{
@@ -291,11 +306,18 @@
// We have no per-CModel data
}
-
// Setup one rendering pass.
-void InstancingModelRenderer::BeginPass(int streamflags)
+void InstancingModelRenderer::BeginPass(const CShaderProgramPtr& shader, int streamflags)
{
ENSURE(streamflags == (streamflags & (STREAM_POS|STREAM_NORMAL|STREAM_UV0|STREAM_UV1)));
+
+ if (m->realInstancing)
+ {
+ shader->VertexAttribDivisor(str_a_instancing, 1);
+ shader->VertexAttribDivisor(str_a_instancing1, 1);
+ shader->VertexAttribDivisor(str_a_instancing2, 1);
+ shader->VertexAttribDivisor(str_a_instancing3, 1);
+ }
}
// Cleanup rendering pass.
@@ -343,14 +365,25 @@
shader->VertexAttribPointer(str_a_skinWeights, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, base + m->imodeldef->m_BlendWeights.offset);
}
+ if (!m->realInstancing)
+ m->bindingInstancingTransform = shader->GetUniformBinding(str_instancingTransform);
+
shader->AssertPointersBound();
}
// Render one model
-void InstancingModelRenderer::RenderModel(const CShaderProgramPtr& shader, int UNUSED(streamflags), CModel* model, CModelRData* UNUSED(data))
+void InstancingModelRenderer::RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model)
{
- const CModelDefPtr& mdldef = model->GetModelDef();
+ RenderModels(shader, streamflags, std::vector{ model });
+}
+
+void InstancingModelRenderer::RenderModels(const CShaderProgramPtr& shader, int UNUSED(streamflags), const std::vector& models)
+{
+ if (g_Renderer.m_SkipSubmit)
+ return;
+
+ const CModelDefPtr& mdldef = models.front()->GetModelDef();
if (m->gpuSkinning)
{
@@ -359,26 +392,42 @@
// HACK: NVIDIA drivers return uniform name with "[0]", Intel Windows drivers without;
// try uploading both names since one of them should work, and this is easier than
// canonicalising the uniform names in CShaderProgramGLSL
- shader->Uniform(str_skinBlendMatrices_0, mdldef->GetNumBones() + 1, model->GetAnimatedBoneMatrices());
- shader->Uniform(str_skinBlendMatrices, mdldef->GetNumBones() + 1, model->GetAnimatedBoneMatrices());
+ shader->Uniform(str_skinBlendMatrices_0, mdldef->GetNumBones() + 1, models.front()->GetAnimatedBoneMatrices());
+ shader->Uniform(str_skinBlendMatrices, mdldef->GetNumBones() + 1, models.front()->GetAnimatedBoneMatrices());
+ }
+
+ if (m->realInstancing)
+ {
+ // Set up a uniform
+ m->transforms.clear();
+ for (CModel* model : models)
+ m->transforms.emplace_back(model->GetTransform());
+
+ pglBindBufferARB(GL_ARRAY_BUFFER, m->buff);
+ // The OpenGL doc states that buffer orphaninh should ideally be done with the same buffer size,
+ // so this uses a minimum size.
+ const size_t arbitrary_instance_threshold = 50;
+ pglBufferDataARB(GL_ARRAY_BUFFER, std::max(64 * arbitrary_instance_threshold, 64 * models.size()), nullptr, GL_STREAM_DRAW);
+ pglBufferSubDataARB(GL_ARRAY_BUFFER, 0, 64 * models.size(), m->transforms.data());
+
+ shader->VertexAttribPointer(str_a_instancing, 4, GL_FLOAT, GL_FALSE, 64, (void*)(0));
+ shader->VertexAttribPointer(str_a_instancing1, 4, GL_FLOAT, GL_FALSE, 64, (void*)(0+16));
+ shader->VertexAttribPointer(str_a_instancing2, 4, GL_FLOAT, GL_FALSE, 64, (void*)(0+32));
+ shader->VertexAttribPointer(str_a_instancing3, 4, GL_FLOAT, GL_FALSE, 64, (void*)(0+48));
+ }
+ else
+ {
+ ENSURE(models.size() == 1);
+ shader->Uniform(m->bindingInstancingTransform, models.front()->GetTransform());
}
- // render the lot
size_t numFaces = mdldef->GetNumFaces();
+ if (models.size() > 1)
+ pglDrawElementsInstancedARB(GL_TRIANGLES, (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldefIndexBase, models.size());
+ else
+ pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)mdldef->GetNumVertices()-1, (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldefIndexBase);
- if (!g_Renderer.m_SkipSubmit)
- {
- // Draw with DrawRangeElements where available, since it might be more efficient
-#if CONFIG2_GLES
- glDrawElements(GL_TRIANGLES, (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldefIndexBase);
-#else
- pglDrawRangeElementsEXT(GL_TRIANGLES, 0, (GLuint)m->imodeldef->m_Array.GetNumVertices()-1,
- (GLsizei)numFaces*3, GL_UNSIGNED_SHORT, m->imodeldefIndexBase);
-#endif
- }
-
- // bump stats
g_Renderer.m_Stats.m_DrawCalls++;
- g_Renderer.m_Stats.m_ModelTris += numFaces;
-
+ g_Renderer.m_Stats.m_SavedDrawCalls += models.size() - 1;
+ g_Renderer.m_Stats.m_ModelTris += numFaces * models.size();
}
Index: source/renderer/ModelRenderer.cpp
===================================================================
--- source/renderer/ModelRenderer.cpp
+++ source/renderer/ModelRenderer.cpp
@@ -277,6 +277,11 @@
if (b->GetMaterial().GetDiffuseTexture() < a->GetMaterial().GetDiffuseTexture())
return false;
+ if (a->GetPlayerID() < b->GetPlayerID())
+ return true;
+ if (b->GetPlayerID() < a->GetPlayerID())
+ return false;
+
return a->GetMaterial().GetStaticUniforms() < b->GetMaterial().GetStaticUniforms();
}
};
@@ -605,6 +610,10 @@
std::vector texBindingNames((BindingNamesListAllocator(arena)));
texBindingNames.reserve(64);
+ // TODO C++20: use span for ModelRenderers and use an Arena allocator here.
+ std::vector keptModels;
+ keptModels.reserve(64);
+
while (idxTechStart < techBuckets.size())
{
CShaderTechniquePtr currentTech = techBuckets[idxTechStart].tech;
@@ -627,7 +636,7 @@
modifier->BeginPass(shader);
- m->vertexRenderer->BeginPass(streamflags);
+ m->vertexRenderer->BeginPass(shader, streamflags);
// When the shader technique changes, textures need to be
// rebound, so ensure there are no remnants from the last pass.
@@ -636,8 +645,31 @@
texBindings.clear();
texBindingNames.clear();
- CModelDef* currentModeldef = NULL;
+ CModelDef* currentModeldef = nullptr;
CShaderUniforms currentStaticUniforms;
+ bool rq_water = false;
+ bool rq_skycube = false;
+ bool rq_time = false;
+
+ CSkeletonAnim* currentAnim = nullptr;
+ float currentAnimTime = 0.f;
+
+ CColor shadingcolor;
+ player_id_t playerid = INVALID_PLAYER;
+
+ // This must be called before changing state.
+ auto RenderKeptModels = [this, &shader, &keptModels, &modifier, &streamflags]()
+ {
+ if (keptModels.empty())
+ return;
+#if 0
+ printf("Rendered %i %s, anim %p\n", keptModels.size(), keptModels.front()->GetModelDef()->GetName().string8().c_str(), (void*)keptModels.front()->GetAnimation());
+#endif
+
+ modifier->PrepareModel(shader, keptModels.front());
+ m->vertexRenderer->RenderModels(shader, streamflags, keptModels);
+ keptModels.clear();
+ };
for (size_t idx = idxTechStart; idx < idxTechEnd; ++idx)
{
@@ -684,6 +716,8 @@
CTexture* newTex = samp.Sampler.get();
if (texBindings[s].Active() && newTex != currentTexs[s])
{
+ RenderKeptModels();
+
shader->BindTexture(texBindings[s], newTex->GetHandle());
currentTexs[s] = newTex;
}
@@ -693,6 +727,8 @@
CModelDef* newModeldef = model->GetModelDef().get();
if (newModeldef != currentModeldef)
{
+ RenderKeptModels();
+
currentModeldef = newModeldef;
m->vertexRenderer->PrepareModelDef(shader, streamflags, *currentModeldef);
}
@@ -701,49 +737,80 @@
CShaderUniforms newStaticUniforms = model->GetMaterial().GetStaticUniforms();
if (newStaticUniforms != currentStaticUniforms)
{
+ RenderKeptModels();
+
currentStaticUniforms = newStaticUniforms;
currentStaticUniforms.BindUniforms(shader);
}
const CShaderRenderQueries& renderQueries = model->GetMaterial().GetRenderQueries();
+ // So long as the shader technique isn't reset,
+ // the render query uniforms & texture bindings will remain valid.
+ // It also doesn't matter if they're bound too early.
for (size_t q = 0; q < renderQueries.GetSize(); ++q)
{
CShaderRenderQueries::RenderQuery rq = renderQueries.GetItem(q);
- if (rq.first == RQUERY_TIME)
+ // Time is an uniform - once set, we're good until the shader program is changed
+ // and it does not matter much if we bind too early either.
+ if (rq.first == RQUERY_TIME && !rq_time)
{
CShaderProgram::Binding binding = shader->GetUniformBinding(rq.second);
if (binding.Active())
{
+ rq_time = true;
double time = g_Renderer.GetTimeManager().GetGlobalTime();
shader->Uniform(binding, time, 0.0f, 0.0f, 0.0f);
}
+
}
- else if (rq.first == RQUERY_WATER_TEX)
+ else if (rq.first == RQUERY_WATER_TEX && !rq_water)
{
+ rq_water = true;
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
double time = WaterMgr->m_WaterTexTimer;
double period = 1.6;
int curTex = static_cast(time * 60.0 / period) % 60;
-
if (WaterMgr->m_RenderWater && WaterMgr->WillRenderFancyWater())
shader->BindTexture(str_waterTex, WaterMgr->m_NormalMap[curTex]);
else
shader->BindTexture(str_waterTex, g_Renderer.GetTextureManager().GetErrorTexture());
}
- else if (rq.first == RQUERY_SKY_CUBE)
+ else if (rq.first == RQUERY_SKY_CUBE && !rq_skycube)
{
+ rq_skycube = true;
shader->BindTexture(str_skyCube, g_Renderer.GetSkyManager()->GetSkyCube());
}
}
- modifier->PrepareModel(shader, model);
+ if (model->GetPlayerID() != playerid || model->GetShadingColor() != shadingcolor)
+ {
+ RenderKeptModels();
+ playerid = model->GetPlayerID();
+ shadingcolor = model->GetShadingColor();
+ modifier->PrepareModel(shader, model);
+ }
+
+ /**
+ * During instanced rendering, we need to make sure all models
+ * are on the same frame of animation (if relevant).
+ */
+ if (m->vertexRenderer->CanInstance() &&
+ (model->GetAnimation() != currentAnim || (model->GetAnimation() && model->GetAnimTime() != currentAnimTime)))
+ {
+ RenderKeptModels();
+ currentAnim = model->GetAnimation();
+ currentAnimTime = model->GetAnimTime();
+ }
CModelRData* rdata = static_cast(model->GetRenderData());
ENSURE(rdata->GetKey() == m->vertexRenderer.get());
-
- m->vertexRenderer->RenderModel(shader, streamflags, model, rdata);
- }
+ if (m->vertexRenderer->CanInstance())
+ keptModels.emplace_back(model);
+ else
+ m->vertexRenderer->RenderModel(shader, streamflags, model);
+ } // numModels loop
+ RenderKeptModels();
}
m->vertexRenderer->EndPass(streamflags);
Index: source/renderer/ModelVertexRenderer.h
===================================================================
--- source/renderer/ModelVertexRenderer.h
+++ source/renderer/ModelVertexRenderer.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2012 Wildfire Games.
+/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -101,7 +101,7 @@
*
* @param streamflags Vertex streams required by the fragment stage.
*/
- virtual void BeginPass(int streamflags) = 0;
+ virtual void BeginPass(const CShaderProgramPtr& shader, int streamflags) = 0;
/**
@@ -133,6 +133,7 @@
*/
virtual void PrepareModelDef(const CShaderProgramPtr& shader, int streamflags, const CModelDef& def) = 0;
+ virtual bool CanInstance() const = 0;
/**
* RenderModel: Invoke the rendering commands for the given model.
@@ -153,7 +154,8 @@
* that use the same CModelDef object and the same texture must
* succeed.
*/
- virtual void RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model, CModelRData* data) = 0;
+ virtual void RenderModel(const CShaderProgramPtr& shader, int streamflags, CModel* model) = 0;
+ virtual void RenderModels(const CShaderProgramPtr& shader, int streamflags, const std::vector& models) = 0;
};
Index: source/renderer/RenderModifiers.h
===================================================================
--- source/renderer/RenderModifiers.h
+++ source/renderer/RenderModifiers.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2012 Wildfire Games.
+/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -126,7 +126,6 @@
void PrepareModel(const CShaderProgramPtr& shader, CModel* model);
private:
- CShaderProgram::Binding m_BindingInstancingTransform;
CShaderProgram::Binding m_BindingShadingColor;
CShaderProgram::Binding m_BindingPlayerColor;
};
Index: source/renderer/RenderModifiers.cpp
===================================================================
--- source/renderer/RenderModifiers.cpp
+++ source/renderer/RenderModifiers.cpp
@@ -97,16 +97,12 @@
shader->Uniform(str_losTransform, los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
}
- m_BindingInstancingTransform = shader->GetUniformBinding(str_instancingTransform);
m_BindingShadingColor = shader->GetUniformBinding(str_shadingColor);
m_BindingPlayerColor = shader->GetUniformBinding(str_playerColor);
}
void ShaderRenderModifier::PrepareModel(const CShaderProgramPtr& shader, CModel* model)
{
- if (m_BindingInstancingTransform.Active())
- shader->Uniform(m_BindingInstancingTransform, model->GetTransform());
-
if (m_BindingShadingColor.Active())
shader->Uniform(m_BindingShadingColor, model->GetShadingColor());
Index: source/renderer/Renderer.h
===================================================================
--- source/renderer/Renderer.h
+++ source/renderer/Renderer.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020 Wildfire Games.
+/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -90,6 +90,8 @@
void Reset() { memset(this, 0, sizeof(*this)); }
// number of draw calls per frame - total DrawElements + Begin/End immediate mode loops
size_t m_DrawCalls;
+ // Number of saved draw calls via instancing each frame.
+ size_t m_SavedDrawCalls;
// number of terrain triangles drawn
size_t m_TerrainTris;
// number of water triangles drawn
@@ -112,6 +114,7 @@
bool m_FragmentShader;
bool m_Shadows;
bool m_PrettyWater;
+ bool m_Instancing;
};
public:
Index: source/renderer/Renderer.cpp
===================================================================
--- source/renderer/Renderer.cpp
+++ source/renderer/Renderer.cpp
@@ -114,6 +114,7 @@
enum {
Row_DrawCalls = 0,
+ Row_SavedDrawCalls,
Row_TerrainTris,
Row_WaterTris,
Row_ModelTris,
@@ -171,6 +172,12 @@
sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_DrawCalls);
return buf;
+ case Row_SavedDrawCalls:
+ if (col == 0)
+ return "# saved draw calls";
+ sprintf_s(buf, sizeof(buf), "%lu", (unsigned long)Stats.m_SavedDrawCalls);
+ return buf;
+
case Row_TerrainTris:
if (col == 0)
return "# terrain tris";
@@ -473,6 +480,7 @@
m_Caps.m_FragmentShader = false;
m_Caps.m_Shadows = false;
m_Caps.m_PrettyWater = false;
+ m_Caps.m_Instancing = false;
// now start querying extensions
if (!g_RenderingOptions.GetNoVBO() && ogl_HaveExtension("GL_ARB_vertex_buffer_object"))
@@ -493,6 +501,9 @@
m_Caps.m_FragmentShader = true;
}
+ if (0 == ogl_HaveExtensions(0, "GL_ARB_draw_instanced", "GL_ARB_instanced_arrays", NULL))
+ m_Caps.m_Instancing = true;
+
#if CONFIG2_GLES
m_Caps.m_Shadows = true;
#else
@@ -552,11 +563,11 @@
ENSURE(g_RenderingOptions.GetRenderPath() != RenderPath::FIXED);
m->Model.VertexRendererShader = ModelVertexRendererPtr(new ShaderModelVertexRenderer());
- m->Model.VertexInstancingShader = ModelVertexRendererPtr(new InstancingModelRenderer(false, g_RenderingOptions.GetPreferGLSL()));
+ m->Model.VertexInstancingShader = ModelVertexRendererPtr(new InstancingModelRenderer(false, g_RenderingOptions.GetPreferGLSL(), g_RenderingOptions.GetPreferGLSL()));
if (g_RenderingOptions.GetGPUSkinning()) // TODO: should check caps and GLSL etc too
{
- m->Model.VertexGPUSkinningShader = ModelVertexRendererPtr(new InstancingModelRenderer(true, g_RenderingOptions.GetPreferGLSL()));
+ m->Model.VertexGPUSkinningShader = ModelVertexRendererPtr(new InstancingModelRenderer(true, g_RenderingOptions.GetPreferGLSL(), g_RenderingOptions.GetPreferGLSL()));
m->Model.NormalSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexGPUSkinningShader));
m->Model.TranspSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexGPUSkinningShader));
}