   Direct3D - annoying speed problem
  Hi,I've been having some serious problems regarding speed in Direct3D (9). I'm trying to draw 2D textured quads, but I can't seem to draw any more than about 100 quads without getting a noticeable speed hit.Could anyone help me solve this?This is the main loop I've been using to test it:while (running) {device->Clear(0,0,D3DCLEAR_TARGET,0xFFFFFFFF,0,0);for (i=0;i<1000;i++) {DXW_Draw(image,(float)mouse_x,(float)mouse_y);}device->EndScene();device->Present(0,0,0,0);}Ok, it might seem a bit pointless to be blitting 1000 sprites but I've seen programs that can do this at 60-75fps with relative ease (and if I remember, SDL and Allegro can both handle this many sprites without too much slowdown). At a guess I'd say my benchmark runs at between 20 and 30 fps on release, 15-20 on debug.Here's some info about how it's running:Device:  Dev Type: D3DDEVTYPE_HAL  Vertex Processing: D3DCREATE_SOFTWARE_VERTEXPROCESSING  Swap Effect: D3DSWAPEFFECT_DISCARD  Presentation: D3DPRESENT_RATE_DEFAULT, D3DPRESENT_INTERVAL_IMMEDIATE  Format: D3DFMT_A8R8G8B8  Vertex shader: NULL  Vertex format: x,y,z,rhw,col,u,v  Lighting: Off  Alpha blend: OnVertex buffer:  Usage: Write only  Pool : ManagedImage:  Format: Always the same as the device  Size: 128 x 128  Pool: Managed  Usage: 0And here's the blit function, if it helps at all:void DXW_Draw(DXW_IMAGE * src, float dst_x, float dst_y) {// LocalsDXW_VERTEX * l_vertices;int width = src->w;  int height = src->h ;int half_w = width / 2;int half_h =  height / 2;DXW_IMAGE * dst = back_buffer;// Start drawingDXW_BeginScene();vertices->Lock(0, 0, (void**)&l_vertices, NULL);// Position verticesl_vertices[0].x = dst_x - half_w;l_vertices[0].y = dst_y - half_h;l_vertices[0].col = 0xFFFFFFFF;l_vertices[1].x = dst_x - half_w + width - 0.5f;l_vertices[1].y = dst_y - half_h;l_vertices[1].col = 0xFFFFFFFF;l_vertices[2].x = dst_x - half_w + width - 0.5f;l_vertices[2].y = dst_y - half_h + height - 0.5f;l_vertices[2].col = 0xFFFFFFFF;l_vertices[3].x = dst_x - half_w;l_vertices[3].y = dst_y - half_h + height - 0.5f;l_vertices[3].col = 0xFFFFFFFF;// Do the drawingvertices->Unlock();device->SetTexture(0, src->texture);device->DrawPrimitive(D3DPT_TRIANGLEFAN,0, 2);}I've tried all sorts of combinations of video modes and pools and they all seem to give the same sort of speed, so I'm at a loss.I found some sources of info about 'batching', but wanted to check on here before trying it. The reason for this being that this graph seems to indicate that I should be getting 6.7 million tris per second - 44,500+ quads at 75fps, even without batching. It says the tests were done on a Geforce 6800, and I only have a Geforce 4 TI but the difference can't be that huge, surely.I suppose my question is: is batching the answer to my problem or am I doing something else horribly wrong? Any help would be great,Thanks, Kris   
  What immediately hits me is that you call device->SetTexture() for each sprite (i.e. 1000 times per frame!).SetTexture() is one of the slowest calls you can make, so what you need to do is to look over your design and batch sprites by texture. You will also want to store as much image data in as few textures as possible (i.e. if you have lots of 32x32 sprites you keep them in a 1024x1024 texture), which will dramatically decrease the number of necessary SetTexture() calls.   ;  There's a number of things that you are doing wrong here. First of all, only call BeginScene and EndScene once per frame, and not around every draw call that you make.So:BeginSceneDraw StuffEndScenePresentSecondly, if you are always just drawing quads, reuse the vertex buffer and reposition it using the world matrix. There's no need to be constantly locking/unlocking it all the time. Alternatively, fill up one large vertex buffer with all of your quads and make a single DrawPrimitive call with it instead. Your video card likes dealing in large batches and not 2 triangles at a time.Thirdly, if the texture isn't changing, ie you are drawing 2 identically textured quads, then there's no need to be constantly setting the texture. Try to draw all of your sprites with one texture, then the next, and so on.Fourthly, if you are just doing a 2-d game, you might want to take a look at ID3DXSPRITE, as it'll handle all of this stuff for you, and might be a better choice.   ;  Sorry, I made a mistake when I was posting; I stripped down the blitting code a bit too much. The BeginScene/EndScene code is wrapped by a function that only calls it if it's needed. I see what you're saying about SetTexture and the vertex buffer though. I'll try the method using a single DrawPrimitive call and see what happens.I had a look at ID3DXSPRITE but I really don't like the idea of needing an extra DLL to run it, so I want to avoid it unless it's really necessary. (also I want to get as much experience as I can with raw polygons, in case I decide to go 3D)I've never really touched the world matrix - any particular functions I should take a look at?Edit: Another thing - whilst indexing the polygons to use with a single DrawPrimitive call, is it possible to use multiple textures? I know I could use seperate areas of a single texture but that'd give me even more of a restriction on image sizes.Thanks again   ;  For efficient drawing you really need to batch.The best thing would be to have all your textures as part of one big texture (texture atlas). Then lock the vertex buffer just once, fill it with data for all the sprites, then draw it using DrawIndexedPrimitive (with a suitable index buffer).(In terms of performance hits, locking/unlocking would be your bottleneck here.)   ;  As has been suggested - batching and state changing are going to be your biggest problems here.Ideally you shouldn't be changing any resources as part of the core loop - your Lock() calls will slaughter the parallelism between the GPU and CPU. You're not using any discard flags, and I dont know if you're using dynamic/writeonly flags when creating. Because you re-lock and re-render the same VB you'll be requiring the GPU pipeline to stay in step with the CPU - and both will have to wait for any AGP/driver overhead before either can continue.I would hazard a guess that the majority of your frame-time would be spent in overhead (D3D runtimes, drivers, AGP transfers etc..) rather than either the CPU or GPU doing any useful work.Quote:The reason for this being that this graph seems to indicate that I should be getting 6.7 million tris per second - 44,500+ quads at 75fps, even without batchingThat graph is probably not the best comparison. It renders each frame in a single batch/draw. For a grid of N*M vertices it'll still only use a single draw call, whereas yours would require a (N-1)*(M-1) or similar number of draw calls. More than 500 draw calls per frame with D3D9 starts to introduce lots of OS overhead (kernel/user mode switches) - that would limit you to around a 20x20 grid of tiles the way you've implemented it.Quote:I had a look at ID3DXSPRITE but I really don't like the idea of needing an extra DLL to run it, so I want to avoid it unless it's really necessary.I wouldn't worry about this too much. At least for your initial learning/experimentation. Deploying an application with the D3DX DLL's is really not that hard - you can even point people to the DX Web Installer if you dont want to do the hard work yourself...As soon as you want to investigate shaders (they can be very powerful for 2D) then you're likely to want the effects framework and HLSL - both of which are substantially easier via the D3DX library.Bottom line - dont write "2D in 3D" in the same way you'd write a traditional 2D application. It might work, but you'll see these sorts of performance problems [wink]hthJack   ;  At the GDC 2006, there was a word that has been pronounced so many times that all game developpers sould have it in mind: BATCH - BATCH - BATCH :)With this in mind, all missuses pointed above will magically disappear( many SetTextures, many Lock/Unlock, and so on)Also, dont forget that above 1000 draw calls per frame, a P-IV CPU would be overwhelmed.BTW, you should at least have created you 'vertice' with a DYNAMIC flag. Also, you should keep in mind that locking with a D3DLOCK_NOOVERWRITE is faster since the driver will not discard the buffer and just let you reuse it. If you're interested in knowing how to achieve that (assuming you dont know, of course)m just PM me.Basically you would create a large vertex buffer which you encapsulate into a class, monitor every Lock operation, and keep tranx of an offset in the large vertex buffer.Regards,Janta   ;  Thanks for your help, everyone. That's given me tons of stuff to work with and read up on.One problem though, I can't seem to find any MSDN page about Direct3D on Google. Have they taken their pages out of the index or something? [looksaround]PS: May as well ask here while I'm at it. How can I get around the texture size limitations if I'm using one large texture with DrawPrimitive\DrawIndexPrimitive?   ;Quote:Original post by Kris_APS: May as well ask here while I'm at it. How can I get around the texture size limitations if I'm using one large texture with DrawPrimitive\DrawIndexPrimitive?I'd say you dont. You just fit as many 'small' textures as you can on one larger texture (you have to investigate on that word: larger, because I'm not sure if the biggest is the better, there might be compromises too... experts opinion wanted !) and draw all tiles that use that texture.And repeat that as many times as needed. You'll end up with far less texture changes, but texture changes are needed anyway :) Unless you can fit everything a single texture.I'm not sure about what would happen if you use a texture bigger than what you hardware support. I think it would resize it, and you would loose a lot of quality.Did that (at least partially) answer your question ?Regards,Janta   ;Quote:Original post by Kris_AOne problem though, I can't seem to find any MSDN page about Direct3D on Google. Have they taken their pages out of the index or something? [looksaround]Nope, they're still there... the trick is to get the right search term/phrase correct. In fact, finding anything on MSDN can be a bit of an artform in its own special way [lol]appending site:msdn.microsoft.com generally gets better results.Quote:Original post by Kris_APS: May as well ask here while I'm at it. How can I get around the texture size limitations if I'm using one large texture with DrawPrimitive\DrawIndexPrimitive?You don't [grin]If the device says it only handles a 2048x2048 texture then thats all you can use. If you need more than that then you'll need to break apart batches based on which texture is being used...hthJack