Use an IBO for the particle system

glDrawElements needs an IBO when using a core profile. The particle
system's IBO is actually quite static since it's always a triangle
strip with 2 triangles followed by a PRI. Therefore, this reduces the
amount of data we have to send to the GPU compared to the previous
solution.

Also, remove the workaround when glPrimitiveRestartIndex is not
available since it is always available with OpenGL 3.1 and when using
a core profile we are guaranteed to have OpenGL 3.1 anyway.
objectmenu
Armin Burgmeier 2016-01-15 18:19:43 -08:00
parent 686a32764c
commit e13cda4bd0
3 changed files with 36 additions and 110 deletions

View File

@ -185,9 +185,6 @@ bool C4Application::DoInit(int argc, char * argv[])
pWindow->SetSize(Config.Graphics.WindowX, Config.Graphics.WindowY);
}
// after initializing graphics, the particle system can check for compatibility
::Particles.DoInit();
// Initialize gamepad
if (!pGamePadControl && Config.General.GamepadEnabled)
pGamePadControl = new C4GamePadControl();

View File

@ -967,16 +967,6 @@ bool C4ParticleChunk::Exec(C4Object *obj, float timeDelta)
return particleCount > 0;
}
#if defined(__APPLE__)
#undef glGenVertexArrays
#undef glBindVertexArray
#undef glDeleteVertexArrays
#define glGenVertexArrays glGenVertexArraysAPPLE
#define glBindVertexArray glBindVertexArrayAPPLE
#define glDeleteVertexArrays glDeleteVertexArraysAPPLE
#endif
void C4ParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall& call, int texUnit, const StdProjectionMatrix& modelview)
{
if (particleCount == 0) return;
@ -1026,11 +1016,11 @@ void C4ParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall& call,
assert (drawingDataVertexArraysObject != 0 && "Could not generate a VAO ID.");
}
glBindBuffer(GL_ARRAY_BUFFER, drawingDataVertexBufferObject);
// Push the new vertex data
// this has to be done before binding the vertex arrays object
glBindBuffer(GL_ARRAY_BUFFER, drawingDataVertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(C4Particle::DrawingData::Vertex) * particleCount, &vertexCoordinates[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// set up the vertex array structure
GLuint vao;
@ -1042,6 +1032,8 @@ void C4ParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall& call,
if (!has_vao)
{
glBindBuffer(GL_ARRAY_BUFFER, drawingDataVertexBufferObject);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ::Particles.GetIBO());
#ifdef GL_KHR_debug
if (glObjectLabel)
glObjectLabel(GL_VERTEX_ARRAY, vao, -1, "<particles>/VAO");
@ -1055,18 +1047,10 @@ void C4ParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall& call,
glVertexAttribPointer(call.GetAttribute(C4SSA_Color), 4, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<GLvoid*>(offsetof(C4Particle::DrawingData::Vertex, r)));
}
if (!Particles.usePrimitiveRestartIndexWorkaround)
{
glDrawElements(GL_TRIANGLE_STRIP, static_cast<GLsizei> (5 * particleCount), GL_UNSIGNED_INT, ::Particles.GetPrimitiveRestartArray());
}
else
{
glMultiDrawElements(GL_TRIANGLE_STRIP, ::Particles.GetMultiDrawElementsCountArray(), GL_UNSIGNED_INT, const_cast<const GLvoid**>(::Particles.GetMultiDrawElementsIndexArray()), static_cast<GLsizei> (particleCount));
}
glDrawElements(GL_TRIANGLE_STRIP, static_cast<GLsizei> (5 * particleCount), GL_UNSIGNED_INT, 0);
// reset buffer data
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
bool C4ParticleChunk::IsOfType(C4ParticleDef *def, uint32_t _blitMode, uint32_t _attachment) const
@ -1144,11 +1128,8 @@ void C4ParticleList::Draw(C4TargetFacet cgo, C4Object *obj)
pDraw->DeactivateBlitModulation();
pDraw->ResetBlitMode();
if (!Particles.usePrimitiveRestartIndexWorkaround)
{
glPrimitiveRestartIndex(0xffffffff);
glEnable(GL_PRIMITIVE_RESTART);
}
glPrimitiveRestartIndex(0xffffffff);
glEnable(GL_PRIMITIVE_RESTART);
// enable shader
C4ShaderCall call(pGL->GetSpriteShader(true, false, false));
@ -1182,10 +1163,7 @@ void C4ParticleList::Draw(C4TargetFacet cgo, C4Object *obj)
accessMutex.Leave();
if (!Particles.usePrimitiveRestartIndexWorkaround)
{
glDisable(GL_PRIMITIVE_RESTART);
}
glDisable(GL_PRIMITIVE_RESTART);
}
void C4ParticleList::Clear()
@ -1255,7 +1233,8 @@ C4ParticleSystem::C4ParticleSystem() : frameCounterAdvancedEvent(false)
{
currentSimulationTime = 0;
globalParticles = 0;
usePrimitiveRestartIndexWorkaround = false;
ibo = 0;
ibo_size = 0;
}
C4ParticleSystem::~C4ParticleSystem()
@ -1264,21 +1243,6 @@ C4ParticleSystem::~C4ParticleSystem()
calculationThread.SignalStop();
CalculateNextStep();
for (std::vector<uint32_t *>::iterator iter = multiDrawElementsIndexArray.begin(); iter != multiDrawElementsIndexArray.end(); ++iter)
delete[] (*iter);
}
void C4ParticleSystem::DoInit()
{
// we use features that are only supported from 3.1 upwards. Check whether the graphics card supports that and - if not - use workarounds
if (!GLEW_VERSION_3_1 || (glPrimitiveRestartIndex == 0))
{
usePrimitiveRestartIndexWorkaround = true;
LogSilent("WARNING (particle system): Your graphics card does not support glPrimitiveRestartIndex - a (slower) fallback will be used!");
}
assert (glGenBuffers != 0 && "Your graphics card does not seem to support buffer objects.");
}
void C4ParticleSystem::ExecuteCalculation()
@ -1304,8 +1268,6 @@ void C4ParticleSystem::ExecuteCalculation()
particleListAccessMutex.Leave();
}
}
#else // ifdef USE_CONSOLE
void C4ParticleSystem::DoInit() {}
#endif
C4ParticleList *C4ParticleSystem::GetNewParticleList(C4Object *forObject)
@ -1451,59 +1413,31 @@ void C4ParticleSystem::Create(C4ParticleDef *of_def, C4ParticleValueProvider &x,
void C4ParticleSystem::PreparePrimitiveRestartIndices(uint32_t forAmount)
{
if (!usePrimitiveRestartIndexWorkaround)
// prepare array with indices, separated by special primitive restart index
const uint32_t PRI = 0xffffffff;
size_t neededAmount = 5 * forAmount;
if (ibo == 0) glGenBuffers(1, &ibo);
if (ibo_size < neededAmount * sizeof(GLuint))
{
// prepare array with indices, separated by special primitive restart index
const uint32_t PRI = 0xffffffff;
size_t neededAmount = 5 * forAmount;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
if (primitiveRestartIndices.size() < neededAmount)
std::vector<GLuint> ibo_data;
ibo_data.reserve(neededAmount);
unsigned int index = 0;
for (unsigned int i = 0; i < neededAmount; ++i)
{
uint32_t oldValue = 0;
if (primitiveRestartIndices.size() > 2)
{
oldValue = primitiveRestartIndices[primitiveRestartIndices.size()-1];
if (oldValue == PRI)
oldValue = primitiveRestartIndices[primitiveRestartIndices.size()-2];
++oldValue;
}
size_t oldSize = primitiveRestartIndices.size();
primitiveRestartIndices.resize(neededAmount);
for (size_t i = oldSize; i < neededAmount; ++i)
{
if (((i+1) % 5 == 0) && (i != 0))
{
primitiveRestartIndices[i] = PRI;
}
else
{
primitiveRestartIndices[i] = oldValue++;
}
}
}
}
else
{
// prepare arrays for glMultiDrawElements
if (multiDrawElementsCountArray.size() <= forAmount)
{
multiDrawElementsCountArray.resize(forAmount, 4);
if ((i+1) % 5 == 0)
ibo_data.push_back(PRI);
else
ibo_data.push_back(index++);
}
if (multiDrawElementsIndexArray.size() <= forAmount)
{
uint32_t oldSize = multiDrawElementsIndexArray.size();
multiDrawElementsIndexArray.resize(forAmount);
for (; oldSize < forAmount; ++oldSize)
{
multiDrawElementsIndexArray[oldSize] = new uint32_t[4];
for (uint32_t i = 0; i < 4; ++i)
multiDrawElementsIndexArray[oldSize][i] = 4 * oldSize + i;
}
}
ibo_size = neededAmount * sizeof(GLuint);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ibo_size, &ibo_data[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
#endif
@ -1511,6 +1445,9 @@ void C4ParticleSystem::PreparePrimitiveRestartIndices(uint32_t forAmount)
void C4ParticleSystem::Clear()
{
#ifndef USE_CONSOLE
if (ibo != 0) glDeleteBuffers(1, &ibo);
ibo = 0; ibo_size = 0;
currentSimulationTime = 0;
ClearAllParticles();
#endif

View File

@ -445,10 +445,8 @@ class C4ParticleSystem
private:
// contains an array with indices for vertices, separated by a primitive restart index
std::vector<uint32_t> primitiveRestartIndices;
// these are fallbacks for if primitiveRestartIndex is not supported by the graphics card
std::vector<GLsizei> multiDrawElementsCountArray;
std::vector<uint32_t *> multiDrawElementsIndexArray;
GLuint ibo;
size_t ibo_size;
std::list<C4ParticleList> particleLists;
CStdCSec particleListAccessMutex;
@ -475,7 +473,6 @@ public:
frameCounterAdvancedEvent.Set();
#endif
}
void DoInit();
// resets the internal state of the particle system and unloads all definitions
void Clear();
void DrawGlobalParticles(C4TargetFacet cgo)
@ -502,14 +499,9 @@ public:
C4ParticleSystemDefinitionList definitions;
#ifndef USE_CONSOLE
// on some graphics card, glPrimitiveRestartIndex might not be supported
bool usePrimitiveRestartIndexWorkaround;
GLsizei *GetMultiDrawElementsCountArray() { return &multiDrawElementsCountArray[0]; }
GLvoid **GetMultiDrawElementsIndexArray() { return reinterpret_cast<GLvoid**> (&multiDrawElementsIndexArray[0]); }
// usually, the following methods are used for drawing
GLuint GetIBO() const { return ibo; }
void PreparePrimitiveRestartIndices(uint32_t forSize);
void *GetPrimitiveRestartArray() { return (void*)&primitiveRestartIndices[0]; }
// creates a new particle
void Create(C4ParticleDef *of_def, C4ParticleValueProvider &x, C4ParticleValueProvider &y, C4ParticleValueProvider &speedX, C4ParticleValueProvider &speedY, C4ParticleValueProvider &lifetime, C4PropList *properties, int amount = 1, C4Object *object=NULL);