From patchwork Tue Oct 11 09:58:12 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: alexandros.frantzis@linaro.org X-Patchwork-Id: 4613 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id ECE4023EF7 for ; Tue, 11 Oct 2011 09:58:16 +0000 (UTC) Received: from mail-ey0-f180.google.com (mail-ey0-f180.google.com [209.85.215.180]) by fiordland.canonical.com (Postfix) with ESMTP id CD7E1A18321 for ; Tue, 11 Oct 2011 09:58:16 +0000 (UTC) Received: by eyg5 with SMTP id 5so1917640eyg.11 for ; Tue, 11 Oct 2011 02:58:16 -0700 (PDT) Received: by 10.223.77.71 with SMTP id f7mr16735268fak.33.1318327096569; Tue, 11 Oct 2011 02:58:16 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.152.24.41 with SMTP id r9cs152055laf; Tue, 11 Oct 2011 02:58:14 -0700 (PDT) Received: by 10.227.32.133 with SMTP id c5mr7910431wbd.4.1318327093232; Tue, 11 Oct 2011 02:58:13 -0700 (PDT) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id fm3si15721343wbb.120.2011.10.11.02.58.12 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 11 Oct 2011 02:58:13 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) client-ip=91.189.90.7; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) smtp.mail=bounces@canonical.com Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1RDZ6C-0008GC-I2 for ; Tue, 11 Oct 2011 09:58:12 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id 777F0E01FB for ; Tue, 11 Oct 2011 09:58:12 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: glmark2 X-Launchpad-Branch: ~glmark2-dev/glmark2/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 149 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [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> Date: Tue, 11 Oct 2011 09:58:12 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="14124"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: 6f42d71ae3ccf3633af408a55ec57c3798ab5bd8 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 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 === 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 #include @@ -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(glGetString(GL_EXTENSIONS)); + if (exts) { + extString = exts; + } + + if (extString.find("GL_OES_mapbuffer") != std::string::npos) { + GLExtensions::MapBuffer = + reinterpret_cast(eglGetProcAddress("glMapBufferOES")); + GLExtensions::UnmapBuffer = + reinterpret_cast(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(glGetString(GL_EXTENSIONS)); + if (exts) { + extString = exts; + } + + if (extString.find("GL_OES_mapbuffer") != std::string::npos) { + GLExtensions::MapBuffer = + reinterpret_cast(eglGetProcAddress("glMapBufferOES")); + GLExtensions::UnmapBuffer = + reinterpret_cast(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 . + * + * 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 #include #elif USE_GLESv2 #include #include -#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::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(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 >& +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 >::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 >::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 >& ranges, + size_t n, size_t nfloats, size_t offset) +{ + float *array(vertex_arrays_[n]); + + /* Update supplied ranges */ + for (std::vector >::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 >& 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 >& 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( + GLExtensions::MapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY) + ); + } + + /* Update supplied ranges */ + for (std::vector >::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 >& 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 ul(vertex_size_); - std::vector ur(vertex_size_); - std::vector ll(vertex_size_); - std::vector 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 ul(vertex_size_); + std::vector ur(vertex_size_); + std::vector ll(vertex_size_); + std::vector 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 *vertex = 0); void set_attrib(int pos, const LibMatrix::vec4 &v, std::vector *vertex = 0); void next_vertex(); + std::vector >& 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 >& ranges); + void update_vbo(const std::vector >& 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 &upper_left, - std::vector &upper_right, - std::vector &lower_right, - std::vector &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 &ensure_vertex(); + void update_single_array(const std::vector >& ranges, + size_t n, size_t nfloats, size_t offset); + void update_single_vbo(const std::vector >& ranges, + size_t n, size_t nfloats); std::vector > vertex_format_; std::vector attrib_locations_; @@ -77,6 +98,9 @@ std::vector vbos_; std::vector 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 . + * + * 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 + +/*********************** + * 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 >& vertices(mesh_.vertices()); + + /* Figure out which length index ranges need update */ + std::vector > 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(n > 0 ? n - 1 : 0, n) + ); + } + } + + displacement_[n] = d; + } + + /* Update the vertex data of the changed ranges */ + for (std::vector >::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 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 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 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 > ranges; + ranges.push_back(std::pair(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