0
\$\begingroup\$

I am programming at the moment a minecraft clone.

The advantage of this clone is that the whole world is handled as a model. That means: The whole world is represented by 2d flat tiles and is uploaded to ONE vertex buffer.( therefore ONE drawcall)

The indices are stored seperatly in the hard drive because to re generate them every time the game starts is a bit overkill.

The loading time for a 64x64 ( 16x16x256 á la chunk) lies between 3 to 5 seconds.

The problem ist following: I calculated the amount of vertices : One VertexPositionTexture has a size of 20 bytes. Multiplied by 8702348 ( because of 8702348 vertices ) the whole amount sent to the graphics card is over 160 MB !

It's no problem for the graphics card: Constant 60 FPS... but I am intended to _ implement infdev. This means the game has to upload frequently over 160 MB to the graphics card: Pure overkill.

I want to avoid using a vertex buffer for each chunk in order to have a single draw call.

Can you suggest a solution?

Thanks.

The project can be found on https://github.com/NET-D3v3l0p3r/XChunk/tree/master/Chunk

\$\endgroup\$
  • \$\begingroup\$ "but I am intended to _ implement infdev. This means the game has to upload frequently over 160 MB to the graphics card" This appears to be your problem statement, but it's not super clear exactly what this means. Am I correct in assuming the implication here is that you're going to be changing some aspect of the world (adding/removing/changing block data) and that's why you are re-uploading it? \$\endgroup\$ – Josh Apr 13 '17 at 15:58
  • \$\begingroup\$ Also, this related question may be of help: gamedev.stackexchange.com/questions/34771/… (conceptually, I am aware it does cover some OpenGL-specific tech) \$\endgroup\$ – Josh Apr 13 '17 at 16:14
  • \$\begingroup\$ Exactly. All problems are resulting by manipulating the world. \$\endgroup\$ – φ Const. NET Apr 13 '17 at 16:17
1
\$\begingroup\$

I think you're operating under a false premise (namely, that the total draw call count is the ultimate arbiter of performance).

This isn't the case. Certainly, reducing draw calls is a good idea, but sacrificing everything else on that alter is going to lead you down a path where you eventually hurt performance instead.

For example, you're ignoring the costs of doing everything else with all of this extra geometry that, for the most part, probably isn't visible in the world. You may be paying a huge cost for overdraw or over-transformation of geometry, for example.

Chunking up the world is a classical divide-and-conquer strategy: reduce the instantaneous operable problem space by divvying up the total problem space into smaller parts that can be handled on their own (or most importantly, ignored on their own).

I think you will eventually simply hit a wall where you cannot solve performance issues if you remain adamant that you use only a single draw. That said, there are similar divide-and-conquer approaches you can try.

  • If it's possible that only certain subsets of the vertex attributes change when you need to reupload (for example, if it's only the texture coordinates that change to indicate that a block is now gone, or a new type of block, et cetera), you can put the dynamic aspects of that vertex data in its own vertex attribute stream. That way you have a static vertex buffer which never changes, and a dynamic one that uploads every frame. The size of the dynamic buffer will be drastically smaller (for example, around 60MB or so if all it contains are 2D texture coordinates). You can have multiple stream sources per draw call, so this still lets you use one draw call.

  • If your actual GPU workload is small (that is, your shaders are super simple), you can potentially offload some of the world handling dynamic aspects of the world to the GPU itself, possibly storing any input you need for that handling in a texture that you update every frame. Similar to the multiple vertex stream source option above, the idea here is to confine the data you upload every frame to only data that is potentially changing every frame and leave the static stuff alone, so you only need to upload it once.

  • Similar, it sounds like your world is essentially heightmap based (that's what I get from your statement that "the whole world is represented by 2d flat tiles;" thus there are no caves or anything like that. It's reasonable in such an implementation to represent each tile as a single vertex you expand to four in the shader pipeline, for example in a geometry shader. It's potentially possible to do this in a fully-3D (caves allowed) world as well, though harder. This can reduce the total data set (indices and vertices) significantly as well.

  • You can try to update only a portion of the vertex buffer based on where the player actually is; if you can know that the player won't be able to see certain parts of the world that are very far away, you can use a buffer that doesn't discard completely when mapped to the CPU and write only a portion. This may be a performance win, although it may not. You'd have to profile and see.

\$\endgroup\$
  • \$\begingroup\$ Hello, thank you for these ideas! I think monogame didn't implemented geometry shader yet. All in all I think some kind of view-frustum-culling is one of the best ways to deal with such an amount of vertices... thank you \$\endgroup\$ – φ Const. NET Apr 13 '17 at 22:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.