=== added file 'data/shaders/text-renderer.frag'
--- data/shaders/text-renderer.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/text-renderer.frag	2011-12-13 14:25:41 +0000
@@ -0,0 +1,10 @@
+uniform sampler2D Texture0;
+
+varying vec2 TextureCoord;
+
+void main(void)
+{
+    vec4 texel = texture2D(Texture0, TextureCoord);
+    gl_FragColor = texel;
+}
+

=== added file 'data/shaders/text-renderer.vert'
--- data/shaders/text-renderer.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/text-renderer.vert	2011-12-13 14:25:41 +0000
@@ -0,0 +1,10 @@
+attribute vec2 position;
+attribute vec2 texcoord;
+
+varying vec2 TextureCoord;
+
+void main(void)
+{
+    TextureCoord = texcoord;
+    gl_Position = vec4(position, 0.0, 1.0);
+}

=== added file 'data/textures/glyph-atlas.png'
Binary files data/textures/glyph-atlas.png	1970-01-01 00:00:00 +0000 and data/textures/glyph-atlas.png	2011-12-13 14:25:41 +0000 differ
=== modified file 'doc/glmark2.1.in'
--- doc/glmark2.1.in	2011-12-15 13:52:04 +0000
+++ doc/glmark2.1.in	2012-01-05 09:46:41 +0000
@@ -36,6 +36,10 @@
 Show all scene option values used for benchmarks
 (only explicitly set options are shown by default)
 .TP
+\fB\-\-show-fps\fR
+Show live FPS count on screen (showing live FPS
+affects benchmarking results, use with care!)
+.TP
 \fB\-d\fR, \fB\-\-debug\fR
 Display debug messages
 .TP

=== modified file 'src/main.cpp'
--- src/main.cpp	2011-11-11 11:07:15 +0000
+++ src/main.cpp	2011-12-13 14:27:23 +0000
@@ -28,6 +28,7 @@
 #include "log.h"
 #include "util.h"
 #include "default-benchmarks.h"
+#include "text-renderer.h"
 
 #include <iostream>
 #include <fstream>
@@ -149,6 +150,8 @@
 void
 do_benchmark(Canvas &canvas, vector<Benchmark *> &benchmarks)
 {
+    static const unsigned int fps_interval = 500000;
+    TextRenderer fps_renderer(canvas);
     unsigned score = 0;
     unsigned int benchmarks_run = 0;
 
@@ -158,6 +161,7 @@
     {
         bool keep_running = true;
         Benchmark *bench = *bench_iter;
+        uint64_t fps_timestamp = Util::get_timestamp_us();
         Scene &scene = bench->setup_scene();
 
         if (!scene.name().empty()) {
@@ -172,6 +176,17 @@
                 scene.draw();
                 scene.update();
 
+                if (Options::show_fps) {
+                    uint64_t now = Util::get_timestamp_us();
+                    if (now - fps_timestamp >= fps_interval) {
+                        std::stringstream ss;
+                        ss << "FPS: " << scene.average_fps();
+                        fps_renderer.text(ss.str());
+                        fps_timestamp = now;
+                    }
+                    fps_renderer.render();
+                }
+
                 canvas.update();
             }
 

=== modified file 'src/options.cpp'
--- src/options.cpp	2011-11-01 16:46:08 +0000
+++ src/options.cpp	2011-12-13 14:27:23 +0000
@@ -39,6 +39,7 @@
 bool Options::list_scenes = false;
 bool Options::show_all_options = false;
 bool Options::show_debug = false;
+bool Options::show_fps = false;
 bool Options::show_help = false;
 
 static struct option long_options[] = {
@@ -49,6 +50,7 @@
     {"size", 1, 0, 0},
     {"list-scenes", 0, 0, 0},
     {"show-all-options", 0, 0, 0},
+    {"show-fps", 0, 0, 0},
     {"debug", 0, 0, 0},
     {"help", 0, 0, 0},
     {0, 0, 0, 0}
@@ -98,6 +100,8 @@
            "                         and their options\n"
            "      --show-all-options Show all scene option values used for benchmarks\n"
            "                         (only explicitly set options are shown by default)\n"
+           "      --show-fps         Show live FPS count on screen (showing live FPS\n"
+           "                         affects benchmarking results, use with care!)\n"
            "  -d, --debug            Display debug messages\n"
            "  -h, --help             Display help\n");
 }
@@ -134,6 +138,8 @@
             Options::list_scenes = true;
         else if (!strcmp(optname, "show-all-options"))
             Options::show_all_options = true;
+        else if (!strcmp(optname, "show-fps"))
+            Options::show_fps = true;
         else if (c == 'd' || !strcmp(optname, "debug"))
             Options::show_debug = true;
         else if (c == 'h' || !strcmp(optname, "help"))

=== modified file 'src/options.h'
--- src/options.h	2011-09-20 12:52:21 +0000
+++ src/options.h	2011-12-13 14:27:23 +0000
@@ -39,6 +39,7 @@
     static bool list_scenes;
     static bool show_all_options;
     static bool show_debug;
+    static bool show_fps;
     static bool show_help;
 };
 

=== added file 'src/text-renderer.cpp'
--- src/text-renderer.cpp	1970-01-01 00:00:00 +0000
+++ src/text-renderer.cpp	2012-01-05 09:35:49 +0000
@@ -0,0 +1,291 @@
+/*
+ * Copyright © 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 "text-renderer.h"
+#include "gl-headers.h"
+#include "scene.h"
+#include "shader-source.h"
+#include "vec.h"
+#include "mat.h"
+#include "texture.h"
+
+using LibMatrix::vec2;
+using LibMatrix::mat4;
+
+/* These are specific to the glyph texture atlas we are using */
+static const unsigned int texture_size(512);
+static const vec2 glyph_size_pixels(29.0, 57.0);
+static const vec2 glyph_size(glyph_size_pixels/texture_size);
+
+/******************
+ * Public methods *
+ ******************/
+
+/**
+ * TextRenderer default constructor.
+ */
+TextRenderer::TextRenderer(Canvas& canvas) :
+    canvas_(canvas), dirty_(false), position_(-1.0, -1.0),
+    texture_(0)
+{
+    size(0.03);
+
+    glGenBuffers(2, vbo_);
+    ShaderSource vtx_source(GLMARK_DATA_PATH"/shaders/text-renderer.vert");
+    ShaderSource frg_source(GLMARK_DATA_PATH"/shaders/text-renderer.frag");
+
+    if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
+                                          frg_source.str()))
+    {
+        return;
+    }
+
+    GLint prev_program;
+    glGetIntegerv(GL_CURRENT_PROGRAM, &prev_program);
+
+    program_.start();
+    program_["Texture0"] = 0;
+
+    glUseProgram(prev_program);
+
+    /* Load the glyph texture atlas */
+    Texture::load(GLMARK_DATA_PATH"/textures/glyph-atlas.png", &texture_,
+                  GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR,0);
+}
+
+TextRenderer::~TextRenderer()
+{
+    glDeleteBuffers(2, vbo_);
+    glDeleteTextures(1, &texture_);
+}
+
+/**
+ * Sets the text string to render.
+ *
+ * @param t the text string
+ */
+void
+TextRenderer::text(const std::string& t)
+{
+    if (text_ != t) {
+        text_ = t;
+        dirty_ = true;
+    }
+}
+
+/**
+ * Sets the screen position to render at.
+ *
+ * @param t the position
+ */
+void
+TextRenderer::position(LibMatrix::vec2& p)
+{
+    if (position_ != p) {
+        position_ = p;
+        dirty_ = true;
+    }
+}
+
+/**
+ * Sets the size of each rendered glyph.
+ *
+ * The size corresponds to the width of each glyph
+ * in normalized screen coordinates.
+ *
+ * @param s the size of each glyph
+ */
+void
+TextRenderer::size(float s)
+{
+    if (size_.x() != s) {
+        /* Take into account the glyph and canvas aspect ratio */
+        double canvas_aspect =
+            static_cast<double>(canvas_.width()) / canvas_.height();
+        double glyph_aspect_rev = glyph_size.y() / glyph_size.x();
+        size_ = vec2(s, s * canvas_aspect * glyph_aspect_rev);
+        dirty_ = true;
+    }
+}
+
+/**
+ * Renders the text.
+ */
+void
+TextRenderer::render()
+{
+    /* Save state */
+    GLint prev_program = 0;
+    GLint prev_array_buffer = 0;
+    GLint prev_elem_array_buffer = 0;
+    GLint prev_blend_src_rgb = 0;
+    GLint prev_blend_dst_rgb = 0;
+    GLint prev_blend_src_alpha = 0;
+    GLint prev_blend_dst_alpha = 0;
+    GLboolean prev_blend = GL_FALSE;
+    GLboolean prev_depth_test = GL_FALSE;
+    glGetIntegerv(GL_CURRENT_PROGRAM, &prev_program);
+    glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &prev_array_buffer);
+    glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &prev_elem_array_buffer);
+    glGetIntegerv(GL_BLEND_SRC_RGB, &prev_blend_src_rgb);
+    glGetIntegerv(GL_BLEND_DST_RGB, &prev_blend_dst_rgb);
+    glGetIntegerv(GL_BLEND_SRC_ALPHA, &prev_blend_src_alpha);
+    glGetIntegerv(GL_BLEND_DST_ALPHA, &prev_blend_dst_alpha);
+    glGetBooleanv(GL_BLEND, &prev_blend);
+    glGetBooleanv(GL_DEPTH_TEST, &prev_depth_test);
+
+    /* Set new state */
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, texture_);
+    glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_[1]);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glDisable(GL_DEPTH_TEST);
+
+    if (dirty_) {
+        create_geometry();
+        dirty_ = false;
+    }
+
+    program_.start();
+    GLint position_loc = program_["position"].location();
+    GLint texcoord_loc = program_["texcoord"].location();
+
+    /* Render */
+    glEnableVertexAttribArray(position_loc);
+    glEnableVertexAttribArray(texcoord_loc);
+    glVertexAttribPointer(position_loc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
+    glVertexAttribPointer(texcoord_loc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
+                          reinterpret_cast<const GLvoid *>(2 * sizeof(float)));
+
+    glDrawElements(GL_TRIANGLES, 6 * text_.length(), GL_UNSIGNED_SHORT, 0);
+
+    glDisableVertexAttribArray(texcoord_loc);
+    glDisableVertexAttribArray(position_loc);
+
+    /* Restore state */
+    if (prev_depth_test == GL_TRUE)
+        glEnable(GL_DEPTH_TEST);
+    if (prev_blend == GL_FALSE)
+        glDisable(GL_BLEND);
+    glBlendFuncSeparate(prev_blend_src_rgb, prev_blend_dst_rgb,
+                        prev_blend_src_alpha, prev_blend_dst_alpha);
+    glBindBuffer(GL_ARRAY_BUFFER, prev_array_buffer);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prev_elem_array_buffer);
+    glUseProgram(prev_program);
+}
+
+/*******************
+ * Private methods *
+ *******************/
+
+/**
+ * Creates the geometry needed to render the text.
+ *
+ * This method assumes that the text VBOs are properly bound.
+ */
+void
+TextRenderer::create_geometry()
+{
+    std::vector<float> array;
+    std::vector<GLushort> elem_array;
+    vec2 pos(position_);
+
+    for (size_t i = 0; i < text_.size(); i++) {
+        vec2 texcoord = get_glyph_coords(text_[i]);
+
+        /* Emit the elements for this glyph quad */
+        /* Lower left */
+        array.push_back(pos.x());
+        array.push_back(pos.y());
+        array.push_back(texcoord.x());
+        array.push_back(texcoord.y());
+
+        /* Lower right */
+        pos.x(pos.x() + size_.x());
+        texcoord.x(texcoord.x() + glyph_size.x());
+        array.push_back(pos.x());
+        array.push_back(pos.y());
+        array.push_back(texcoord.x());
+        array.push_back(texcoord.y());
+
+        /* Upper left */
+        pos.x(pos.x() - size_.x());
+        pos.y(pos.y() + size_.y());
+        texcoord.x(texcoord.x() - glyph_size.x());
+        texcoord.y(texcoord.y() + glyph_size.y());
+        array.push_back(pos.x());
+        array.push_back(pos.y());
+        array.push_back(texcoord.x());
+        array.push_back(texcoord.y());
+
+        /* Upper right */
+        pos.x(pos.x() + size_.x());
+        texcoord.x(texcoord.x() + glyph_size.x());
+        array.push_back(pos.x());
+        array.push_back(pos.y());
+        array.push_back(texcoord.x());
+        array.push_back(texcoord.y());
+
+        /* Prepare for the next glyph */
+        pos.y(pos.y() - size_.y());
+
+        /* Emit the element indices for this glyph quad */
+        elem_array.push_back(4 * i);
+        elem_array.push_back(4 * i + 1);
+        elem_array.push_back(4 * i + 2);
+        elem_array.push_back(4 * i + 2);
+        elem_array.push_back(4 * i + 1);
+        elem_array.push_back(4 * i + 3);
+    }
+
+    /* Load the data into the corresponding VBOs */
+    glBufferData(GL_ARRAY_BUFFER, array.size() * sizeof(float),
+                 &array[0], GL_DYNAMIC_DRAW);
+
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, elem_array.size() * sizeof(GLushort),
+                 &elem_array[0], GL_DYNAMIC_DRAW);
+}
+
+/**
+ * Gets the texcoords of a glyph in the glyph texture atlas.
+ *
+ * @param c the character to get the glyph texcoords of
+ *
+ * @return the texcoords
+ */
+vec2
+TextRenderer::get_glyph_coords(char c)
+{
+    static const unsigned int glyphs_per_row(texture_size / glyph_size_pixels.x());
+
+    /* We only support the ASCII printable characters */
+    if (c < 32 || c >= 127)
+        c = 32;
+
+    int n = c - 32;
+    int row = n / glyphs_per_row;
+    int col = n % glyphs_per_row;
+
+    return vec2(col * glyph_size.x(), 1.0 - (row + 1) * glyph_size.y());
+}
+

=== added file 'src/text-renderer.h'
--- src/text-renderer.h	1970-01-01 00:00:00 +0000
+++ src/text-renderer.h	2011-12-13 14:25:41 +0000
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 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)
+ */
+#ifndef GLMARK2_TEXT_RENDERER_H_
+#define GLMARK2_TEXT_RENDERER_H_
+
+#include <string>
+#include "gl-headers.h"
+#include "vec.h"
+#include "program.h"
+#include "canvas.h"
+
+/**
+ * Renders text using OpenGL textures.
+ */
+class TextRenderer
+{
+public:
+    TextRenderer(Canvas& canvas);
+    ~TextRenderer();
+
+    void text(const std::string& t);
+    void position(LibMatrix::vec2& p);
+    void size(float s);
+
+    void render();
+
+private:
+    void create_geometry();
+    LibMatrix::vec2 get_glyph_coords(char c);
+
+    Canvas& canvas_;
+    bool dirty_;
+    std::string text_;
+    LibMatrix::vec2 position_;
+    LibMatrix::vec2 size_;
+    Program program_;
+    GLuint vbo_[2];
+    GLuint texture_;
+};
+
+#endif

