OpenGL ES (GLES 1.1 / 2.0) compliance use of vertex arrays instead of immediate mode?
#1
Lightbulb 
Hi all,

I'm trying to migrate XBMC's GL code to use vertex/color/texcoord arrays instead of immediate mode and am running into some problems. To start, I am modifying the GUIFontTTF.cpp renderer. However, it seems to only render the last character, and the character doesn't look exactly correct.

I am pasting the diff below, please let me now if you see anything I'm doing wrong. Yes, I've added vertices[], texCoords[], and colorArray[] to the CGUIFontTTF class, didn't add that in the diff because I thought it was rather implied.

Thanks!
(btw, I'm new to the forums here, so hi everyone)

Code:
Index: guilib/GUIFontTTF.cpp
===================================================================
--- guilib/GUIFontTTF.cpp       (revision 14611)
+++ guilib/GUIFontTTF.cpp       (working copy)
@@ -19,6 +19,8 @@
  *
  */

+#define USE_VERTEX_ARRAYS      1
+
#include "include.h"
#include "GUIFontTTF.h"
#include "GUIFontManager.h"
@@ -822,6 +824,7 @@
     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
     VerifyGLState();

+#ifndef USE_VERTEX_ARRAYS
     glBegin(GL_QUADS);
#endif
   }
@@ -841,8 +844,22 @@
   m_pD3DDevice->SetTexture(0, NULL);
   m_pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
#elif defined(HAS_SDL_OPENGL)
+#ifndef USE_VERTEX_ARRAYS
   glEnd();
+#else
+
+       glEnableClientState(GL_VERTEX_ARRAY);
+       glEnableClientState(GL_COLOR_ARRAY);
+       glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+       glColorPointer(4, GL_UNSIGNED_BYTE, 0, colorArray);
+       glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+       glVertexPointer(3, GL_FLOAT, 0, vertices);
+       glDrawArrays(GL_QUADS, 0, 4);
+       glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+       glDisableClientState(GL_COLOR_ARRAY);
+       glDisableClientState(GL_VERTEX_ARRAY);
#endif
+#endif
}

void CGUIFontTTF::RenderCharacter(float posX, float posY, const Character *ch, D3DCOLOR dwColor)
@@ -937,6 +954,8 @@

   SDL_FreeSurface(tempSurface);
#elif defined(HAS_SDL_OPENGL)
+
+
   // tex coords converted to 0..1 range
   float tl = texture.x1 / m_textureWidth;
   float tr = texture.x2 / m_textureWidth;
@@ -945,27 +964,69 @@

   GLubyte colors[4] = { (GLubyte)((dwColor >> 16) & 0xff), (GLubyte)((dwColor >> 8) & 0xff), (GLubyte)(dwColor & 0xff), (GLubyte)(dwColor >> 24) };

+#ifndef USE_VERTEX_ARRAYS
   // Top-left vertex (corner)
   glColor4ubv(colors);
   glTexCoord2f(tl, tt);
-  glVertex3f(x1, y1, z1);
+  glVertex3f(x1, y1, z1);      // v0

   // Bottom-left vertex (corner)
   glColor4ubv(colors);
   glTexCoord2f(tr, tt);
-  glVertex3f(x2, y2, z2);
+  glVertex3f(x2, y2, z2);      // v1

   // Bottom-right vertex (corner)
   glColor4ubv(colors);
   glTexCoord2f(tr, tb);
-  glVertex3f(x3, y3, z3);
+  glVertex3f(x3, y3, z3);      // v2

   // Top-right vertex (corner)
   glColor4ubv(colors);
   glTexCoord2f(tl, tb);
-  glVertex3f(x4, y4, z4);
+  glVertex3f(x4, y4, z4);      // v3
+#else
+       vertices[0] = x1;       // v0
+       vertices[1] = y1;
+       vertices[2] = z1;
+       texCoords[0] = tl;
+       texCoords[1] = tt;
+       colorArray[0] = colors[0];
+       colorArray[1] = colors[1];
+       colorArray[2] = colors[2];
+       colorArray[3] = colors[3];

+       vertices[3] = x2;       // v1
+       vertices[4] = y2;
+       vertices[5] = z2;
+       texCoords[2] = tr;
+       texCoords[3] = tl;
+       colorArray[4] = colors[0];
+       colorArray[5] = colors[1];
+       colorArray[6] = colors[2];
+       colorArray[7] = colors[3];
+
+       vertices[6] = x3;       // v2
+       vertices[7] = y3;
+       vertices[8] = z3;
+       texCoords[4] = tr;
+       texCoords[5] = tb;
+       colorArray[8] = colors[0];
+       colorArray[9] = colors[1];
+       colorArray[10] = colors[2];
+       colorArray[11] = colors[3];
+
+       vertices[9] = x4;       // v3
+       vertices[10] = y4;
+       vertices[11] = z4;
+       texCoords[6] = tl;
+       texCoords[7] = tb;
+       colorArray[12] = colors[0];
+       colorArray[13] = colors[1];
+       colorArray[14] = colors[2];
+       colorArray[15] = colors[3];
#endif
+
+#endif
}

// Oblique code - original taken from freetype2 (ftsynth.c)
Reply
#2
Hi there, and welcome.

The problem is that the End() is only called once we're done rendering the entire string.

So it basically goes:

Begin();
while (!done)
RenderCharacter();
End();

Thus, your arrays are going to have to be a lot longer and you're going to have to move along them as you go.

Cheers,
Jonathan
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.


Image
Reply
#3
Thanks Jonathan!

That makes total sense now based on the results I'm seeing.

I'll post back some code after I tackle this issue if anyone is interested.
Reply
#4
annex Wrote:Thanks Jonathan!

That makes total sense now based on the results I'm seeing.

I'll post back some code after I tackle this issue if anyone is interested.

Yeah please do, should give abit of a speed boost shouldn't it?

thx,
topfs2
If you have problems please read this before posting

Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.

Image

"Well Im gonna download the code and look at it a bit but I'm certainly not a really good C/C++ programer but I'd help as much as I can, I mostly write in C#."
Reply
#5
Well if you only convert the 4 vertices for each character into a vertex buffer and run that I think it actually might be slower.

You'll only gain anything if you can reuse vertice buffers somehow. Per rendered string or something.

One idea could be have a map of recently rendered strings, if starting coordinate and transformations are identical to a previous render, reuse the vertice buffer created for teh entire string that was rendered. This would probably speed up static content greatly.
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.


Image
Reply
#6
I agree that for the way the current font rendering is implemented, vertex arrays will give only some minimal gain. The real benefit of using vertex arrays is that it is OpenGL ES 1.x compliant, which is what I'm working towards.
Reply
#7
FYI, I'm no Direct3D expert, but it looks like the original Direct3D code uses vertex arrays for this same purpose.
Reply
#8
Along the lines of what elupus was saying:

Adding the computed vertex array alongside the glyph in the cache would greatly speed up rendering. I'll note this down as a follow-up optimization.
Reply
#9
Note that the glyph in the cache is completely independent of what the resulting thing will be rendered - all it holds is what the character looks like pretty much.

Color, location + size is dependent primarily on the transform, not the letter. So saving the vertex buffer in the cache will serve very little purpose I suspect.

As elupus indicates, color, location + size is constant per rendered string, so if we could have a string cache with:

1. String
2. Position
3. Transform

And save the vertex buffer for that, it would be a significant improvement to static text. In fact, if the transform and positioning were done direct in GL (this would have to include the final rounding as well via a vertex shader or similar though) then saving the vertices on the basis of the string alone would suffice.

Just some other ideas. I think it'd still be worth seeing whether the arrays in and of themselves offer any speed benefits (independent of GL versioning benefits.)

Cheers,
Jonathan
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.


Image
Reply
#10
May I ask why are you porting to OpenGL ES?
Reply
#11
Sure. For fun? Smile

Actually, since there is nothing inherent in XBMC that requires X11, it would be nice to run it outside of X. Coupling this with the fact that many embedded devices are now supporting DirectFB and OpenGL ES from the vendor, it would be nice to run XBMC on one of these.
Reply
#12
Question 
@annex, are you hinting that you might be interested in looking at DirectFB/XDirectFB support for XBMC next?
http://forum.xbmc.org/showthread.php?tid=34562
http://forum.xbmc.org/showthread.php?tid=35139

That would be so cool! Cool
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.
Reply
#13
Yes, my target platform will be running DirectFBGL and OpenGL ES.
Reply
#14
Question 
annex Wrote:Yes, my target platform will be running DirectFBGL and OpenGL ES.
Nice! What hardware are you currently developing this on/for exactly? Big Grin
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.
Reply
#15
jmarshall Wrote:And save the vertex buffer for that, it would be a significant improvement to static text. In fact, if the transform and positioning were done direct in GL (this would have to include the final rounding as well via a vertex shader or similar though) then saving the vertices on the basis of the string alone would suffice.
While we're musing here, optimally the cache should just be the big texture which contains the characters.

If you change to letting GL do the transform (positioning) of the characters, you can create one vertex array with all 4 verts per character, then to render a string, you transform to where each character should be, make a 4 element array with the /indexes/ of the 4 verts in it, then use glDrawElements to do an indexed primitive for each character.

The advantage being the cache is tiny, needing only the font texture and 4 vertices per character, and speed because there's only one VA for the whole font which is pushed to the card. I don't think you'll see any performance benefit from building a new vertex array for every character of every string because under the hood you're pushing the same amount of data per character.

At the very least, I'd disable using color array and set the color before the first character is drawn. GL will track the color to every subsequent vertex and you'll be pushing less data and reducing the vertex pipeline stall created by changing the color.
Reply

Logout Mark Read Team Forum Stats Members Help
OpenGL ES (GLES 1.1 / 2.0) compliance use of vertex arrays instead of immediate mode?1