2
\$\begingroup\$

I'm working on a libgdx implementation of mesh terrain and running into some performance issues.

The terrain is represented by a number of mesh tiles, each mesh is made of vertices laid onto a 2D plane.

The implementation of the meshes is done via libgdx Mesh, each of which is cached after its initial generation. I use GL3.0 and therefore the vertices are handled via VertexBufferObjectWithVAO which as I understand it should allow GPU caching. Meshes are indexed.

Aiming to optimise performance, I have tried to increase the number of vertices in each mesh (while keeping the same overall amount of vertices) but weirdly the performance gets worse rather than improving.

Question 1: any possible reasons why given the same total number of vertices the scenario with lower amount of meshes (#3 below) is slower than the scenarios with higher number of meshes?

Question 2: based on the OPENGL pipeline summarised below is it correct to assume that VBOs are being transferred to the GPU once and then drawn via GPU memory reference?

Performance comparison

  1. 1,600 meshes * 3,042 vert (4.8M vertices) -> 131 FPS

  2. 625 meshes * 11,250 vert (4.5M vertices) -> 132 FPS

  3. 100 meshes * 45,000 vert (4.5M vertices) -> 113 FPS

Hardware Details

GTX660 2GB Memory Utilisation during test 70% in all scenarios. Vertex allocation memory impact seems to be negligible compared to textures.

OPENGL pipeline

From API TRACE, this is the frame life-cycle in summary

mesh generation (one off)

glGenBuffers()
glGenVertexArrays()

render (every frame)

glClear(GL_COLOR_BUFFER_BIT)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glUseProgram(...)
glUniformMatrix4fv(...)
glUniform1f(...)
glActiveTexture(...)
glBindTexture(...)
...
glBindVertexArray(...)
glBindBuffer(...)
glDrawElements(...)
[for each mesh]

*edited to clarify that I have tried to group the vertices into less amount of meshes to reduce number of draw calls

**edited to provide more data and streamline questions.

\$\endgroup\$
  • \$\begingroup\$ Please edit to clarify exactly what you mean by increasing the size of the mesh (i.e.increasing the vertex count, increasing the distance between vertices, etc). \$\endgroup\$ – Pikalek Feb 14 at 16:00
  • \$\begingroup\$ Are you using a Vertex Only draw call, or are you also using Indices? The reason I ask is that for a uniform grid, you will usually end up calculating the same vertex up to 6 times in the vertex buffer. As you will have 6 triangles sharing the same point. Also, depending on the size of the triangles, you will start getting pipeline issues if the triangles become either the same size or smaller than your resolution. LOD helps, and using index buffers in this case also assists LOD (you can have a number of LOD levels assocated to your mesh). \$\endgroup\$ – ErnieDingo Feb 14 at 21:44
  • \$\begingroup\$ @ErnieDingo Meshes are indexed with shared vertices between triangles. \$\endgroup\$ – Adunato Feb 15 at 12:19
1
\$\begingroup\$

There could be a number of reasons why performance is sluggish, I'll mention some of which I think are the biggest ones, based on your question:

  • Bigger mesh does not mean less performance-intensive. Assuming a mesh is clearly visible by the camera (not cut-off) then the smallest it is, the less pixels have to calculate how to draw it (in shaders).
  • The idea on VBO is that you give a mesh to the GPU, and then you can draw that mesh multiple times on different places. In your case that you are drawing a terrain, if it's anything similar to games like minecraft, you'd only need to upload to the GPU a few different meshes, and then you'd be able to draw massive amounts of land with them.
  • A classic trap new people fall into is that they think the whole virtual world is always active and being drawn. That is not the case. No matter how big your map is, you'd almost never draw all of it. Divine it into chunks, load only the ones closest to the player. If the player is approaching the edge of a chunk, load the next one. With this method you can make "infinite" worlds without performance suffering.
\$\endgroup\$
  • \$\begingroup\$ Thanks. Bigger mesh = better performance assumption in the context of same amount of vertices in frustum, i.e. less draw calls with same total vertices. Good reminder about culling but it's not the issue here. \$\endgroup\$ – Adunato Feb 14 at 12:41
  • \$\begingroup\$ The VBO in separate instances is a good point but I have not been able to generalise a mesh template as my implementation passes vertex-specific attributes in the mesh to support shader real-time texture swapping mixed with noise. Still I would expect VBOs to be uploaded once and render cheaply after that. \$\endgroup\$ – Adunato Feb 14 at 12:44
  • \$\begingroup\$ I'm leaning towards GPU memory exhaustion which would lead to the VBOs being streamed into the GPU at every frame. Will do some testing and update. \$\endgroup\$ – Adunato Feb 14 at 12:45
  • \$\begingroup\$ @Adunato bigger mesh does not in any way mean better performance, ever. If you have 3 vertices in the frustum, that create a triangle, the closer they are to the "camera" (and the bigger the surface it draws on the screen) the more resource-intensive it is to draw it. Keep in mind that the pixel shader runs for each individual pixel on the screen. If you mean it's better to have 1 object covering the whole screen, than having 4 objects covering a quarter of the screen, then yes, that's true. \$\endgroup\$ – TomTsagk Feb 14 at 13:14
  • \$\begingroup\$ @Adunato Do you mean that you upload to the GPU object word of 4,500,000 vertices? When you do that, have you checked that the GPU that you have can actually hold that many? Have you got a fallback? \$\endgroup\$ – TomTsagk Feb 14 at 13:16

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.