Introducing VBO Based rendering of Structures in Minecraft.

in utopian-io •  7 years ago  (edited)

During the last couple of days I have been working on porting most of our Structure rendering code to a faster version. To achieve this certain features were stripped and new once introduced.

VBO Based rendering what is it?

For everybody who is new to rendering 3D models (or 2D models for that matter, since a 3D model is basically a set of coherant 2D models) I will start with a quick explanation. VBO, stands for Vertex Buffer Object based rendering and in comparison to Listbased (or even normal direct rendering) it increases drawing speed of models by reducing the overhead a single draw call makes. It does this by sending all vertex data (so position, normal, color, texture position, and lightmap position for minecraft) as well as the order in which the vertices need to be drawn in one go and storing it in the GPU.

An Example:

Say you would draw a single block in minecraft (with culling disabled, so that all 6 sides are drawn regardless) that produces 6*4 = 24 Vertices. Each vertex exists out of 3+3+4+2=12 Floats and 2 shorts. Which results in 12*32 + 2*16 = 13*32 = 416 Bits that need to be send from the CPU to the GPU for each Vertex. This does not seem all that much, however if we start to increase the amount from one vertex to one block we get: 9,984 Bits, so roughly 10kb. Now we have a nice fast CPU and GPU, but the transfer of that amount of data is still the bottleneck, now you might say, well PCIe is fast sending 10kb over is not much, and you are right, but what if we do that for 30000 Blocks? It then becomes quickly obvious that we need something else to get this to work.

Our solution:

Minecraft already has an internal system to render a lot of blocks very fast: ChunkRendering. Due to its multithreaded nature (it uses more then one thread on the CPU to write the block model data into the Buffers on the CPU side), this system had to be adapted.
Due to my experience with rendering and OpenGL I decided it was time.

Introducing: The TemplateTessellator

By adopting the Tessellator class that MC uses I could write a none multithreaded version that writes the data into the Buffer on the CPU side, by locking the Tessellator after the initial compilation of the modeldata into the buffer and the upload to the GPU, I ensured that it could not be modified. The initial draw compiled the buffer data and uploaded said data to the GPU, after which it remembers the buffer id, which is then send to the GPU to draw.

image.png

Above you can see the code that is used to actually draw the 3D model of the structure. As you can see on the first call it will enter the if-switch that checks for readonlyness, uploads the data to the GPU and marks the tessellator as ready for drawing, by setting it to readonly. Then it proceeds as with any normal drawing call: It uses the preTemplateBufferBinding (private method not displayed here) to perform a TRSR (Translate, Rotate, Scale, Rotate) operation. In order: It translates to the placement position of the primary block of the structure, it will then reverse translate the primary blocks offset from the structures origin to ensure that the primary block appears in the correct spot. It will then perform rotations (as specified by the user) and mirroring (using a -1f scale on either the x- or z-axis depending on the type of mirror selected, hence the disabling of the culling). When a mirror is active it will add an additional translation to move the primary block of the structure back into position, What is left is to explain how this Tessellator is used.

Welcome to the stage: The TemplateRenderHandler

image.png
Now that we have a proper class which can upload and draw structure model data using GPU memory, we need a way to compile the model data of the individual blocks together. Fortunately Minecraft provides a handy class which does this for us: BlockRenderDispatcher. It is capable of writing the correct model data into any BufferBuilder (which thanks to our implementation of the Tessellator we now have) as long as you give it a position and a IBlockAcces. Now what is this BlockAccess, it is a an interface describing a common way to access environment information during rendering. By implementing the interface and looking up the blocks in out Structure we can use it to provide the dispatcher with the needed information for him to fill up our Tessellators BufferBuilder.
Good, so we have a structure compiled and in memory and we are drawing it on the screen. What if the user builds the structure and then never looks at it again? It would be wastefull to leave the data in memory. To circumvent this problem we are using a Cache from Guava, it has a nice callback that gets called when there are too many entries and it is about to delete one. This callback then deletes the data from GPU memory and releases the buffer id for reuse. As you can see we allow a maximum of 50 structures to be in the GPU memory at the same time. This way when a user reopens the UI and reselects the structure it can be reused without the need for a compile and upload again.

Two ScreenShots:

The following two screenshots show our current biggest Structure before and after the change:
image.png
image.png

As you can see it is about a 100x times faster using this system.

Thanks for reading,

Orion



Posted on Utopian.io - Rewarding Open Source Contributors

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

It looks like lighting works now correctly too, so it is not only faster but also better looking!

Looks amazing, our players will love this, great job!

Thank you for the contribution. It has been approved.

Very educational, good job explaining.
Would animated gifs give more impact visually for this post?

You can contact us on Discord.
[utopian-moderator]

Not in this case. The models are all staticly drawn I am working on improvements for this. Maybe then a nice Gif would work very well. Thanks for the tip.

Congratulations @oriononline! You received a personal award!

1 Year on Steemit

Click here to view your Board of Honor

Support SteemitBoard's project! Vote for its witness and get one more award!

Congratulations @oriononline! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 2 years!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Vote for @Steemitboard as a witness to get one more award and increased upvotes!