=== added file 'data/shaders/buffer-wireframe.frag'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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