Is DrawPrimitiveUP() Slow ?

Started by
13 comments, last by MasterWorks 18 years, 7 months ago
Hi guys, My 2D game will have about 500 moving sprites displayed at any given time. I'm still trying to figure out the best way to set up a 2D environment. If I use a vertex buffer I can either Lock()/Unlock() every time a sprite moves (every frame), or to avoid that I can setup an orthogonal matrix but I still have to apply the transformations for each sprite (every frame). So I was thinking of using DrawPrimitiveUP() for my blits. Do you think that's a good idea ? If not, considering my situation of about 500 moving sprites, what's the best (fastest) method ? Thanks.
Advertisement
Applying new values to a matrix is probably much much faster than locking a vertex buffer, so go with that method.

DrawPrimitiveUP() is for when you want culling but are too lazy to deal with winding order. The fastest way would be to apply the correct winding order and culling, then use DrawPrimitive().

Edit: Actually if you had all the vertices in one big VB, locking it might be faster then editing 500 matrices.
The fast way to do this is to use a dynamic vertex buffer. Make it big, then each frame lock it with NOOVERWRITE flag and add your new sprites and render them. When you get to the end, you lock with DISCARD and start over.

The huge advantage here is you can have dynamic sprites, update them every frame, AND render them all in one DrawPrimitive call. (Unless you have many textures, then your number of draw calls = number of textures.)

So you could make a VB with room for 5000 vertices, and on the first frame you might fill and render 1-150. Then on the next frame you might render vertices 150-300, etc... and if you want even better speed, make a corresponding dynamic index buffer so you only need to send 4 vertices per sprite instead of 6.

I have to stress that if you get the locking flags correct, this method is VERY FAST for 2D sprites. If you get them wrong things can be SLOW.

If you have large numbers of sprites that won't ever change you can use a static vertex buffer in addition, but in general static VBs aren't as useful for sprites. You have to render each static vertex buffer independently which is more DrawPrimitive calls.
Quote:Original post by Scet
Edit: Actually if you had all the vertices in one big VB, locking it might be faster then editing 500 matrices.


I would say definitely. Matrices are cool but you just can't leverage them with sprites (except to transform your own vertices unrelated to D3D). I tried rendering each sprite individually and its a dead end in terms of performance. There's no way to apply different matrices "within" a DrawPrimitive call, so the dynamic vertex buffer works better. With a dynamic VB rendering your sprites, you can afford a large number of sprites/particles/etc because you are exploiting parallelism with the GPU.

It is important to have a utility or something to combine textures together. You want large sheets of sprites/animations (use texture coordinates to select individual frames) whenever possible so you can minimize the number of times you have to interrupt your rendering to switch texture.
To answer your question, DrawPrimitiveUP is slower than creating and maintaining your own vertex buffer. That's because DrawPrimitiveUP creates a new vertex buffer every time it is called.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Hi guys,

That helps a lot. But I don't understand how to render two (or more) seperate sprites with one call to DrawPrimitive().

And is this the general idea:

-start frame-

lock vertex buffer
set X and Y values to where the live sprites are
unlock vertex buffer

DrawPrimitive()

-end frame-

That's assuming I have one texture.

I really want to get the hang of this because I have quite a few mini-games I made with DirectDraw that I'd love to update.

More help would be appreciated. Thanks.
You pretty much have the right idea here:

Quote:Original post by Endemoniada
-start frame-

lock vertex buffer
set X and Y values to where the live sprites are
unlock vertex buffer

DrawPrimitive()

-end frame-


You can add as many sprites as you like, in between the lock and unlock calls.

The way I do it, is keep a vector of "drawdata" which contains the data I need to set up the vertices. When I call my "draw" function, all it does is just add a new "drawdata" to my vector.

When I call "endscene", I do all the real work: lock the VB, and set up all the vertices at once, unlock the buffer, and use one drawprimitive call to render them.

(That's kind of simplified, since it assumes I'm only using one texture, but I hope you get the idea)
Quote:Original post by Endemoniada
-start frame-

lock vertex buffer
set X and Y values to where the live sprites are
unlock vertex buffer

DrawPrimitive()

-end frame-

That's assuming I have one texture.


You'll probably have to have one DrawPrimitive() per texture, but that should be it. Sort your sprites before rendering so that all the ones that use the same texture are grouped together and can be rendered with just the one DrawPrimitive() call for that texture.

Quote:Original post by JohnBolton
To answer your question, DrawPrimitiveUP is slower than creating and maintaining your own vertex buffer. That's because DrawPrimitiveUP creates a new vertex buffer every time it is called.

It doesn't create a buffer each frame. It uses a dynamic vertex buffer that's locked and filled every time it's called.

Hi guys,

I think I understand everything now. Here is how I set it up:

// initialize vertex buffer (called once)

UINT nVertices=MAX_SPRITES*4;
DWORD dwSize=nVertices*sizeof(CUSTOMVERTEX);

CreateVertexBuffer(dwSize, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_VB, NULL);

// initialize index buffer (called once)

UINT iSize=MAX_SPRITES*6*2;

CreateIndexBuffer(iSize, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &g_pIB, NULL);

// set all the index values here


// my render loop (called every frame)

Lock(0,0,(void**)&v,D3DLOCK_DISCARD); // vertex buffer

// set the vertices for the live sprites here

UnLock() // vertex buffer

BeginScene()

SetTexture(...)
DrawIndexedPrimitive(...);

SetTexture();
DrawIndexedPrimitive(...);

EndScene(...);

Am I using the flags correctly ? I made the vertex buffer dynamic and the index buffer static, both are write only. About the pools, I can't use managed for the vertex buffer so I just used default, what is recommended ? I can use the D3DLOCK_NOOVERWRITE method but it seems pretty complicated, will it be much faster that way ?

I made it so that I only have to switch textures once (so I have two textures in total).

I didn't set up a performance counter yet, I just want to make sure I have the right idea here.

Thanks again everyone.

This topic is closed to new replies.

Advertisement