particles: initialize primitive restart index IBO lazily

Otherwise the IBO is created from scratch for every particle that is e.g. created in a for loop. This is extremely slow and unnecessary.
The symptoms was that OneMillionParticles appeared to 'freeze' on the non-batch creation test. (Hint: it didn't freeze, it just took forever).
directional-lights
David Dormagen 2016-05-10 21:46:32 +02:00
parent 26334cb4d7
commit 29333ef87a
2 changed files with 33 additions and 26 deletions

View File

@ -1062,7 +1062,7 @@ void C4ParticleChunk::Draw(C4TargetFacet cgo, C4Object *obj, C4ShaderCall& call,
if (!has_vao)
{
glBindBuffer(GL_ARRAY_BUFFER, drawingDataVertexBufferObject);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ::Particles.GetIBO());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ::Particles.GetIBO(particleCount));
pGL->ObjectLabel(GL_VERTEX_ARRAY, vao, -1, "<particles>/VAO");
glEnableVertexAttribArray(call.GetAttribute(C4SSA_Position));
@ -1098,7 +1098,7 @@ void C4ParticleChunk::ClearBufferObjects()
void C4ParticleChunk::ReserveSpace(uint32_t forAmount)
{
uint32_t newSize = static_cast<uint32_t>(particleCount) + forAmount + 1;
::Particles.PreparePrimitiveRestartIndices(newSize);
if (particles.capacity() < newSize)
particles.reserve(std::max<uint32_t>(newSize, particles.capacity() * 2));
@ -1260,7 +1260,6 @@ C4ParticleSystem::C4ParticleSystem() : frameCounterAdvancedEvent(false)
currentSimulationTime = 0;
globalParticles = 0;
ibo = 0;
ibo_size = 0;
}
C4ParticleSystem::~C4ParticleSystem()
@ -1435,34 +1434,41 @@ void C4ParticleSystem::Create(C4ParticleDef *of_def, C4ParticleValueProvider &x,
pxList->Unlock();
}
GLuint C4ParticleSystem::GetIBO(size_t forParticleAmount)
{
PreparePrimitiveRestartIndices(forParticleAmount);
return ibo;
}
void C4ParticleSystem::PreparePrimitiveRestartIndices(uint32_t forAmount)
{
if (ibo == 0) glGenBuffers(1, &ibo);
// Each particle has 4 vertices and one PRI.
const size_t neededEntryAmount = 5 * forAmount;
const size_t neededIboSize = neededEntryAmount * sizeof(GLuint);
// Nothing to do?
if (ibo_size >= neededIboSize) return;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
// 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);
std::vector<GLuint> ibo_data;
ibo_data.reserve(neededEntryAmount);
if (ibo_size < neededAmount * sizeof(GLuint))
unsigned int index = 0;
for (unsigned int i = 0; i < neededEntryAmount; ++i)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
std::vector<GLuint> ibo_data;
ibo_data.reserve(neededAmount);
unsigned int index = 0;
for (unsigned int i = 0; i < neededAmount; ++i)
{
if ((i+1) % 5 == 0)
ibo_data.push_back(PRI);
else
ibo_data.push_back(index++);
}
ibo_size = neededAmount * sizeof(GLuint);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ibo_size, &ibo_data[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
if ((i+1) % 5 == 0)
ibo_data.push_back(PRI);
else
ibo_data.push_back(index++);
}
ibo_size = neededEntryAmount * sizeof(GLuint);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ibo_size, &ibo_data[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
#endif

View File

@ -453,6 +453,7 @@ private:
GLuint ibo;
size_t ibo_size;
std::list<C4ParticleList> particleLists;
void PreparePrimitiveRestartIndices(uint32_t forSize);
CStdCSec particleListAccessMutex;
CStdEvent frameCounterAdvancedEvent;
@ -504,9 +505,9 @@ public:
C4ParticleSystemDefinitionList definitions;
#ifndef USE_CONSOLE
// usually, the following methods are used for drawing
GLuint GetIBO() const { return ibo; }
void PreparePrimitiveRestartIndices(uint32_t forSize);
// Returns the IBO ID that contains the PRI data.
// This makes sure that the IBO contains enough indices for at least 'forParticleAmount' particles.
GLuint GetIBO(size_t forParticleAmount);
// 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);