# Batching Draw Calls, thousands of cubes.

This topic is 2693 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hey guys so, I'm slightly confused in XNA. I've searched Google relentlessly for the past few days and I just cannot find what I need.

I'm programming a Voxel engine and right now I have 50000 cubes on screen and I'm getting 4-5 fps. Even with only 10000-15000 I only get around 18fps.

I'm using
GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, cubes[chunkx, chunkz][a, b, c].Vertices,
0, cubes[chunkx, chunkz][a, b, c].Vertices.Length,
cubes[chunkx, chunkz][a, b, c].Indices, 0, cubes[chunkx, chunkz][a, b, c].Indices.Length / 3);
(Called for every cube)

Now I have read everywhere and it's all saying "Reduce your draw calls by batching them" But I don't understand how I would do that and I couldn't find anything on it really.
Any help would be nice, even some links or something maybe.

##### Share on other sites
Try searching vertex buffer object and voxel polygonization. It looks as if you already have chunks of information,
but I'm not sure about XNA and vertex lists and such: Has your geometry been pushed onto the GPU first?

[s]Your chunks are basically what batching is about. You have 100kpolys here, and 100kpolys there, and they're either static for the rest of the scene,[/s]
[s]or not updated every frame or so. : Then you produce one vertex buffer of the two combined, reducing your draw calls by half.[/s]

[color="#1C2837"]GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, cubes[chunkx, chunkz][a, b, c].Vertices,[color="#1C2837"]0, cubes[chunkx, chunkz][a, b, c].Vertices.Length,
cubes[chunkx, chunkz][a, b, c].Indices, 0, cubes[chunkx, chunkz][a, b, c].Indices.Length / 3);

[color="#1C2837"](Called for every cube)[/quote]

Wait. - Called for every cube?

You shouldn't do that.

You have 60 cubes in a 32^3 area for example, and they're either static for the rest of the scene, or not updated every frame or so.
Generate a list of those 60 cubes in series, and draw that instead. That way you can have lists for only every non-empty 32^3 area.
You could also refer to the voxels from a dependency tree, and rebuild the list with a minimum of changes, that's what i do.

Obviously, when polygonising voxel cell edges, it's also important to do that only to the boundary between empty and non-empty cells.

[s]How many polygons are you trying to render, and what is your hardware like?[/s]

##### Share on other sites
Each "draw call" (such as DrawUserIndexedPrimitives) is known as a "batch". You want to do the least amount of these as possible. In a complex modern game scene, I'd estimate about 1000 to 2000 would be required.... so 50,000 is indeed a huuuuuuge number of batches!

You want to be using DrawIndexedPrimitives instead of DrawUserIndexedPrimitives, which obtains it's vertex data from a VertexBuffer object.
You then want to put as many cubes as you can in that VertexBuffer object, so you can draw them all at once in a single batch.

##### Share on other sites

Try searching vertex buffer object and voxel polygonization. It looks as if you already have chunks of information,
but I'm not sure about XNA and vertex lists and such: Has your geometry been pushed onto the GPU first?

[s]Your chunks are basically what batching is about. You have 100kpolys here, and 100kpolys there, and they're either static for the rest of the scene,[/s]
[s]or not updated every frame or so. : Then you produce one vertex buffer of the two combined, reducing your draw calls by half.[/s]

[color="#1C2837"]GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, cubes[chunkx, chunkz][a, b, c].Vertices,[color="#1C2837"]0, cubes[chunkx, chunkz][a, b, c].Vertices.Length,
cubes[chunkx, chunkz][a, b, c].Indices, 0, cubes[chunkx, chunkz][a, b, c].Indices.Length / 3);

[color="#1C2837"](Called for every cube)

Wait. - Called for every cube?

You shouldn't do that.

You have 60 cubes in a 32^3 area for example, and they're either static for the rest of the scene, or not updated every frame or so.
Generate a list of those 60 cubes in series, and draw that instead. That way you can have lists for only every non-empty 32^3 area.
You could also refer to the voxels from a dependency tree, and rebuild the list with a minimum of changes, that's what i do.

Obviously, when polygonising voxel cell edges, it's also important to do that only to the boundary between empty and non-empty cells.

[s]How many polygons are you trying to render, and what is your hardware like?[/s]
[/quote]
Thanks for the responses, but what do you mean generate a list like list<>?
Do you think you could give an example? =] Thank you

##### Share on other sites

[quote name='SuperVGA' timestamp='1305179126' post='4809670']
Try searching vertex buffer object and voxel polygonization. It looks as if you already have chunks of information,
but I'm not sure about XNA and vertex lists and such: Has your geometry been pushed onto the GPU first?

[s]Your chunks are basically what batching is about. You have 100kpolys here, and 100kpolys there, and they're either static for the rest of the scene,[/s]
[s]or not updated every frame or so. : Then you produce one vertex buffer of the two combined, reducing your draw calls by half.[/s]

[color="#1C2837"]GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, cubes[chunkx, chunkz][a, b, c].Vertices,[color="#1C2837"]0, cubes[chunkx, chunkz][a, b, c].Vertices.Length,
cubes[chunkx, chunkz][a, b, c].Indices, 0, cubes[chunkx, chunkz][a, b, c].Indices.Length / 3);

[color="#1C2837"](Called for every cube)

Wait. - Called for every cube?

You shouldn't do that.

You have 60 cubes in a 32^3 area for example, and they're either static for the rest of the scene, or not updated every frame or so.
Generate a list of those 60 cubes in series, and draw that instead. That way you can have lists for only every non-empty 32^3 area.
You could also refer to the voxels from a dependency tree, and rebuild the list with a minimum of changes, that's what i do.

Obviously, when polygonising voxel cell edges, it's also important to do that only to the boundary between empty and non-empty cells.

[s]How many polygons are you trying to render, and what is your hardware like?[/s]
[/quote]
Thanks for the responses, but what do you mean generate a list like list<>?
Do you think you could give an example? =] Thank you
[/quote]

list<> could be used, but i use vector<> for my example.
I'll try, although this will be somewhat pseudocode as I haven't used DX for a while;

 struct coord { float x, y, z; } struct my_vertex { float x, y, z; float r, g, b; } vector< my_vertex > cube_vertices; unsigned cube_buffer; vector< coord > cubes unsigned batch_buffer; vector< my_vertex > batch_vertices; // You do this? void render_all() { for(unsigned c = 0; cubes.size() != c; ++c) { // call cube_buffer assembled from cube_vertices (24 points) } } // Do this once (or every time the region has changed) void generate_buffer(const coord &from_point_in_space, const coord &to_point_in_space) { // (for all edges between the two given points, find a subset of edges between solid and nonsolid) for(unsigned c = 0; edges_between_solid_and_nonsolid.size() != c; ++c) { // put vertices for edge into the batch_vertices } // generate batch_buffer - vertex buffer object from batch_vertices } void render_all_2() { // Call drawing function for batch_buffer (will draw all cells between from_point_in_space and to_point_in_space given earlier) } 

You should have multiple batches if you need to render a high-resolution voxel set.

For ease of reading, here are the steps listed chronologically:
1 (on update): Examine each voxel in a specified zone for adjancent "air", then add the sides on the boundary between that air voxel and this solid voxel. you'll end up with a vector of vertices, that represent the geometry of the specified zone. This vector is your batch geometry.
2 (also on update): Transfer this to your gpu and get a uid for that particular vertex buffer. That would be the batch vertex buffer for the specified zone.
3 (every frame): Call the draw function for the buffer of every zone you want to render, and you're done.

1. 1
2. 2
3. 3
4. 4
5. 5
Rutin
11

• 12
• 19
• 10
• 14
• 10
• ### Forum Statistics

• Total Topics
632665
• Total Posts
3007710
• ### Who's Online (See full list)

There are no registered users currently online

×