ecere/gfx/drivers/OpenGL: Optimized immediate mode emulation
authorJerome St-Louis <jerome@ecere.com>
Mon, 11 Jul 2016 04:16:48 +0000 (00:16 -0400)
committerJerome St-Louis <jerome@ecere.com>
Thu, 28 Jul 2016 21:35:40 +0000 (17:35 -0400)
- Making use of glBufferSubData
- Batching glyphs of a single WriteText() call

ecere/src/gfx/drivers/OpenGLDisplayDriver.ec
ecere/src/gfx/drivers/gl3/glHelpers.h
ecere/src/gfx/drivers/gl3/glab.ec
ecere/src/gfx/drivers/gl3/immediate.ec

index e1811ab..01ac726 100644 (file)
@@ -360,6 +360,8 @@ public void GLSetupLighting(bool enable)
 }
 #endif
 
+static GLuint lastBlitTex;
+
 static int displayWidth, displayHeight;
 
 #define GL_CLAMP_TO_EDGE 0x812F
@@ -428,6 +430,10 @@ class OGLSystem : struct
    GLXDrawable glxDrawable;
 #endif
    GLCapabilities capabilities;
+
+   // Buffer Data
+   uint16 *shortBDBuffer;
+   uint shortBDSize;
 };
 
 class OGLSurface : struct
@@ -973,6 +979,7 @@ class OpenGLDisplayDriver : DisplayDriver
          glDeleteShader(oglSystem.vertexShader);
 #endif
 
+      delete oglSystem.shortBDBuffer;
       glimtkTerminate();
 
    #if defined(__WIN32__)
@@ -1021,7 +1028,15 @@ class OpenGLDisplayDriver : DisplayDriver
 
 #if ENABLE_GL_SHADERS
       if(glcaps_shaders)
+      {
+#if ENABLE_GL_LEGACY
+         glDisableClientState(GL_VERTEX_ARRAY);
+         glDisableClientState(GL_NORMAL_ARRAY);
+         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+         glDisableClientState(GL_COLOR_ARRAY);
+#endif
          loadShaders(display.displaySystem, "<:ecere>shaders/fixed.vertex", "<:ecere>shaders/fixed.frag");
+      }
 #if ENABLE_GL_LEGACY
       else
       {
@@ -2261,18 +2276,25 @@ class OpenGLDisplayDriver : DisplayDriver
    void Blit(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h)
    {
       OGLSurface oglSurface = surface.driverData;
+      GLuint tex = (GLuint)(uintptr)bitmap.driverData;
+      if(!tex) return;
 
       if(!oglSurface.writingText)
       {
          // glTranslatef(-0.375f, -0.375f, 0.0f);
          GLSetupTexturing(true);
          GLColor4fv(oglSurface.bitmapMult);
+         glBindTexture(GL_TEXTURE_2D, tex);
+         GLBegin(GLIMTKMode::quads);
+      }
+      else if(lastBlitTex != tex)
+      {
+         if(lastBlitTex)
+            GLEnd();
+         glBindTexture(GL_TEXTURE_2D, tex);
+         GLBegin(GLIMTKMode::quads);
+         lastBlitTex = tex;
       }
-      else if(oglSurface.xOffset)
-         GLTranslated(oglSurface.xOffset / 64.0/*-0.375*/, 0.0, 0.0);
-
-      glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)bitmap.driverData);
-      GLBegin(GLIMTKMode::quads);
 
       if(h < 0)
       {
@@ -2307,16 +2329,12 @@ class OpenGLDisplayDriver : DisplayDriver
          GLTexCoord2f((float)sx/ bitmap.width, (float)(sy+h)/ bitmap.height);
          GLVertex2f((float)dx+surface.offset.x, (float)dy+h+surface.offset.y);
       }
-      GLEnd();
-
       if(!oglSurface.writingText)
       {
+         GLEnd();
          GLSetupTexturing(false);
-
          //glTranslate(0.375, 0.375, 0.0);
       }
-      else if(oglSurface.xOffset)
-         GLTranslated(-oglSurface.xOffset / 64.0/*+0.375*/, 0.0, 0.0);
    }
 
    void Stretch(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh)
@@ -2569,41 +2587,49 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void WriteText(Display display, Surface surface, int x, int y, const char * text, int len, int prevGlyph, int * rPrevGlyph)
    {
-      OGLSurface oglSurface = surface.driverData;
-      OGLSystem oglSystem = display.displaySystem.driverData;
-      oglSystem.loadingFont = true;
+      if(len && text[0])
+      {
+         OGLSurface oglSurface = surface.driverData;
+         OGLSystem oglSystem = display.displaySystem.driverData;
+         oglSystem.loadingFont = true;
 
-      //glTranslated(-0.375, -0.375, 0.0);
+         //glTranslated(-0.375, -0.375, 0.0);
 
-      if(surface.textOpacity)
-      {
-         int w = 0, h, adv = 0;
-         FontExtent(display.displaySystem, surface.font, text, len, &w, &h, 0, null, &adv);
-         w += adv;
-         display.displaySystem.driver.Area(display, surface,x,y,x+w-1,y+h-1);
-      }
+         if(surface.textOpacity)
+         {
+            int w = 0, h, adv = 0;
+            FontExtent(display.displaySystem, surface.font, text, len, &w, &h, 0, null, &adv);
+            w += adv;
+            display.displaySystem.driver.Area(display, surface,x,y,x+w-1,y+h-1);
+         }
 
-      oglSurface.writingText = true;
+         oglSurface.writingText = true;
 
-      GLSetupTexturing(true);
+         GLSetupTexturing(true);
 
-      if(surface.font.outlineSize)
-      {
-         ColorAlpha outlineColor = surface.outlineColor;
-         GLColor4ub(outlineColor.color.r, outlineColor.color.g, outlineColor.color.b, outlineColor.a);
-         oglSurface.writingOutline = true;
+         if(surface.font.outlineSize)
+         {
+            ColorAlpha outlineColor = surface.outlineColor;
+            GLColor4ub(outlineColor.color.r, outlineColor.color.g, outlineColor.color.b, outlineColor.a);
+            oglSurface.writingOutline = true;
+            lastBlitTex = 0;
+            ((subclass(DisplayDriver))class(LFBDisplayDriver)).WriteText(display, surface, x, y, text, len, prevGlyph, rPrevGlyph);
+            if(lastBlitTex) GLEnd();
+            oglSurface.writingOutline = false;
+         }
+         GLColor4fv(oglSurface.foreground);
+
+         lastBlitTex = 0;
          ((subclass(DisplayDriver))class(LFBDisplayDriver)).WriteText(display, surface, x, y, text, len, prevGlyph, rPrevGlyph);
-         oglSurface.writingOutline = false;
-      }
-      GLColor4fv(oglSurface.foreground);
+         if(lastBlitTex) GLEnd();
 
-      ((subclass(DisplayDriver))class(LFBDisplayDriver)).WriteText(display, surface, x, y, text, len, prevGlyph, rPrevGlyph);
-      oglSurface.writingText = false;
-      oglSystem.loadingFont = false;
+         oglSurface.writingText = false;
+         oglSystem.loadingFont = false;
 
-      GLSetupTexturing(false);
+         GLSetupTexturing(false);
 
-      //glTranslated(0.375, 0.375, 0.0);
+         //glTranslated(0.375, 0.375, 0.0);
+      }
    }
 
    void TextFont(Display display, Surface surface, Font font)
@@ -3246,20 +3272,20 @@ class OpenGLDisplayDriver : DisplayDriver
          OGLMesh oglMesh = mesh.data;
          if(!flags) flags = mesh.flags;
          if(flags.vertices)
-            oglMesh.vertices.upload(
-               mesh.nVertices * (mesh.flags.doubleVertices ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.vertices);
+            oglMesh.vertices.allocate(
+               mesh.nVertices * (mesh.flags.doubleVertices ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.vertices, staticDraw);
 
          if(flags.normals)
-            oglMesh.normals.upload(
-               mesh.nVertices * (mesh.flags.doubleNormals ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.normals);
+            oglMesh.normals.allocate(
+               mesh.nVertices * (mesh.flags.doubleNormals ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.normals, staticDraw);
 
          if(flags.texCoords1)
-            oglMesh.texCoords.upload(
-               mesh.nVertices * sizeof(Pointf), mesh.texCoords);
+            oglMesh.texCoords.allocate(
+               mesh.nVertices * sizeof(Pointf), mesh.texCoords, staticDraw);
 
          if(flags.colors)
-            oglMesh.colors.upload(
-               mesh.nVertices * sizeof(ColorRGBAf), mesh.colors);
+            oglMesh.colors.allocate(
+               mesh.nVertices * sizeof(ColorRGBAf), mesh.colors, staticDraw);
       }
    }
 
@@ -3306,13 +3332,27 @@ class OpenGLDisplayDriver : DisplayDriver
                glGenBuffers(1, &oglIndices.buffer.buffer);
             if(glabCurElementBuffer != oglIndices.buffer.buffer)
                GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oglIndices.buffer.buffer);
-            glimtkBufferDatai(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32) * nIndices, oglIndices.indices, GL_STATIC_DRAW);
+
+            {
+               uint * pointer = (uint *)oglIndices.indices;
+               int i;
+               uint16 * b;
+               if(nIndices > oglSystem.shortBDSize)
+               {
+                  oglSystem.shortBDSize = nIndices;
+                  oglSystem.shortBDBuffer = renew oglSystem.shortBDBuffer uint16[oglSystem.shortBDSize];
+               }
+               b = oglSystem.shortBDBuffer;
+               for(i = 0; i < nIndices; i++)
+                  b[i] = (uint16)pointer[i];
+
+               glBufferData(GL_ELEMENT_ARRAY_BUFFER, nIndices * sizeof(uint16), b, GL_STATIC_DRAW);
          }
          else
 #endif
-         oglIndices.buffer.upload(
+         oglIndices.buffer.allocate(
             nIndices * (indices32bit ? sizeof(uint32) : sizeof(uint16)),
-            oglIndices.indices);
+            oglIndices.indices, staticDraw);
       }
    }
 
index dc15c83..6d621ab 100644 (file)
    #define GLTexCoord2d(x,y)                 (glcaps_immediate ? glTexCoord2d(x,y) : glimtkTexCoord2d(x,y))
    #define GLTexCoord2fv(v)                  (glcaps_immediate ? glTexCoord2fv(v) : glimtkTexCoord2fv(v))
    #define GLNormal3f(x,y,z)                 (glcaps_immediate ? glNormal3f : glimtkNormal3f)
+   #define GLNormal3d(x,y,z)                 (glcaps_immediate ? glNormal3d : glimtkNormal3d)
    #define GLNormal3fv(v)                    (glcaps_immediate ? glNormal3fv(v) : glimtkNormal3fv(v))
+   #define GLNormal3dv(v)                    (glcaps_immediate ? glNormal3dv(v) : glimtkNormal3dv(v))
    #define GLColor3f(a,b,c)                  (glcaps_immediate ? glColor3f(a,b,c) : glimtkColor3f(a,b,c))
    #define GLColor4ub(a,b,c,d)               (glcaps_immediate ? glColor4ub(a,b,c,d) : glimtkColor4ub(a,b,c,d))
    #define GLColor4f(a,b,c,d)                (glcaps_immediate ? glColor4f(a,b,c,d) : glimtkColor4f(a,b,c,d))
    #define GLColor4ub                        glimtkColor4ub
    #define GLColor4f                         glimtkColor4f
    #define GLColor4fv                        glimtkColor4fv
-   #define GLNormal3fv                       glimtkNormal3fv
    #define GLNormal3f                        glimtkNormal3f
+   #define GLNormal3d                        glimtkNormal3d
+   #define GLNormal3fv                       glimtkNormal3fv
+   #define GLNormal3dv                       glimtkNormal3dv
    #define GLTexCoord2fv                     glimtkTexCoord2fv
    #define GLVertex3d                        glimtkVertex3d
    #define GLVertex3dv                       glimtkVertex3dv
index 8d7618b..ff3bd6b 100644 (file)
@@ -7,48 +7,66 @@ import "OpenGLDisplayDriver"
 
 // Kept public for now
 
-// NOTE: Don't call if without vertexBuffer
 public void GLABDeleteBuffers(int count, GLAB * buffers)
 {
-   int i;
-   for(i = 0; i < count; i++)
+   if(glcaps_vertexBuffer)
    {
-      uint buffer = buffers[i].buffer;
-      if(buffer)
+      int i;
+      for(i = 0; i < count; i++)
       {
-         if(buffer == glabCurArrayBuffer)
-            GLABBindBuffer(GL_ARRAY_BUFFER, 0);
-         else if(buffer == glabCurElementBuffer)
-            GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+         uint buffer = buffers[i].buffer;
+         if(buffer)
+         {
+            if(buffer == glabCurArrayBuffer)
+               GLABBindBuffer(GL_ARRAY_BUFFER, 0);
+            else if(buffer == glabCurElementBuffer)
+               GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+         }
       }
+      if(count && buffers[0].buffer)
+         glDeleteBuffers(count, (GLuint *)buffers);
    }
-   if(count && buffers[0].buffer)
-      glDeleteBuffers(count, (GLuint *)buffers);
 }
 
 // NOTE: Don't call if without vertexBuffer
 void GLABBindBuffer(int target, uint buffer)
 {
-   glBindBuffer(target, buffer);
-   if(target == GL_ARRAY_BUFFER)
-      glabCurArrayBuffer = buffer;
-   else if(target == GL_ELEMENT_ARRAY_BUFFER)
-      glabCurElementBuffer = buffer;
+   if(glcaps_vertexBuffer)
+   {
+      glBindBuffer(target, buffer);
+      if(target == GL_ARRAY_BUFFER)
+         glabCurArrayBuffer = buffer;
+      else if(target == GL_ELEMENT_ARRAY_BUFFER)
+         glabCurElementBuffer = buffer;
+   }
 }
 
 public enum GLBufferContents { vertex, normal, texCoord, color };
 
+public enum GLBufferUsage { staticDraw, dynamicDraw, streamDraw };
+
+static GLint bufferUsages[] = { GL_DYNAMIC_DRAW, GL_STATIC_DRAW, GL_STREAM_DRAW };
+
 public define noAB = GLAB { 0 };
 
 uint glabCurArrayBuffer;
 
+static short *shortVPBuffer = null;
+static uint shortVPSize = 0;
+
+void glabTerminate()
+{
+   shortVPSize = 0;
+   delete shortVPBuffer;
+}
+
 public struct GLAB
 {
    uint buffer;
 
-   void upload(uint size, void * data)
+   void allocate(uint size, void * data, GLBufferUsage usage)
    {
-      if(this != null && data)
+      if(this != null)
       {
          if(glcaps_vertexBuffer)
          {
@@ -56,13 +74,23 @@ public struct GLAB
                glGenBuffers(1, &buffer);
             if(glabCurArrayBuffer != buffer)
                GLABBindBuffer(GL_ARRAY_BUFFER, buffer);
-            glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);  //GL_DYNAMIC_DRAW);
+            glBufferData(GL_ARRAY_BUFFER, size, data, bufferUsages[usage]);
          }
          else
             buffer = 1;
       }
    }
 
+   void upload(uint offset, uint size, void * data)
+   {
+      if(this != null && glcaps_vertexBuffer)
+      {
+         if(glabCurArrayBuffer != buffer)
+            GLABBindBuffer(GL_ARRAY_BUFFER, buffer);
+         glBufferSubData(GL_ARRAY_BUFFER, offset, size, data);
+      }
+   }
+
    void free()
    {
       if(this != null && buffer)
@@ -86,10 +114,10 @@ public struct GLAB
       if(!glcaps_shaders)
          switch(contents)
          {
-            case normal:   glNormalPointer(type, stride, pointer); break;
-            case vertex:   glVertexPointer(n, type, stride, pointer); break;
-            case texCoord: glTexCoordPointer(n, type, stride, pointer); break;
-            case color:    glColorPointer(n, type, stride, pointer); break;
+            case normal:   glNormalPointer      (type, stride, pointer); break;
+            case vertex:   glVertexPointer   (n, type, stride, pointer); break;
+            case texCoord: glTexCoordPointer (n, type, stride, pointer); break;
+            case color:    glColorPointer    (n, type, stride, pointer); break;
          }
 #endif
    }
@@ -100,7 +128,23 @@ public struct GLAB
       if(glabCurArrayBuffer != ((this != null) ? buffer : 0) && glcaps_vertexBuffer)
          GLABBindBuffer(GL_ARRAY_BUFFER, ((this != null) ? buffer : 0));
       if(type == GL_INT)
-         glimtkVertexPointeri(n, stride, pointer, count);
+      {
+         if(pointer)
+         {
+            int i;
+            if(count*n > shortVPSize)
+            {
+               shortVPSize = count*n;
+               shortVPBuffer = renew shortVPBuffer short[shortVPSize];
+            }
+            for(i = 0; i < count*n; i++)
+               shortVPBuffer[i] = (short)pointer[i];
+
+            GLVertexPointer(n, GL_SHORT, stride, shortVPBuffer);
+         }
+         else
+            GLVertexPointer(n, GL_SHORT, stride, 0);
+      }
       else if(type == GL_DOUBLE)
       {
 #if ENABLE_GL_SHADERS
@@ -127,9 +171,9 @@ public struct GLEAB
 {
    uint buffer;
 
-   void upload(uint size, void * data)
+   void allocate(uint size, void * data, GLBufferUsage usage)
    {
-      if(this != null && data)
+      if(this != null)
       {
          if(glcaps_vertexBuffer)
          {
@@ -139,7 +183,7 @@ public struct GLEAB
             if(glcaps_vertexBuffer && glabCurElementBuffer != buffer)
                GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
             if(size)
-               glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);  //GL_DYNAMIC_DRAW);
+               glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, bufferUsages[usage]);
             else
                ;
          }
@@ -148,6 +192,16 @@ public struct GLEAB
       }
    }
 
+   void upload(uint offset, uint size, void * data)
+   {
+      if(this != null && glcaps_vertexBuffer)
+      {
+         if(glabCurArrayBuffer != buffer)
+            GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
+         glBufferSubData(buffer, offset, size, data);
+      }
+   }
+
    void free()
    {
       if(this != null && buffer)
index 9052f7c..e856028 100644 (file)
@@ -20,16 +20,58 @@ public enum GLIMTKMode
 };
 
 static int beginCount;
-static int vertexCount;
-static int normalCount;
-static float *vertexPointer;
-static float *normalPointer;
 static GLIMTKMode beginMode = unset;
-static uint beginBufferSize, normalBufferSize;
-static int numVertexCoords = 2;
 static bool vertexColorValues = false;
-static int vertexStride = 4;
-static int vertexOffset = 2;
+static int numCoords = 2;     // Number of coordinates per vertex
+static int vertexOffset = 2;  // Offset of vertex info
+
+static struct FloatGLAB : GLAB
+{
+   uint count;       // Count of vertices
+   uint size;        // Size in vertices
+   int stride;      // Number of floats per vertex
+   float * pointer;
+   uint bufSize;     // Size in bytes of VBO
+
+   static inline float * ensure(uint extraVertices)
+   {
+      if(count + extraVertices >= size)
+      {
+         size = size ? (size + size/2) : Max(count + extraVertices, 6);
+         pointer = renew pointer float[size * stride];
+      }
+      return pointer + count * stride;
+   }
+
+   static inline void upload()
+   {
+      uint bufSize = count * stride * sizeof(float);
+      if(bufSize > this.bufSize)
+      {
+         this.bufSize = bufSize;
+         GLAB::allocate(bufSize, null, dynamicDraw);
+      }
+      GLAB::upload(0, bufSize, verticesBuf.pointer);
+   }
+
+   static inline void free()
+   {
+      bufSize = 0;
+      count = 0;
+      size = 0;
+      delete pointer;
+      GLAB::free();
+   }
+};
+
+FloatGLAB verticesBuf { stride = 4 };
+FloatGLAB normalsBuf { stride = 3 };
+
+void glimtkTerminate()
+{
+   verticesBuf.free();
+   normalsBuf.free();
+}
 
 public void glimtkRecti(int a, int b, int c, int d)
 {
@@ -44,240 +86,124 @@ public void glimtkRecti(int a, int b, int c, int d)
 public void glimtkBegin(GLIMTKMode mode)
 {
    beginMode = mode;
-   beginCount = 0;
-   vertexCount = 0;
    vertexColorValues = false;
+   beginCount = 0;
    vertexOffset = 2;
-   vertexStride = 4;
-   numVertexCoords = 2;
 
-   if(!vertexPointer)
-   {
-      normalBufferSize = beginBufferSize = 1024;  // default number of vertices
-      vertexPointer = new float[beginBufferSize * vertexStride];
-      normalPointer = new float[normalBufferSize * 3];
-   }
+   verticesBuf.count = 0;
+   verticesBuf.stride = 4;
+   numCoords = 2;
 }
 
 public void glimtkTexCoord2f(float x, float y)
 {
-   int count = vertexCount;
-
-   if(vertexCount + numVertexCoords > beginBufferSize)
-   {
-      beginBufferSize = beginBufferSize + beginBufferSize/2;
-      vertexPointer = renew vertexPointer float[beginBufferSize * vertexStride];
-   }
-
-   vertexPointer[count*vertexStride  ] = x;
-   vertexPointer[count*vertexStride+1] = y;
-   count++;
-
-   if(beginMode == quads && ((beginCount % 4) == 3))
+   int stride = verticesBuf.stride;
+   bool quadsAdd = beginMode == quads && !glcaps_quads && ((beginCount % 4) == 3);
+   float * buf = verticesBuf.ensure(quadsAdd ? 3 : 1);
+   buf[0] = x;
+   buf[1] = y;
+   buf += stride;
+   if(quadsAdd)
    {
-      vertexPointer[count*vertexStride  ] = vertexPointer[(count-4)*vertexStride];
-      vertexPointer[count*vertexStride+1] = vertexPointer[(count-4)*vertexStride+1];
-      count++;
-      vertexPointer[count*vertexStride  ] = vertexPointer[(count-3)*vertexStride];
-      vertexPointer[count*vertexStride+1] = vertexPointer[(count-3)*vertexStride+1];
-      count++;
+      buf[0] = buf[-4*stride];
+      buf[1] = buf[-4*stride+1];
+      buf += stride;
+      buf[0] = buf[-3*stride];
+      buf[1] = buf[-3*stride+1];
+      buf += stride;
    }
 }
+
 public void glimtkTexCoord2i(int x, int y)       { glimtkTexCoord2f((float)x, (float)y); }
 public void glimtkTexCoord2d(double x, double y) { glimtkTexCoord2f((float)x, (float)y); }
 public void glimtkTexCoord2fv(float * a)         { glimtkTexCoord2f(a[0], a[1]); }
 
 public void glimtkVertex2f(float x, float y)
 {
-   numVertexCoords = 2;
-   vertexStride = vertexOffset + numVertexCoords;
-
-   if(vertexCount + 4 > beginBufferSize)
-   {
-      beginBufferSize = beginBufferSize + beginBufferSize/2;
-      vertexPointer = renew vertexPointer float[beginBufferSize * vertexStride];
-   }
-
-   vertexPointer[vertexCount*vertexStride+vertexOffset] = x;
-   vertexPointer[vertexCount*vertexStride+vertexOffset + 1] = y;
-   vertexCount++;
-
-   if(beginMode == quads && ((beginCount % 4) == 3))
+   numCoords = 2;
+   verticesBuf.stride = vertexOffset + numCoords;
    {
-      vertexPointer[vertexCount*vertexStride+vertexOffset] = vertexPointer[(vertexCount-4)*vertexStride+vertexOffset];
-      vertexPointer[vertexCount*vertexStride+vertexOffset + 1] = vertexPointer[(vertexCount-4)*vertexStride+vertexOffset + 1];
-      vertexCount++;
-      vertexPointer[vertexCount*vertexStride+vertexOffset] = vertexPointer[(vertexCount-3)*vertexStride+vertexOffset];
-      vertexPointer[vertexCount*vertexStride+vertexOffset + 1] = vertexPointer[(vertexCount-3)*vertexStride+vertexOffset + 1];
-      vertexCount++;
+      int stride = verticesBuf.stride;
+      bool quadsAdd = beginMode == quads && !glcaps_quads && ((beginCount % 4) == 3);
+      float * buf = verticesBuf.ensure(quadsAdd ? 3 : 1) + vertexOffset;
+      buf[0] = x;
+      buf[1] = y;
+      verticesBuf.count++;
+      if(quadsAdd)
+      {
+         buf += stride;
+         buf[0] = buf[-4*stride];
+         buf[1] = buf[-4*stride+1];
+         buf += stride;
+         buf[0] = buf[-3*stride];
+         buf[1] = buf[-3*stride+1];
+         verticesBuf.count+=2;
+      }
    }
    beginCount++;
 }
 public void glimtkVertex2i(int x, int y)         { glimtkVertex2f((float)x, (float)y); }
 public void glimtkVertex2d(double x, double y)   { glimtkVertex2f((float)x, (float)y); }
 
-GLAB streamVecAB, streamNorAB;
-
-public void glimtkEnd()
-{
-   GLIMTKMode mode = beginMode;
-   if(mode == quads)        mode = triangles;
-   else if(mode == polygon) mode = triangleFan;
-
-   GLEnableClientState(TEXCOORDS);
-
-   if(glcaps_vertexBuffer)
-   {
-      streamVecAB.upload(vertexStride * sizeof(float) * vertexCount, vertexPointer);
-      streamVecAB.use(texCoord, 2, GL_FLOAT, vertexStride * sizeof(float), 0);
-   }
-   else
-      noAB.use(texCoord, 2, GL_FLOAT, vertexStride * sizeof(float), vertexPointer);
-
-   if(vertexColorValues)
-   {
-      GLEnableClientState(COLORS);
-      if(glcaps_vertexBuffer)
-         streamVecAB.use(color, 4, GL_FLOAT, vertexStride * sizeof(float), (void *)(2 * sizeof(float)));
-      else
-         noAB.use(color, 4, GL_FLOAT, vertexStride * sizeof(float), vertexPointer + 2);
-
-#if ENABLE_GL_SHADERS
-      if(glcaps_shaders)
-         shader_setPerVertexColor(true);
-#endif
-   }
-
-   if(glcaps_vertexBuffer)
-      streamVecAB.use(vertex, numVertexCoords, GL_FLOAT, vertexStride * sizeof(float), (void *)(vertexOffset * sizeof(float)));
-   else
-      noAB.use(vertex, numVertexCoords, GL_FLOAT, vertexStride * sizeof(float), vertexPointer + vertexOffset);
-
-   if(normalCount && normalCount == vertexCount)
-   {
-      GLEnableClientState(NORMALS);
-      if(glcaps_vertexBuffer)
-      {
-         streamNorAB.upload(3*sizeof(float) * vertexCount, normalPointer);
-         streamNorAB.use(normal, 3, GL_FLOAT, 3*sizeof(float), 0);
-      }
-      else
-         noAB.use(normal, 3, GL_FLOAT, 3*sizeof(float),normalPointer);
-   }
-
-   GLFlushMatrices();
-   glDrawArrays(mode, 0, vertexCount);
-
-   if(normalCount)
-      GLDisableClientState(NORMALS);
-   if(vertexColorValues)
-   {
-      GLDisableClientState(COLORS);
-
-#if ENABLE_GL_SHADERS
-      if(glcaps_shaders)
-         shader_setPerVertexColor(false);
-#endif
-
-   }
-   GLDisableClientState(TEXCOORDS);
-
-   normalCount = 0;
-   vertexColorValues = false;
-   numVertexCoords = 2;
-   beginMode = unset;
-}
-
-// Vertex Pointer
-static float *floatVPBuffer = null;
-static short *shortVPBuffer = null;
-static unsigned int shortVPSize = 0, floatVPSize = 0;
-
-// Buffer Data
-static unsigned short *shortBDBuffer = null;
-static unsigned int shortBDSize = 0;
-
-public void glimtkVertexPointeri(int numCoords, int stride, int *pointer, int numVertices)
-{
-   if(pointer)
-   {
-      int i;
-      if(numVertices*numCoords > shortVPSize)
-      {
-         shortVPSize = numVertices*numCoords;
-         shortVPBuffer = renew shortVPBuffer short[shortVPSize];
-      }
-      for(i = 0; i < numVertices*numCoords; i++)
-         shortVPBuffer[i] = (short)pointer[i];
-
-      GLVertexPointer(numCoords, GL_SHORT, stride, shortVPBuffer);
-   }
-   else
-      GLVertexPointer(numCoords, GL_SHORT, stride, 0);
-}
-
-public void glimtkVertexPointerd(int numCoords, int stride, double *pointer, int numVertices)
+public void glimtkVertex3f( float x, float y, float z )
 {
-   if(pointer)
+   numCoords = 3;
+   verticesBuf.stride = vertexOffset + numCoords;
    {
-      int i;
-      if(numVertices*numCoords > floatVPSize)
+      int stride = verticesBuf.stride;
+      bool quadsAdd = beginMode == quads && !glcaps_quads && ((beginCount % 4) == 3);
+      float * buf = verticesBuf.ensure(quadsAdd ? 3 : 1) + vertexOffset;
+      buf[0] = x;
+      buf[1] = y;
+      buf[2] = z;
+      verticesBuf.count++;
+      if(quadsAdd)
       {
-         floatVPSize = numVertices*numCoords;
-         floatVPBuffer = renew floatVPBuffer float[floatVPSize];
+         buf += stride;
+         buf[0] = buf[-4*stride];
+         buf[1] = buf[-4*stride+1];
+         buf[2] = buf[-4*stride+2];
+         buf += stride;
+         buf[0] = buf[-3*stride];
+         buf[1] = buf[-3*stride+1];
+         buf[2] = buf[-3*stride+2];
+         verticesBuf.count+=2;
       }
-      for(i = 0; i < numVertices*numCoords; i++)
-         floatVPBuffer[i] = (float)pointer[i];
-      GLVertexPointer(numCoords, GL_FLOAT, stride, floatVPBuffer);
    }
-   else
-      GLVertexPointer(numCoords, GL_FLOAT, stride, 0);
-}
-
-public void glimtkTexReuseIntVP(int numCoords)
-{
-   GLTexCoordPointer(numCoords, GL_SHORT, 0, floatVPBuffer);
+   beginCount++;
 }
 
-public void glimtkTexReuseDoubleVP(int numCoords)
-{
-   GLTexCoordPointer(numCoords, GL_FLOAT, 0, floatVPBuffer);
-}
+public void glimtkVertex3d( double x, double y, double z )  { glimtkVertex3f((float)x, (float)y, (float)z); }
+public void glimtkVertex3fv( float* coords )                { glimtkVertex3f(coords[0], coords[1], coords[2]); }
+public void glimtkVertex3dv( double* coords )               { glimtkVertex3f((float)coords[0], (float)coords[1], (float)coords[2]); }
 
 public void glimtkColor4f(float r, float g, float b, float a)
 {
    if(beginMode != unset)
    {
-      int count = vertexCount;
-
+      // Called within glBegin()/glEnd()
       vertexColorValues = true;
       vertexOffset = 6;
-      vertexStride = vertexOffset + numVertexCoords;
-
-      if(vertexCount + vertexStride > beginBufferSize)
+      verticesBuf.stride = vertexOffset + numCoords;
       {
-         beginBufferSize = beginBufferSize + beginBufferSize/2;
-         vertexPointer = renew vertexPointer float[beginBufferSize * vertexStride];
-      }
-
-      vertexPointer[count*vertexStride + 2] = r;
-      vertexPointer[count*vertexStride + 3] = g;
-      vertexPointer[count*vertexStride + 4] = b;
-      vertexPointer[count*vertexStride + 5] = a;
-      count++;
+         int stride = verticesBuf.stride;
+         bool quadsAdd = beginMode == quads && !glcaps_quads && ((beginCount % 4) == 3);
+         float * buf = verticesBuf.ensure(quadsAdd ? 3 : 1) + 2;
+         buf[0] = r, buf[1] = g, buf[2] = b, buf[3] = a;
 
-      if(beginMode == quads && ((beginCount % 4) == 3))
-      {
-         vertexPointer[count*vertexStride + 2] = vertexPointer[(count-4) * vertexStride + 2];
-         vertexPointer[count*vertexStride + 3] = vertexPointer[(count-4) * vertexStride + 3];
-         vertexPointer[count*vertexStride + 4] = vertexPointer[(count-4) * vertexStride + 4];
-         vertexPointer[count*vertexStride + 5] = vertexPointer[(count-4) * vertexStride + 5];
-         count++;
-         vertexPointer[count*vertexStride + 2] = vertexPointer[(count-3) * vertexStride + 2];
-         vertexPointer[count*vertexStride + 3] = vertexPointer[(count-3) * vertexStride + 3];
-         vertexPointer[count*vertexStride + 4] = vertexPointer[(count-3) * vertexStride + 4];
-         vertexPointer[count*vertexStride + 5] = vertexPointer[(count-3) * vertexStride + 5];
-         count++;
+         if(quadsAdd)
+         {
+            buf += stride;
+            buf[0] = buf[-4*stride];
+            buf[1] = buf[-4*stride+1];
+            buf[2] = buf[-4*stride+2];
+            buf[3] = buf[-4*stride+3];
+            buf += stride;
+            buf[0] = buf[-3*stride];
+            buf[1] = buf[-3*stride+1];
+            buf[2] = buf[-3*stride+2];
+            buf[3] = buf[-3*stride+3];
+         }
       }
    }
    else
@@ -302,128 +228,109 @@ public void glimtkColor4f(float r, float g, float b, float a)
    }
 }
 
-public void glimtkColor3f( float r, float g, float b )
-{
-   glimtkColor4f(r, g, b, 1.0f);
-}
-
-public void glimtkColor4ub(byte r, byte g, byte b, byte a)
-{
-   glimtkColor4f(r/255.0f, g/255.0f, b/255.0f, a/255.0f);
-}
+public void glimtkColor3f( float r, float g, float b )      { glimtkColor4f(r, g, b, 1.0f); }
+public void glimtkColor4ub(byte r, byte g, byte b, byte a)  { glimtkColor4f(r/255.0f, g/255.0f, b/255.0f, a/255.0f); }
+public void glimtkColor4fv(float * a)                       { glimtkColor4f(a[0], a[1], a[2], a[3]); }
 
-public void glimtkColor4fv(float * a)
-{
-   glimtkColor4f(a[0], a[1], a[2], a[3]);
-}
 
-public void glimtkBufferDatad(int target, int size, void * data, int usage)
+public void glimtkNormal3f(float x, float y, float z)
 {
-   int numElems = size/sizeof(double);
-   double * dblPtr = (double *)data;
-   int i;
-   if (numElems > floatVPSize)
+   normalsBuf.count = verticesBuf.count;
    {
-      floatVPSize = numElems;
-      floatVPBuffer = renew floatVPBuffer float[floatVPSize];
-   }
-   for (i=0; i< numElems; i++)
-      floatVPBuffer[i] = (float)dblPtr[i];
+      int stride = normalsBuf.stride;
+      bool quadsAdd = beginMode == quads && !glcaps_quads && ((beginCount % 4) == 3);
+      float * buf = normalsBuf.ensure(quadsAdd ? 3 : 1) + 2;
 
-   glBufferData(target, numElems*sizeof(float), floatVPBuffer, usage);
-}
+      buf[0] = x, buf[1] = y, buf[2] = z;
+      normalsBuf.count++;
 
-public void glimtkBufferDatai(int target, int size, void * data, int usage)
-{
-   int numElems = size/sizeof(unsigned int);
-   unsigned int * pointer = (unsigned int *)data;
-   int i;
-   if (numElems > shortBDSize)
-   {
-      shortBDSize = numElems;
-      shortBDBuffer = renew shortBDBuffer uint16[shortBDSize];
+      if(quadsAdd)
+      {
+         buf[0] = buf[-4*stride];
+         buf[1] = buf[-4*stride+1];
+         buf[2] = buf[-4*stride+2];
+         buf += stride;
+         buf[0] = buf[-3*stride];
+         buf[1] = buf[-3*stride+1];
+         buf[2] = buf[-3*stride+2];
+         normalsBuf.count += 2;
+      }
    }
-   for (i=0; i< numElems; i++)
-      shortBDBuffer[i] = (unsigned short)pointer[i];
-
-   glBufferData(target, numElems*sizeof(unsigned short), shortBDBuffer, usage);
 }
+public void glimtkNormal3d(double x, double y, double z)         { glimtkNormal3f((float)x, (float)y, (float)z); }
+public void glimtkNormal3fv(float * coords)                      { glimtkNormal3f(coords[0], coords[1], coords[2]); }
+public void glimtkNormal3fd(double * coords)                     { glimtkNormal3f((float)coords[0], (float)coords[1], (float)coords[2]); }
 
-public void glimtkVertex3f( float x, float y, float z )
-{
-   numVertexCoords = 3;
-   vertexStride = vertexOffset + numVertexCoords;
 
-   if(vertexCount + vertexStride > beginBufferSize)
+public void glimtkEnd()
+{
+   GLIMTKMode mode = beginMode;
+   if(!glcaps_quads)
    {
-      beginBufferSize = beginBufferSize + beginBufferSize/2;
-      vertexPointer = renew vertexPointer float[beginBufferSize * vertexStride];
+      if(mode == quads)        mode = triangles;
+      else if(mode == polygon) mode = triangleFan;
    }
 
-   vertexPointer[vertexCount*vertexStride+vertexOffset] = x;
-   vertexPointer[vertexCount*vertexStride+vertexOffset+1] = y;
-   vertexPointer[vertexCount*vertexStride+vertexOffset+2] = z;
-   vertexCount++;
+   GLEnableClientState(TEXCOORDS);
 
-   if(beginMode == quads && ((beginCount % 4) == 3))
+   if(glcaps_vertexBuffer)
    {
-      vertexPointer[vertexCount*vertexStride+vertexOffset] = vertexPointer[(vertexCount-4)*vertexStride+vertexOffset];
-      vertexPointer[vertexCount*vertexStride+vertexOffset+1] = vertexPointer[(vertexCount-4)*vertexStride+vertexOffset+1];
-      vertexPointer[vertexCount*vertexStride+vertexOffset+2] = vertexPointer[(vertexCount-4)*vertexStride+vertexOffset+2];
-      vertexCount++;
-      vertexPointer[vertexCount*vertexStride+vertexOffset] = vertexPointer[(vertexCount-3)*vertexStride+vertexOffset];
-      vertexPointer[vertexCount*vertexStride+vertexOffset+1] = vertexPointer[(vertexCount-3)*vertexStride+vertexOffset+1];
-      vertexPointer[vertexCount*vertexStride+vertexOffset+2] = vertexPointer[(vertexCount-3)*vertexStride+vertexOffset+2];
-      vertexCount++;
+      verticesBuf.upload();
+      verticesBuf.use(texCoord, 2, GL_FLOAT, verticesBuf.stride * sizeof(float), 0);
    }
-   beginCount++;
-}
-
-public void glimtkVertex3d( double x, double y, double z )  { glimtkVertex3f((float)x, (float)y, (float)z); }
-public void glimtkVertex3fv( float* coords )                { glimtkVertex3f(coords[0], coords[1], coords[2]); }
-public void glimtkVertex3dv( double* coords )               { glimtkVertex3f((float)coords[0], (float)coords[1], (float)coords[2]); }
+   else
+      noAB.use(texCoord, 2, GL_FLOAT, verticesBuf.stride * sizeof(float), verticesBuf.pointer);
 
-public void glimtkNormal3f(float x, float y, float z)
-{
-   normalCount = vertexCount;
-   if(vertexCount + 4 > normalBufferSize)
+   if(vertexColorValues)
    {
-      normalBufferSize = normalBufferSize + normalBufferSize/2;
-      normalPointer = renew normalPointer float[normalBufferSize * 2];
+      GLEnableClientState(COLORS);
+      if(glcaps_vertexBuffer)
+         verticesBuf.use(color, 4, GL_FLOAT, verticesBuf.stride * sizeof(float), (void *)(2 * sizeof(float)));
+      else
+         noAB.use(color, 4, GL_FLOAT, verticesBuf.stride * sizeof(float), verticesBuf.pointer + 2);
+
+#if ENABLE_GL_SHADERS
+      if(glcaps_shaders)
+         shader_setPerVertexColor(true);
+#endif
    }
 
-   normalPointer[normalCount*3+0] = x;
-   normalPointer[normalCount*3+1] = y;
-   normalPointer[normalCount*3+2] = z;
-   normalCount++;
+   if(glcaps_vertexBuffer)
+      verticesBuf.use(vertex, numCoords, GL_FLOAT, verticesBuf.stride * sizeof(float), (void *)(vertexOffset * sizeof(float)));
+   else
+      noAB.use(vertex, numCoords, GL_FLOAT, verticesBuf.stride * sizeof(float), verticesBuf.pointer + vertexOffset);
 
-   if(beginMode == quads && ((beginCount % 4) == 3))
+   if(normalsBuf.count && normalsBuf.count == verticesBuf.count)
    {
-      normalPointer[normalCount*3+0] = normalPointer[(normalCount-4)*3+0];
-      normalPointer[normalCount*3+1] = normalPointer[(normalCount-4)*3+1];
-      normalPointer[normalCount*3+2] = normalPointer[(normalCount-4)*3+2];
-      normalCount++;
-      normalPointer[normalCount*3+0] = normalPointer[(normalCount-3)*3+0];
-      normalPointer[normalCount*3+1] = normalPointer[(normalCount-3)*3+1];
-      normalPointer[normalCount*3+2] = normalPointer[(normalCount-3)*3+2];
-      normalCount++;
+      GLEnableClientState(NORMALS);
+      if(glcaps_vertexBuffer)
+      {
+         normalsBuf.upload();
+         normalsBuf.use(normal, 3, GL_FLOAT, 3*sizeof(float), 0);
+      }
+      else
+         noAB.use(normal, 3, GL_FLOAT, 3*sizeof(float),normalsBuf.pointer);
    }
-}
-public void glimtkNormal3fd(double x, double y, double z)         { glimtkNormal3f((float)x, (float)y, (float)z); }
-public void glimtkNormal3fv(float * coords)                       { glimtkNormal3f(coords[0], coords[1], coords[2]); }
 
-public void glimtkTerminate()
-{
-   delete vertexPointer;
-   delete normalPointer;
-   beginBufferSize = 0;
+   GLFlushMatrices();
+   glDrawArrays(mode, 0, verticesBuf.count);
+
+   if(normalsBuf.count)
+      GLDisableClientState(NORMALS);
+   if(vertexColorValues)
+   {
+      GLDisableClientState(COLORS);
 
-   delete floatVPBuffer;
-   shortVPSize = 0;
+#if ENABLE_GL_SHADERS
+      if(glcaps_shaders)
+         shader_setPerVertexColor(false);
+#endif
 
-   delete shortVPBuffer;
-   floatVPSize = 0;
+   }
+   GLDisableClientState(TEXCOORDS);
 
-   delete shortBDBuffer;
-   shortBDSize = 0;
+   normalsBuf.count = 0;
+   vertexColorValues = false;
+   numCoords = 2;
+   beginMode = unset;
 }