diff mbox

[Branch,~glmark2-dev/glmark2/trunk] Rev 149: SceneBuffer: New scene to benchmark various aspects of buffer update operations.

Message ID 20111011095812.12535.96394.launchpad@ackee.canonical.com
State Accepted
Headers show

Commit Message

alexandros.frantzis@linaro.org Oct. 11, 2011, 9:58 a.m. UTC
Merge authors:
  Alexandros Frantzis (afrantzis)
Related merge proposals:
  https://code.launchpad.net/~linaro-graphics-wg/glmark2/buffer-update/+merge/78869
  proposed by: Alexandros Frantzis (afrantzis)
  review: Approve - Jesse Barker (jesse-barker)
------------------------------------------------------------
revno: 149 [merge]
committer: Alexandros Frantzis <alexandros.frantzis@linaro.org>
branch nick: trunk
timestamp: Tue 2011-10-11 12:39:35 +0300
message:
  SceneBuffer: New scene to benchmark various aspects of buffer update operations.
  
  SceneBuffer provides various options to control the details of the update
  process: whether to use MapBuffer or BufferSubData, how much of the buffer
  to update, how dispersed the update ranges are, the buffer usage hint, whether
  to use interleaved attribute storage and the size of the grid.
  
  Visually the scene draws a grid modulated by a sine wave. The wave's details
  (wavelength, duty-cycle) are affected by some of the options to produce an
  interesting visual result. Furthermore, the grid is drawn using a GLSL wireframe
  technique which is very interesting in and of itself.
added:
  data/shaders/buffer-wireframe.frag
  data/shaders/buffer-wireframe.vert
  src/gl-headers.cpp
  src/scene-buffer.cpp
modified:
  src/android.cpp
  src/canvas-android.cpp
  src/canvas-android.h
  src/canvas-x11-egl.cpp
  src/canvas-x11-egl.h
  src/canvas-x11-glx.cpp
  src/canvas-x11-glx.h
  src/gl-headers.h
  src/main.cpp
  src/mesh.cpp
  src/mesh.h
  src/scene-build.cpp
  src/scene.h


--
lp:glmark2
https://code.launchpad.net/~glmark2-dev/glmark2/trunk

You are subscribed to branch lp:glmark2.
To unsubscribe from this branch go to https://code.launchpad.net/~glmark2-dev/glmark2/trunk/+edit-subscription
diff mbox

Patch

=== added file 'data/shaders/buffer-wireframe.frag'
--- data/shaders/buffer-wireframe.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/buffer-wireframe.frag	2011-10-08 12:31:31 +0000
@@ -0,0 +1,17 @@ 
+varying vec4 dist;
+
+const vec4 LINE_COLOR = vec4(1.0);
+const vec4 TRIANGLE_COLOR = vec4(0.0, 0.5, 0.8, 0.8);
+
+void main(void)
+{
+    // Get the minimum distance of this fragment from a triangle edge.
+    // We need to multiply with dist.w to undo the workaround we had
+    // to perform to get linear interpolation (instead of perspective correct).
+    float d = min(dist.x * dist.w, min(dist.y * dist.w, dist.z * dist.w));
+
+    // Get the intensity of the wireframe line
+    float I = exp2(-2.0 * d * d);
+
+    gl_FragColor = mix(TRIANGLE_COLOR, LINE_COLOR, I);
+}

=== added file 'data/shaders/buffer-wireframe.vert'
--- data/shaders/buffer-wireframe.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/buffer-wireframe.vert	2011-10-05 16:42:33 +0000
@@ -0,0 +1,57 @@ 
+// Wireframe shader based on:
+// J. A. Bærentzen, S. Munk-Lund, M. Gjøl, and B. D. Larsen,
+// “Two methods for antialiased wireframe drawing with hidden
+// line removal,” in Proceedings of the Spring Conference in
+// Computer Graphics, 2008.
+//
+// We are not using geometry shaders, though, as they are not
+// available in GLES 2.0.
+
+attribute vec3 position;
+// Coordinates of the triangle vertices this vertex belongs to
+attribute vec3 tvertex0;
+attribute vec3 tvertex1;
+attribute vec3 tvertex2;
+
+uniform vec2 Viewport;
+uniform mat4 ModelViewProjectionMatrix;
+
+varying vec4 dist;
+
+void main(void)
+{
+    // Get the clip coordinates of all vertices
+    vec4 pos  = ModelViewProjectionMatrix * vec4(position, 1.0);
+    vec4 pos0 = ModelViewProjectionMatrix * vec4(tvertex0, 1.0);
+    vec4 pos1 = ModelViewProjectionMatrix * vec4(tvertex1, 1.0);
+    vec4 pos2 = ModelViewProjectionMatrix * vec4(tvertex2, 1.0);
+
+    // Get the screen coordinates of all vertices
+    vec3 p  = vec3(0.5 * Viewport * (pos.xy / pos.w), 0.0);
+    vec3 p0 = vec3(0.5 * Viewport * (pos0.xy / pos0.w), 0.0);
+    vec3 p1 = vec3(0.5 * Viewport * (pos1.xy / pos1.w), 0.0);
+    vec3 p2 = vec3(0.5 * Viewport * (pos2.xy / pos2.w), 0.0);
+
+    // Get the vectors representing the edges of the current
+    // triangle primitive. 'vN' is the edge opposite vertex N.
+    vec3 v0 = p2 - p1;
+    vec3 v1 = p2 - p0;
+    vec3 v2 = p1 - p0;
+
+    // Calculate the distance of the current vertex from all
+    // the triangle edges. The distance of point p from line
+    // v is length(cross(p - p1, v)) / length(v), where
+    // p1 is any of the two edge points of v.
+    float d0 = length(cross(p - p1, v0)) / length(v0);
+    float d1 = length(cross(p - p2, v1)) / length(v1);
+    float d2 = length(cross(p - p0, v2)) / length(v2);
+
+    // OpenGL(ES) performs perspective-correct interpolation
+    // (it divides by .w) but we want linear interpolation. To
+    // work around this, we premultiply by pos.w here and then
+    // multiple with the inverse (stored in dist.w) in the fragment
+    // shader to undo this operation.
+    dist = vec4(pos.w * d0, pos.w * d1, pos.w * d2, 1.0 / pos.w);
+
+    gl_Position = pos;
+}

=== modified file 'src/android.cpp'
--- src/android.cpp	2011-09-16 08:29:57 +0000
+++ src/android.cpp	2011-10-10 16:10:04 +0000
@@ -45,6 +45,9 @@ 
     "effect2d:kernel=1,1,1,1,1;1,1,1,1,1;1,1,1,1,1;",
     "pulsar:quads=5:texture=false:light=false",
     "desktop:windows=4:effect=blur:blur-radius=5:passes=1:separable=true",
+    "buffer:update-fraction=0.5:update-dispersion=0.9:columns=200:update-method=map:interleave=false",
+    "buffer:update-fraction=0.5:update-dispersion=0.9:columns=200:update-method=subdata:interleave=false",
+    "buffer:update-fraction=0.5:update-dispersion=0.9:columns=200:update-method=map:interleave=true",
     "conditionals:vertex-steps=0:fragment-steps=0",
     "conditionals:vertex-steps=0:fragment-steps=5",
     "conditionals:vertex-steps=5:fragment-steps=0",
@@ -87,6 +90,7 @@ 
     Benchmark::register_scene(*new SceneEffect2D(*g_canvas));
     Benchmark::register_scene(*new ScenePulsar(*g_canvas));
     Benchmark::register_scene(*new SceneDesktop(*g_canvas));
+    Benchmark::register_scene(*new SceneBuffer(*g_canvas));
 
     add_default_benchmarks(g_benchmarks);
 

=== modified file 'src/canvas-android.cpp'
--- src/canvas-android.cpp	2011-09-09 11:24:17 +0000
+++ src/canvas-android.cpp	2011-10-10 11:00:36 +0000
@@ -22,6 +22,7 @@ 
 #include "canvas-android.h"
 #include "log.h"
 #include "options.h"
+#include "gl-headers.h"
 #include <EGL/egl.h>
 
 #include <fstream>
@@ -35,6 +36,8 @@ 
     if (!eglSwapInterval(eglGetCurrentDisplay(), 0))
         Log::info("** Failed to set swap interval. Results may be bounded above by refresh rate.\n");
 
+    init_gl_extensions();
+
     glEnable(GL_DEPTH_TEST);
     glDepthFunc(GL_LEQUAL);
     glEnable(GL_CULL_FACE);
@@ -120,3 +123,24 @@ 
                                                1.0, 1024.0);
 }
 
+void
+CanvasAndroid::init_gl_extensions()
+{
+    /*
+     * Parse the extensions we care about from the extension string.
+     * Don't even bother to get function pointers until we know the
+     * extension is present.
+     */
+    std::string extString;
+    const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
+    if (exts) {
+        extString = exts;
+    }
+
+    if (extString.find("GL_OES_mapbuffer") != std::string::npos) {
+        GLExtensions::MapBuffer = 
+            reinterpret_cast<PFNGLMAPBUFFEROESPROC>(eglGetProcAddress("glMapBufferOES"));
+        GLExtensions::UnmapBuffer = 
+            reinterpret_cast<PFNGLUNMAPBUFFEROESPROC>(eglGetProcAddress("glUnmapBufferOES"));
+    }
+}

=== modified file 'src/canvas-android.h'
--- src/canvas-android.h	2011-08-10 18:00:03 +0000
+++ src/canvas-android.h	2011-10-10 11:00:36 +0000
@@ -40,6 +40,9 @@ 
     void write_to_file(std::string &filename);
     bool should_quit();
     void resize(int width, int height);
+
+private:
+    void init_gl_extensions();
 };
 
 #endif

=== modified file 'src/canvas-x11-egl.cpp'
--- src/canvas-x11-egl.cpp	2011-06-30 12:24:25 +0000
+++ src/canvas-x11-egl.cpp	2011-10-10 11:00:36 +0000
@@ -194,6 +194,33 @@ 
     return true;
 }
 
+void
+CanvasX11EGL::init_gl_extensions()
+{
+#if USE_GLESv2
+    /*
+     * Parse the extensions we care about from the extension string.
+     * Don't even bother to get function pointers until we know the
+     * extension is present.
+     */
+    std::string extString;
+    const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
+    if (exts) {
+        extString = exts;
+    }
+
+    if (extString.find("GL_OES_mapbuffer") != std::string::npos) {
+        GLExtensions::MapBuffer = 
+            reinterpret_cast<PFNGLMAPBUFFEROESPROC>(eglGetProcAddress("glMapBufferOES"));
+        GLExtensions::UnmapBuffer = 
+            reinterpret_cast<PFNGLUNMAPBUFFEROESPROC>(eglGetProcAddress("glUnmapBufferOES"));
+    }
+#elif USE_GL
+    GLExtensions::MapBuffer = glMapBuffer;
+    GLExtensions::UnmapBuffer = glUnmapBuffer;
+#endif
+}
+
 bool
 CanvasX11EGL::make_current()
 {
@@ -211,5 +238,7 @@ 
     if (!eglSwapInterval(egl_display_, 0))
         Log::info("** Failed to set swap interval. Results may be bounded above by refresh rate.\n");
 
+    init_gl_extensions();
+
     return true;
 }

=== modified file 'src/canvas-x11-egl.h'
--- src/canvas-x11-egl.h	2011-06-30 11:45:52 +0000
+++ src/canvas-x11-egl.h	2011-10-10 11:00:36 +0000
@@ -44,6 +44,7 @@ 
     bool ensure_egl_display();
     bool ensure_egl_config();
     bool ensure_egl_surface();
+    void init_gl_extensions();
 
     EGLDisplay egl_display_;
     EGLSurface egl_surface_;

=== modified file 'src/canvas-x11-glx.cpp'
--- src/canvas-x11-glx.cpp	2011-06-30 12:24:25 +0000
+++ src/canvas-x11-glx.cpp	2011-10-10 11:00:36 +0000
@@ -187,6 +187,13 @@ 
     return true;
 }
 
+void
+CanvasX11GLX::init_gl_extensions()
+{
+    GLExtensions::MapBuffer = glMapBuffer;
+    GLExtensions::UnmapBuffer = glUnmapBuffer;
+}
+
 bool
 CanvasX11GLX::ensure_glx_context()
 {
@@ -203,6 +210,8 @@ 
         return false;
     }
 
+    init_gl_extensions();
+
     return true;
 }
 

=== modified file 'src/canvas-x11-glx.h'
--- src/canvas-x11-glx.h	2011-06-30 12:17:09 +0000
+++ src/canvas-x11-glx.h	2011-10-10 11:00:36 +0000
@@ -45,6 +45,7 @@ 
     void init_extensions();
     bool ensure_glx_fbconfig();
     bool ensure_glx_context();
+    void init_gl_extensions();
 
     GLXFBConfig glx_fbconfig_;
     GLXContext glx_context_;

=== added file 'src/gl-headers.cpp'
--- src/gl-headers.cpp	1970-01-01 00:00:00 +0000
+++ src/gl-headers.cpp	2011-10-10 11:00:36 +0000
@@ -0,0 +1,26 @@ 
+/*
+ * Copyright © 2010-2011 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Alexandros Frantzis (glmark2)
+ */
+#include "gl-headers.h"
+
+void* (*GLExtensions::MapBuffer) (GLenum target, GLenum access) = 0;
+GLboolean (*GLExtensions::UnmapBuffer) (GLenum target) = 0;
+

=== modified file 'src/gl-headers.h'
--- src/gl-headers.h	2011-06-30 13:33:37 +0000
+++ src/gl-headers.h	2011-10-10 11:00:36 +0000
@@ -22,13 +22,26 @@ 
 #ifndef GLMARK2_GL_HEADERS_H_
 #define GLMARK2_GL_HEADERS_H_
 
+#define GL_GLEXT_PROTOTYPES
+
 #if USE_GL
-#define GL_GLEXT_PROTOTYPES
 #include <GL/gl.h>
 #include <GL/glext.h>
 #elif USE_GLESv2
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
-#endif
+#ifndef GL_WRITE_ONLY
+#define GL_WRITE_ONLY GL_WRITE_ONLY_OES
+#endif
+#endif
+
+/** 
+ * Struct that holds pointers to functions that belong to extensions
+ * in either GL2.0 or GLES2.0.
+ */
+struct GLExtensions {
+    static void* (*MapBuffer) (GLenum target, GLenum access);
+    static GLboolean (*UnmapBuffer) (GLenum target);
+};
 
 #endif

=== modified file 'src/main.cpp'
--- src/main.cpp	2011-09-20 12:52:21 +0000
+++ src/main.cpp	2011-10-10 16:10:04 +0000
@@ -56,6 +56,9 @@ 
     "effect2d:kernel=1,1,1,1,1;1,1,1,1,1;1,1,1,1,1;",
     "pulsar:quads=5:texture=false:light=false",
     "desktop:windows=4:effect=blur:blur-radius=5:passes=1:separable=true",
+    "buffer:update-fraction=0.5:update-dispersion=0.9:columns=200:update-method=map:interleave=false",
+    "buffer:update-fraction=0.5:update-dispersion=0.9:columns=200:update-method=subdata:interleave=false",
+    "buffer:update-fraction=0.5:update-dispersion=0.9:columns=200:update-method=map:interleave=true",
     "conditionals:vertex-steps=0:fragment-steps=0",
     "conditionals:vertex-steps=0:fragment-steps=5",
     "conditionals:vertex-steps=5:fragment-steps=0",
@@ -124,6 +127,7 @@ 
     scenes.push_back(new SceneEffect2D(canvas));
     scenes.push_back(new ScenePulsar(canvas));
     scenes.push_back(new SceneDesktop(canvas));
+    scenes.push_back(new SceneBuffer(canvas));
 
     for (vector<Scene*>::const_iterator iter = scenes.begin();
          iter != scenes.end();

=== modified file 'src/mesh.cpp'
--- src/mesh.cpp	2011-07-04 10:33:31 +0000
+++ src/mesh.cpp	2011-10-10 14:06:22 +0000
@@ -23,10 +23,12 @@ 
  */
 #include "mesh.h"
 #include "log.h"
+#include "gl-headers.h"
 
 
 Mesh::Mesh() :
-    vertex_size_(0)
+    vertex_size_(0), interleave_(false), vbo_update_method_(VBOUpdateMethodMap),
+    vbo_usage_(VBOUsageStatic)
 {
 }
 
@@ -165,6 +167,63 @@ 
     vertices_.push_back(std::vector<float>(vertex_size_));
 }
 
+/**
+ * Gets the mesh vertices.
+ *
+ * You should use the ::set_attrib() method to manipulate
+ * the vertex data.
+ *
+ * You shouldn't resize the vector (change the number of vertices)
+ * manually. Use ::next_vertex() instead.
+ */
+std::vector<std::vector<float> >&
+Mesh::vertices()
+{
+    return vertices_;
+}
+
+/**
+ * Sets the VBO update method.
+ *
+ * The default value is VBOUpdateMethodMap.
+ */
+void
+Mesh::vbo_update_method(Mesh::VBOUpdateMethod method)
+{
+    vbo_update_method_ = method;
+}
+
+/**
+ * Sets the VBO usage hint.
+ *
+ * The usage hint takes effect in the next call to ::build_vbo().
+ *
+ * The default value is VBOUsageStatic.
+ */
+void
+Mesh::vbo_usage(Mesh::VBOUsage usage)
+{
+    vbo_usage_ = usage;
+}
+
+/** 
+ * Sets the vertex attribute interleaving mode.
+ *
+ * If true the vertex attributes are going to be interleaved in a single
+ * buffer. Otherwise they will be separated into different buffers (one
+ * per attribute).
+ *
+ * Interleaving mode takes effect in the next call to ::build_array() or
+ * ::build_vbo().
+ * 
+ * @param interleave whether to interleave
+ */
+void
+Mesh::interleave(bool interleave)
+{
+    interleave_ = interleave;
+}
+
 void
 Mesh::reset()
 {
@@ -180,11 +239,11 @@ 
 }
 
 void
-Mesh::build_array(bool interleaved)
+Mesh::build_array()
 {
     int nvertices = vertices_.size();
 
-    if (!interleaved) {
+    if (!interleave_) {
         /* Create an array for each attribute */
         for (std::vector<std::pair<int, int> >::const_iterator ai = vertex_format_.begin();
              ai != vertex_format_.end();
@@ -229,16 +288,24 @@ 
 }
 
 void
-Mesh::build_vbo(bool interleave)
+Mesh::build_vbo()
 {
     delete_array();
-    build_array(interleave);
+    build_array();
 
     int nvertices = vertices_.size();
 
     attrib_data_ptr_.clear();
 
-    if (!interleave) {
+    GLenum buffer_usage;
+    if (vbo_usage_ == Mesh::VBOUsageStream)
+        buffer_usage = GL_STREAM_DRAW;
+    if (vbo_usage_ == Mesh::VBOUsageDynamic)
+        buffer_usage = GL_DYNAMIC_DRAW;
+    else /* if (vbo_usage_ == Mesh::VBOUsageStatic) */
+        buffer_usage = GL_STATIC_DRAW;
+
+    if (!interleave_) {
         /* Create a vbo for each attribute */
         for (std::vector<std::pair<int, int> >::const_iterator ai = vertex_format_.begin();
              ai != vertex_format_.end();
@@ -250,7 +317,7 @@ 
             glGenBuffers(1, &vbo);
             glBindBuffer(GL_ARRAY_BUFFER, vbo);
             glBufferData(GL_ARRAY_BUFFER, nvertices * ai->first * sizeof(float),
-                         data, GL_STATIC_DRAW);
+                         data, buffer_usage);
 
             vbos_.push_back(vbo);
             attrib_data_ptr_.push_back(0);
@@ -279,6 +346,137 @@ 
     delete_array();
 }
 
+/**
+ * Updates ranges of a single vertex array.
+ *
+ * @param ranges the ranges of vertices to update
+ * @param n the index of the vertex array to update
+ * @param nfloats how many floats to update for each vertex
+ * @param offset the offset (in floats) in the vertex data to start reading from
+ */
+void
+Mesh::update_single_array(const std::vector<std::pair<size_t, size_t> >& ranges,
+                          size_t n, size_t nfloats, size_t offset)
+{
+    float *array(vertex_arrays_[n]);
+
+    /* Update supplied ranges */
+    for (std::vector<std::pair<size_t, size_t> >::const_iterator ri = ranges.begin();
+         ri != ranges.end();
+         ri++)
+    {
+        /* Update the current range from the vertex data */
+        float *dest(array + nfloats * ri->first);
+        for (size_t n = ri->first; n <= ri->second; n++) {
+            for (size_t i = 0; i < nfloats; i++)
+                *dest++ = vertices_[n][offset + i];
+        }
+
+    }
+}
+
+/**
+ * Updates ranges of the vertex arrays.
+ *
+ * @param ranges the ranges of vertices to update
+ */
+void
+Mesh::update_array(const std::vector<std::pair<size_t, size_t> >& ranges)
+{
+    /* If we don't have arrays to update, create them */
+    if (vertex_arrays_.empty()) {
+        build_array();
+        return;
+    }
+
+    if (!interleave_) {
+        for (size_t i = 0; i < vertex_arrays_.size(); i++) {
+            update_single_array(ranges, i, vertex_format_[i].first,
+                                vertex_format_[i].second);
+        }
+    }
+    else {
+        update_single_array(ranges, 0, vertex_size_, 0);
+    }
+
+}
+
+
+/**
+ * Updates ranges of a single VBO.
+ *
+ * This method use either glMapBuffer or glBufferSubData to perform
+ * the update. The used method can be set with ::vbo_update_method().
+ *
+ * @param ranges the ranges of vertices to update
+ * @param n the index of the vbo to update
+ * @param nfloats how many floats to update for each vertex
+ */
+void
+Mesh::update_single_vbo(const std::vector<std::pair<size_t, size_t> >& ranges,
+                        size_t n, size_t nfloats)
+{
+    float *src_start(vertex_arrays_[n]);
+    float *dest_start(0);
+
+    glBindBuffer(GL_ARRAY_BUFFER, vbos_[n]);
+
+    if (vbo_update_method_ == VBOUpdateMethodMap) {
+        dest_start = reinterpret_cast<float *>(
+                GLExtensions::MapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)
+                );
+    }
+
+    /* Update supplied ranges */
+    for (std::vector<std::pair<size_t, size_t> >::const_iterator iter = ranges.begin();
+         iter != ranges.end();
+         iter++)
+    {
+        float *src(src_start + nfloats * iter->first);
+        float *src_end(src_start + nfloats * (iter->second + 1));
+
+        if (vbo_update_method_ == VBOUpdateMethodMap) {
+            float *dest(dest_start + nfloats * iter->first);
+            std::copy(src, src_end, dest);
+        }
+        else if (vbo_update_method_ == VBOUpdateMethodSubData) {
+            glBufferSubData(GL_ARRAY_BUFFER, nfloats * iter->first * sizeof(float),
+                            (src_end - src) * sizeof(float), src);
+        }
+    }
+
+    if (vbo_update_method_ == VBOUpdateMethodMap)
+        GLExtensions::UnmapBuffer(GL_ARRAY_BUFFER);
+}
+
+/**
+ * Updates ranges of the VBOs.
+ *
+ * @param ranges the ranges of vertices to update
+ */
+void
+Mesh::update_vbo(const std::vector<std::pair<size_t, size_t> >& ranges)
+{
+    /* If we don't have VBOs to update, create them */
+    if (vbos_.empty()) {
+        build_vbo();
+        return;
+    }
+
+    update_array(ranges);
+
+    if (!interleave_) {
+        for (size_t i = 0; i < vbos_.size(); i++)
+            update_single_vbo(ranges, i, vertex_format_[i].first);
+    }
+    else {
+        update_single_vbo(ranges, 0, vertex_size_);
+    }
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+
 void
 Mesh::delete_array()
 {
@@ -351,25 +549,27 @@ 
             LibMatrix::vec3 c(a.x() + side_width, a.y(), 0);
             LibMatrix::vec3 d(a.x() + side_width, a.y() - side_height, 0);
 
-            std::vector<float> ul(vertex_size_);
-            std::vector<float> ur(vertex_size_);
-            std::vector<float> ll(vertex_size_);
-            std::vector<float> lr(vertex_size_);
-
-            set_attrib(0, a, &ul);
-            set_attrib(0, c, &ur);
-            set_attrib(0, b, &ll);
-            set_attrib(0, d, &lr);
-
-            if (conf_func != 0)
-                conf_func(*this, i, j, n_x, n_y, ul, ur, lr, ll);
-
-            next_vertex(); vertices_.back() = ul;
-            next_vertex(); vertices_.back() = ll;
-            next_vertex(); vertices_.back() = ur;
-            next_vertex(); vertices_.back() = ll;
-            next_vertex(); vertices_.back() = lr;
-            next_vertex(); vertices_.back() = ur;
+            if (!conf_func) {
+                std::vector<float> ul(vertex_size_);
+                std::vector<float> ur(vertex_size_);
+                std::vector<float> ll(vertex_size_);
+                std::vector<float> lr(vertex_size_);
+
+                set_attrib(0, a, &ul);
+                set_attrib(0, c, &ur);
+                set_attrib(0, b, &ll);
+                set_attrib(0, d, &lr);
+
+                next_vertex(); vertices_.back() = ul;
+                next_vertex(); vertices_.back() = ll;
+                next_vertex(); vertices_.back() = ur;
+                next_vertex(); vertices_.back() = ll;
+                next_vertex(); vertices_.back() = lr;
+                next_vertex(); vertices_.back() = ur;
+            }
+            else {
+                conf_func(*this, i, j, n_x, n_y, a, b, c, d);
+            }
         }
     }
 }

=== modified file 'src/mesh.h'
--- src/mesh.h	2011-07-04 10:33:31 +0000
+++ src/mesh.h	2011-10-10 13:55:46 +0000
@@ -44,10 +44,27 @@ 
     void set_attrib(int pos, const LibMatrix::vec3 &v, std::vector<float> *vertex = 0);
     void set_attrib(int pos, const LibMatrix::vec4 &v, std::vector<float> *vertex = 0);
     void next_vertex();
+    std::vector<std::vector<float> >& vertices();
+
+    enum VBOUpdateMethod {
+        VBOUpdateMethodMap,
+        VBOUpdateMethodSubData,
+    };
+    enum VBOUsage {
+        VBOUsageStatic,
+        VBOUsageStream,
+        VBOUsageDynamic,
+    };
+
+    void vbo_update_method(VBOUpdateMethod method);
+    void vbo_usage(VBOUsage usage);
+    void interleave(bool interleave);
 
     void reset();
-    void build_array(bool interleaved = false);
-    void build_vbo(bool interleaved = false);
+    void build_array();
+    void build_vbo();
+    void update_array(const std::vector<std::pair<size_t, size_t> >& ranges);
+    void update_vbo(const std::vector<std::pair<size_t, size_t> >& ranges);
     void delete_array();
     void delete_vbo();
 
@@ -55,10 +72,10 @@ 
     void render_vbo();
 
     typedef void (*grid_configuration_func)(Mesh &mesh, int x, int y, int n_x, int n_y,
-                                            std::vector<float> &upper_left,
-                                            std::vector<float> &upper_right,
-                                            std::vector<float> &lower_right,
-                                            std::vector<float> &lower_left);
+                                            LibMatrix::vec3 &ul,
+                                            LibMatrix::vec3 &ll,
+                                            LibMatrix::vec3 &ur,
+                                            LibMatrix::vec3 &lr);
 
     void make_grid(int n_x, int n_y, double width, double height,
                    double spacing, grid_configuration_func conf_func = 0);
@@ -66,6 +83,10 @@ 
 private:
     bool check_attrib(int pos, int size);
     std::vector<float> &ensure_vertex();
+    void update_single_array(const std::vector<std::pair<size_t, size_t> >& ranges,
+                             size_t n, size_t nfloats, size_t offset);
+    void update_single_vbo(const std::vector<std::pair<size_t, size_t> >& ranges,
+                           size_t n, size_t nfloats);
 
     std::vector<std::pair<int, int> > vertex_format_;
     std::vector<int> attrib_locations_;
@@ -77,6 +98,9 @@ 
     std::vector<GLuint> vbos_;
     std::vector<float *> attrib_data_ptr_;
     int vertex_stride_;
+    bool interleave_;
+    VBOUpdateMethod vbo_update_method_;
+    VBOUsage vbo_usage_;
 };
 
 #endif

=== added file 'src/scene-buffer.cpp'
--- src/scene-buffer.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-buffer.cpp	2011-10-11 08:52:12 +0000
@@ -0,0 +1,451 @@ 
+/*
+ * Copyright © 2010-2011 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Alexandros Frantzis (glmark2)
+ */
+#include "scene.h"
+#include "log.h"
+#include "mat.h"
+#include "stack.h"
+#include "shader-source.h"
+#include "util.h"
+#include "gl-headers.h"
+#include <cmath>
+
+/***********************
+ * Wave implementation *
+ ***********************/
+
+/** 
+ * A callback used to set up the grid by the Wave class.
+ * It is called for each "quad" of the grid.
+ */
+static void
+wave_grid_conf(Mesh &mesh, int x, int y, int n_x, int n_y,
+               LibMatrix::vec3 &ul,
+               LibMatrix::vec3 &ll,
+               LibMatrix::vec3 &ur,
+               LibMatrix::vec3 &lr)
+{
+    (void)x; (void)y; (void)n_x; (void)n_y;
+
+    /* 
+     * Order matters here, so that Wave::vertex_length_index() can work.
+     * Vertices of the triangles at index i that belong to length index i
+     * are even, those that belong to i + 1 are odd.
+     */
+    const LibMatrix::vec3* t[] = {
+        &ll, &ur, &ul, &ur, &ll, &lr
+    };
+
+    for (int i = 0; i < 6; i++) {
+        mesh.next_vertex();
+        /* 
+         * Set the vertex position and the three vertex positions
+         * of the triangle this vertex belongs to.
+         */
+        mesh.set_attrib(0, *t[i]);
+        mesh.set_attrib(1, *t[3 * (i / 3)]);
+        mesh.set_attrib(2, *t[3 * (i / 3) + 1]);
+        mesh.set_attrib(3, *t[3 * (i / 3) + 2]);
+    }
+}
+
+/** 
+ * Renders a grid mesh modulated by a sine wave
+ */
+class WaveMesh
+{
+public:
+    /** 
+     * Creates a wave mesh.
+     * 
+     * @param length the total length of the grid (in model coordinates)
+     * @param width the total width of the grid (in model coordinates)
+     * @param nlength the number of length-wise grid subdivisions
+     * @param nwidth the number of width-wise grid subdivisions
+     * @param wavelength the wave length as a proportion of the length
+     * @param duty_cycle the duty cycle ()
+     */
+    WaveMesh(double length, double width, size_t nlength, size_t nwidth,
+             double wavelength, double duty_cycle) :
+        length_(length), width_(width), nlength_(nlength), nwidth_(nwidth),
+        wave_k_(2 * M_PI / (wavelength * length)),
+        wave_period_(2.0 * M_PI / wave_k_),
+        wave_full_period_(wave_period_ / duty_cycle),
+        wave_velocity_(0.1 * length), displacement_(nlength + 1)
+    {
+        create_program();
+        create_mesh();
+    }
+
+
+    ~WaveMesh() { reset(); }
+
+    /** 
+     * Updates the state of a wave mesh.
+     * 
+     * @param elapsed the time elapsed since the beginning of the rendering
+     */
+    void update(double elapsed)
+    {
+        std::vector<std::vector<float> >& vertices(mesh_.vertices());
+
+        /* Figure out which length index ranges need update */
+        std::vector<std::pair<size_t, size_t> > ranges;
+
+        for (size_t n = 0; n <= nlength_; n++) {
+            double d(displacement(n, elapsed));
+
+            if (d != displacement_[n]) {
+                if (ranges.size() > 0 && ranges.back().second == n - 1) {
+                    ranges.back().second = n;
+                }
+                else {
+                    ranges.push_back(
+                            std::pair<size_t, size_t>(n > 0 ? n - 1 : 0, n)
+                            );
+                }
+            }
+
+            displacement_[n] = d;
+        }
+
+        /* Update the vertex data of the changed ranges */
+        for (std::vector<std::pair<size_t, size_t> >::iterator iter = ranges.begin();
+             iter != ranges.end();
+             iter++)
+        {
+            /* First vertex of length index range */
+            size_t vstart(iter->first * nwidth_ * 6 + (iter->first % 2));
+            /* 
+             * First vertex not included in the range. We should also update all
+             * vertices of triangles touching index i.
+             */
+            size_t vend((iter->second + (iter->second < nlength_)) * nwidth_ * 6);
+
+            for (size_t v = vstart; v < vend; v++) {
+                size_t vt = 3 * (v / 3);
+                vertices[v][0 * 3 + 2] = displacement_[vertex_length_index(v)];
+                vertices[v][1 * 3 + 2] = displacement_[vertex_length_index(vt)];
+                vertices[v][2 * 3 + 2] = displacement_[vertex_length_index(vt + 1)];
+                vertices[v][3 * 3 + 2] = displacement_[vertex_length_index(vt + 2)];
+            }
+
+            /* Update pair with actual vertex range */
+            iter->first = vstart;
+            iter->second = vend - 1;
+        }
+
+        mesh_.update_vbo(ranges);
+    }
+
+    Mesh& mesh() { return mesh_; }
+    Program& program() { return program_; }
+
+    void reset()
+    {
+        program_.stop();
+        program_.release();
+        mesh_.reset();
+    }
+
+private:
+    Mesh mesh_;
+    Program program_;
+    double length_;
+    double width_;
+    size_t nlength_;
+    size_t nwidth_;
+    /* Wave parameters */
+    double wave_k_;
+    double wave_period_;
+    double wave_full_period_;
+    double wave_fill_;
+    double wave_velocity_;
+
+    std::vector<double> displacement_;
+
+    /** 
+     * Calculates the length index of a vertex.
+     */
+    size_t vertex_length_index(size_t v)
+    {
+        return v / (6 * nwidth_) + (v % 2);
+    }
+
+    /** 
+     * The sine wave function with duty-cycle.
+     *
+     * @param x the space coordinate
+     * 
+     * @return the operation error code
+     */
+    double wave_func(double x)
+    {
+        double r(fmod(x, wave_full_period_));
+        if (r < 0)
+            r += wave_full_period_;
+
+        /* 
+         * Return either the sine value or 0.0, depending on the
+         * wave duty cycle.
+         */ 
+        if (r > wave_period_)
+        {
+            return 0;
+        }
+        else
+        {
+            return 0.2 * std::sin(wave_k_ * r);
+        }
+    }
+
+    /** 
+     * Calculates the displacement of the wave.
+     * 
+     * @param n the length index
+     * @param elapsed the time elapsed since the beginning of the rendering
+     * 
+     * @return the displacement at point n at time elapsed
+     */
+    double displacement(size_t n, double elapsed)
+    {
+        double x(n * length_ / nlength_);
+
+        return wave_func(x - wave_velocity_ * elapsed);
+    }
+
+    /** 
+     * Creates the GL shader program.
+     */
+    void create_program()
+    {
+        /* Set up shaders */
+        static const std::string vtx_shader_filename(
+                GLMARK_DATA_PATH"/shaders/buffer-wireframe.vert");
+        static const std::string frg_shader_filename(
+                GLMARK_DATA_PATH"/shaders/buffer-wireframe.frag");
+
+        ShaderSource vtx_source(vtx_shader_filename);
+        ShaderSource frg_source(frg_shader_filename);
+
+        if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
+                                              frg_source.str()))
+        {
+            return;
+        }
+    }
+
+    /** 
+     * Creates the grid mesh.
+     */
+    void create_mesh()
+    {
+        /* 
+         * We need to pass the positions of all vertex of the triangle
+         * in order to draw the wireframe.
+         */
+        std::vector<int> vertex_format;
+        vertex_format.push_back(3);     // Position of vertex
+        vertex_format.push_back(3);     // Position of triangle vertex 0
+        vertex_format.push_back(3);     // Position of triangle vertex 1
+        vertex_format.push_back(3);     // Position of triangle vertex 2
+        mesh_.set_vertex_format(vertex_format);
+
+        std::vector<GLint> attrib_locations;
+        attrib_locations.push_back(program_["position"].location());
+        attrib_locations.push_back(program_["tvertex0"].location());
+        attrib_locations.push_back(program_["tvertex1"].location());
+        attrib_locations.push_back(program_["tvertex2"].location());
+        mesh_.set_attrib_locations(attrib_locations);
+
+        mesh_.make_grid(nlength_, nwidth_, length_, width_,
+                        0.0, wave_grid_conf);
+    }
+
+};
+
+/******************************
+ * SceneBuffer implementation *
+ ******************************/
+
+struct SceneBufferPrivate {
+    WaveMesh *wave;
+    ~SceneBufferPrivate() { delete wave; }
+};
+
+SceneBuffer::SceneBuffer(Canvas &pCanvas) :
+    Scene(pCanvas, "buffer")
+{
+    priv_ = new SceneBufferPrivate();
+    mOptions["interleave"] = Scene::Option("interleave", "false",
+                                           "Whether to interleave vertex attribute data [true,false]");
+    mOptions["update-method"] = Scene::Option("update-method", "map",
+                                              "[map,subdata]");
+    mOptions["update-fraction"] = Scene::Option("update-fraction", "1.0",
+                                                "The fraction of the mesh length that is updated at every iteration (0.0-1.0)");
+    mOptions["update-dispersion"] = Scene::Option("update-dispersion", "0.0",
+                                                  "How dispersed the updates are [0.0 - 1.0]");
+    mOptions["columns"] = Scene::Option("columns", "100",
+                                       "The number of mesh subdivisions length-wise");
+    mOptions["rows"] = Scene::Option("rows", "20",
+                                      "The number of mesh subdisivisions width-wise");
+    mOptions["buffer-usage"] = Scene::Option("buffer-usage", "static",
+                                      "How the buffer will be used [static,stream,dynamic]");
+}
+
+SceneBuffer::~SceneBuffer()
+{
+    delete priv_;
+}
+
+int
+SceneBuffer::load()
+{
+    mRunning = false;
+
+    return 1;
+}
+
+void
+SceneBuffer::unload()
+{
+}
+
+void
+SceneBuffer::setup()
+{
+    using LibMatrix::vec3;
+
+    Scene::setup();
+
+    bool interleave = (mOptions["interleave"].value == "true");
+    Mesh::VBOUpdateMethod update_method;
+    Mesh::VBOUsage usage;
+    double update_fraction;
+    double update_dispersion;
+    size_t nlength;
+    size_t nwidth;
+
+    if (mOptions["update-method"].value == "map")
+        update_method = Mesh::VBOUpdateMethodMap;
+    else if (mOptions["update-method"].value == "subdata")
+        update_method = Mesh::VBOUpdateMethodSubData;
+    else
+        update_method = Mesh::VBOUpdateMethodMap;
+
+    if (mOptions["buffer-usage"].value == "static")
+        usage = Mesh::VBOUsageStatic;
+    else if (mOptions["buffer-usage"].value == "stream")
+        usage = Mesh::VBOUsageStream;
+    else
+        usage = Mesh::VBOUsageDynamic;
+
+    std::stringstream ss;
+    ss << mOptions["update-fraction"].value;
+    ss >> update_fraction;
+    ss.clear();
+    ss << mOptions["update-dispersion"].value;
+    ss >> update_dispersion;
+    ss.clear();
+    ss << mOptions["columns"].value;
+    ss >> nlength;
+    ss.clear();
+    ss << mOptions["rows"].value;
+    ss >> nwidth;
+
+    if (update_method == Mesh::VBOUpdateMethodMap &&
+        (GLExtensions::MapBuffer == 0 || GLExtensions::UnmapBuffer == 0))
+    {
+        Log::error("Requested MapBuffer VBO update method but GL_OES_mapbuffer"
+                   "is not supported!");
+        return;
+    }
+
+    priv_->wave = new WaveMesh(5.0, 2.0, nlength, nwidth,
+                               update_fraction * (1.0 - update_dispersion + 0.0001),
+                               update_fraction);
+
+    priv_->wave->mesh().interleave(interleave);
+    priv_->wave->mesh().vbo_update_method(update_method);
+    priv_->wave->mesh().vbo_usage(usage);
+    priv_->wave->mesh().build_vbo();
+
+    priv_->wave->program().start();
+    priv_->wave->program()["Viewport"] = LibMatrix::vec2(mCanvas.width(), mCanvas.height());
+
+    glDisable(GL_CULL_FACE);
+
+    mCurrentFrame = 0;
+    mRunning = true;
+    mStartTime = Scene::get_timestamp_us() / 1000000.0;
+    mLastUpdateTime = mStartTime;
+}
+
+void
+SceneBuffer::teardown()
+{
+    delete priv_->wave;
+    priv_->wave = 0;
+
+    glEnable(GL_CULL_FACE);
+
+    Scene::teardown();
+}
+
+void
+SceneBuffer::update()
+{
+    double current_time = Scene::get_timestamp_us() / 1000000.0;
+    double elapsed_time = current_time - mStartTime;
+
+    mLastUpdateTime = current_time;
+
+    if (elapsed_time >= mDuration) {
+        mAverageFPS = mCurrentFrame / elapsed_time;
+        mRunning = false;
+    }
+
+    priv_->wave->update(elapsed_time);
+
+    mCurrentFrame++;
+}
+
+void
+SceneBuffer::draw()
+{
+    LibMatrix::Stack4 model_view;
+
+    // Load the ModelViewProjectionMatrix uniform in the shader
+    LibMatrix::mat4 model_view_proj(mCanvas.projection());
+    model_view.translate(0.0, 0.0, -4.0);
+    model_view.rotate(45.0, -1.0, 0.0, 0.0);
+    model_view_proj *= model_view.getCurrent();
+
+    priv_->wave->program()["ModelViewProjectionMatrix"] = model_view_proj;
+
+    priv_->wave->mesh().render_vbo();
+}
+
+Scene::ValidationResult
+SceneBuffer::validate()
+{
+    return Scene::ValidationUnknown;
+}

=== modified file 'src/scene-build.cpp'
--- src/scene-build.cpp	2011-09-21 09:52:37 +0000
+++ src/scene-build.cpp	2011-10-05 16:42:33 +0000
@@ -118,10 +118,13 @@ 
     mUseVbo = (mOptions["use-vbo"].value == "true");
     bool interleave = (mOptions["interleave"].value == "true");
 
+    mMesh.vbo_update_method(Mesh::VBOUpdateMethodMap);
+    mMesh.interleave(interleave);
+
     if (mUseVbo)
-        mMesh.build_vbo(interleave);
+        mMesh.build_vbo();
     else
-        mMesh.build_array(interleave);
+        mMesh.build_array();
 
     /* Calculate a projection matrix that is a good fit for the model */
     vec3 maxVec = model.maxVec();
@@ -194,10 +197,16 @@ 
     normal_matrix.inverse().transpose();
     mProgram["NormalMatrix"] = normal_matrix;
 
+    std::vector<std::pair<size_t,size_t> > ranges;
+    ranges.push_back(std::pair<size_t,size_t>(2,
+                                              0 * mMesh.vertices().size()/3 + 3));
+
     if (mUseVbo) {
+        mMesh.update_vbo(ranges);
         mMesh.render_vbo();
     }
     else {
+        mMesh.update_array(ranges);
         mMesh.render_array();
     }
 }

=== modified file 'src/scene.h'
--- src/scene.h	2011-09-20 09:45:24 +0000
+++ src/scene.h	2011-10-08 12:30:54 +0000
@@ -353,4 +353,24 @@ 
 private:
     SceneDesktopPrivate *priv_;
 };
+
+struct SceneBufferPrivate;
+
+class SceneBuffer : public Scene
+{
+public:
+    SceneBuffer(Canvas &canvas);
+    int load();
+    void unload();
+    void setup();
+    void teardown();
+    void update();
+    void draw();
+    ValidationResult validate();
+
+    ~SceneBuffer();
+
+private:
+    SceneBufferPrivate *priv_;
+};
 #endif