=== 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

