Original Post
This code for rendering batches of 2D particles seems to be creating a large bottleneck on the GPU. I can be at <10% CPU utilisation and still only get 15fps with high particle counts. This function renders batches of particles, due to the fact that alpha blended stuff must be drawn back to front, it often will only render 50 or so particles in each batch. I was thinking of optimising out Set* calls where the state is already set, for things like vertex buffer, FVF, that would be most of the time for particle systems, since each system contains multiple particles type (meaning multiple batches) and often several systems are drawn in a row. Would that really make such a difference though? I assume d3d optimises out Set* calls that have no effect to avoid expensive kernal/user mode switches? What else can I do to speed this up, a few thousand quads isn't really that much compared to what many 3D games render? [Edited by - Fire Lancer on July 3, 2009 3:56:23 PM]
void Ps::Render(ps::System *sys, ps::ParticleType *type, const CONTAINER &particles)
{
CONTAINER::const_iterator
it = particles.begin(),
end = particles.end();
if(it==end)return;//early exit if no particles
//set device up
if(type->Image)device->SetTexture(0, ((Texture*)type->Image)->GetD3dTexture());
else device->SetTexture(0,0);
device->SetFVF(PsVertexFVF);
device->SetIndices(ib);
device->SetStreamSource(0, vb, 0, sizeof(PsVertex));
grf->SetBlendMode(type->BlendMode);
//calc transforms
if(type->WorldAligned)
device->SetTransform(D3DTS_WORLD, &matIdentity);//particle position is already relative to the world
else
{
//we need to transform all the particles relative to the sysetem
D3DXMATRIX matSystem, matPos, matRot;
D3DXMatrixRotationZ (&matRot, sys->GetDir());
D3DXMatrixTranslation(&matPos, sys->GetPos().x, sys->GetPos().y, 0);
matSystem = matRot * matPos;
device->SetTransform(D3DTS_WORLD, &matSystem);
}
unsigned count = 0;
PsVertex *vertices;
//direct3d can discard the old buffer and give us a new block of memory
//this means we can lock the buffer even if the graphics card is still
//rendering the last batch
vb->Lock(0,0, (void**)&vertices, D3DLOCK_DISCARD);
while(true)
{
unsigned c = (*it)->GetColourARGB();
float x = (*it)->GetPos().x;
float y = (*it)->GetPos().y;
float s = (*it)->GetSize();
float a = (*it)->GetImageAngle();
//make vector
D3DXVECTOR4 v1i(x-s,y-s, 0,1);
D3DXVECTOR4 v2i(x+s,y-s, 0,1);
D3DXVECTOR4 v3i(x+s,y+s, 0,1);
D3DXVECTOR4 v4i(x-s,y+s, 0,1);
D3DXVECTOR4 v1o,v2o,v3o,v4o;
//make matrix
D3DXMATRIX transform;
D3DXMatrixRotationZ(&transform, a);
//transform
D3DXVec4Transform(&v1o,&v1i,&transform);
D3DXVec4Transform(&v2o,&v2i,&transform);
D3DXVec4Transform(&v3o,&v3i,&transform);
D3DXVec4Transform(&v4o,&v4i,&transform);
//fill vb
vertices[0] = PsVertex(v1o.x,v1o.y, c, 0.0f,0.0f);
vertices[1] = PsVertex(v2o.x,v2o.y, c, 1.0f,0.0f);
vertices[2] = PsVertex(v3o.x,v3o.y, c, 1.0f,1.0f);
vertices[3] = PsVertex(v4o.x,v4o.y, c, 0.0f,1.0f);
//inc counters
vertices += 4;
count++;
it++;
//render if filled batch or done all particles
if(count == BATCH_SIZE || it==end)
{
vb->Unlock();
device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0,0, count*4, 0, count*2);
if(it==end)break;
else
{
count = 0;
vb->Lock(0,0, (void**)&vertices, D3DLOCK_DISCARD);
}
}
}
}
