5
\$\begingroup\$

I'm implementing GUI in OpenGL ES 2.0 to fit with my application framework's existing structure. I'd like to have the UI rendering eat as little mobile processing capacity as possible. So to confirm...

Is the following general approach standard these days, for mobile games?

Use the following for each of controls and text:

  • One VBO - one glDrawElements() call for all elements
  • One texture atlas (certainly just one for text, preferably just one for control backgrounds)
  • Many quads batched in VBO, each quad corner having appropriate UVs to access the character or detail texture in question
  • For overlapping UI "panels", turn on depth buffer & use different float depths per panel
  • Modifiable textfields have their own small, separate VBO modified using glBufferSubData() or glBufferData() (to shorten on backspace/delete) as noted here.

Please confirm / deny / correct existing points or add missing details where necessary.

\$\endgroup\$
  • \$\begingroup\$ Is this speculative? My impression is that there are certain standard ways of doing things that crystallise in our industry, and that this is one such thing - when there are solid reasons to do a certain thing a certain way, say 80% of the time, I guess one can assume a de facto standard. \$\endgroup\$ – Engineer Nov 3 '15 at 11:08
  • \$\begingroup\$ I wouldn't separate text fields in terms of rendering. By simply batching all the vertex data you can together, they should be taken care of automatically. \$\endgroup\$ – snake5 Nov 3 '15 at 12:07
  • \$\begingroup\$ @snake5 Fair enough. I assumed that it would be cheaper to reupload a small buffer using glBufferData, than having to recompact / reupload the whole thing if some characters were deleted, hence the thought to keep dynamic textfields separate - but maybe it really doesn't matter. Also re separating text from the rest, that's so I can use a different shader (e.g. SDF or outline) for text as opposed to plain diffuse shaders for the control graphics. Then again the question is about general methods, not my specific needs. \$\endgroup\$ – Engineer Nov 3 '15 at 13:54
  • \$\begingroup\$ glBufferData has been extremely slow regardless of the situation in my experience. IMO, the best way to avoid it to just use one big buffer for everything. You can still flush submitted vertices whenever you change shaders/textures, that works with either method. \$\endgroup\$ – snake5 Nov 3 '15 at 14:49
  • 1
    \$\begingroup\$ There doesn't seem to be any mention of alpha blending (with back to front rendering). \$\endgroup\$ – Tara Nov 29 '16 at 22:12
3
\$\begingroup\$

Your points seem reasonable.

For static elements that are set up once and never change, you can certainly pack them into a GL_STATIC_DRAW buffer and keep it alive for as long as needed. If you still have to move these static elements around in the screen, then this approach is not very interesting anymore because each would need a transformation matrix that can only be set via uniforms, so it results in one draw call per element.

The "usual" way of handling UI is to just resubmit the whole vertex data each frame, using screen space positions. Generate the final 2D vertexes in the application code and submit to a dynamic VBO. So you can, as described in your first point, draw everything with a single draw call (assuming other things like textures are not involved. Texture altas can help).

So the decision might be between either several draw calls per frame or one large buffer update per frame and a handful of draws.

You should certainly try to batch things that share the save properties together. Drawing with depth enabled is also interesting, since it should lift the requirement of a sorting step before submission.

You can also try to be smart and avoid resubmitting individual UI elements that didn't change or move in a given frame (assuming you are taking the path of generating the final vertex position on the CPU-side).

Since this is about mobile, mandatory read: Best Practices for Working with Vertex Data.

\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks for the response. I wasn't sure about the depth buffer because I assume anyway that it costs GPU time when the depth buffer is turned on; but OTOH if I don't use it, a CPU sorting step is required as you suggest - in fact I think I prefer CPU-side sort and just modify the vertices' z values per quad. About resubmission, I guess just glBufferSubData() at best, glBufferData() at worst? No other tricks? - don't want to resubmit unchanged elements. TY for that article - I found it yet again today and interleaving attributes is another thing I took from there to include in the UI work. \$\endgroup\$ – Engineer Nov 3 '15 at 18:38
  • 1
    \$\begingroup\$ @ArcaneEngineer Actually, glBufferData might be more efficient due to something called "buffer orphaning" (more info here). In a nutshell, it might avoid expensive CPU-GPU synchronization if you just allocate a new buffer, instead of updating an existing. As always, benchmark before making a final choice. As for the depth buffer, I'd give it a try anyways. Depth testing is "free" in any modern renderer, so my guess is that it should be faster than manual sorting... but again, benchmark it first ;) \$\endgroup\$ – glampert Nov 3 '15 at 18:55
  • \$\begingroup\$ Thanks - I was just looking up buffer orphaning concept now in fact - hugely beneficial to understand why to generally prefer glBufferData. FYI I'll leave the question a while still to brew / get more answers but yours and snake5's comments have given good direction. \$\endgroup\$ – Engineer Nov 3 '15 at 19:01
  • 2
    \$\begingroup\$ He who dares, succeeds. All the others lose out. Thanks glampert for making the attempt. \$\endgroup\$ – Engineer Nov 6 '15 at 13:32

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.