diff mbox

[Branch,~glcompbench-dev/glcompbench/trunk] Rev 78: Merging lp:~glcompbench-dev/glcompbench/blur into trunk.

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

Commit Message

Jesse Barker March 20, 2012, 4:35 p.m. UTC
Merge authors:
  Jesse Barker (jesse-barker)
Related merge proposals:
  https://code.launchpad.net/~glcompbench-dev/glcompbench/blur/+merge/98238
  proposed by: Jesse Barker (jesse-barker)
------------------------------------------------------------
revno: 78 [merge]
committer: Jesse Barker <jesse.barker@linaro.org>
branch nick: trunk
timestamp: Tue 2012-03-20 09:32:54 -0700
message:
  Merging lp:~glcompbench-dev/glcompbench/blur into trunk.
  
  This adds a new blur effect test, as well as some additional objects and new
  internal API changes to support it.
added:
  data/background.png
  data/desktop-blur.frag
  data/desktop.frag
  data/desktop.vert
  data/window.png
  src/composite-test-simple-blur.cc
  src/render-object.cc
  src/render-object.h
  src/texture.cc
  src/texture.h
modified:
  src/benchmark.cc
  src/benchmark.h
  src/composite-canvas.cc
  src/composite-test-default-options.cc
  src/composite-test-pixman.cc
  src/composite-test-simple-base.cc
  src/composite-test-simple-brick.cc
  src/composite-test-xrender.cc
  src/composite-test.h
  src/glcompbench.cc
  src/wscript_build
  wscript


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

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

Patch

=== added file 'data/background.png'
Binary files data/background.png	1970-01-01 00:00:00 +0000 and data/background.png	2012-03-01 23:28:36 +0000 differ
=== added file 'data/desktop-blur.frag'
--- data/desktop-blur.frag	1970-01-01 00:00:00 +0000
+++ data/desktop-blur.frag	2012-02-02 22:12:48 +0000
@@ -0,0 +1,13 @@ 
+uniform sampler2D Texture0;
+
+varying vec2 TextureCoord;
+
+void main(void)
+{
+    vec4 result;
+
+    $CONVOLUTION$
+
+    gl_FragColor = result;
+}
+

=== added file 'data/desktop.frag'
--- data/desktop.frag	1970-01-01 00:00:00 +0000
+++ data/desktop.frag	2012-03-08 12:05:59 +0000
@@ -0,0 +1,10 @@ 
+uniform sampler2D MaterialTexture0;
+
+varying vec2 TextureCoord;
+
+void main(void)
+{
+    vec4 texel = texture2D(MaterialTexture0, TextureCoord);
+    gl_FragColor = vec4(texel.xyz, 0.5);
+}
+

=== added file 'data/desktop.vert'
--- data/desktop.vert	1970-01-01 00:00:00 +0000
+++ data/desktop.vert	2012-02-02 22:12:48 +0000
@@ -0,0 +1,11 @@ 
+attribute vec2 position;
+attribute vec2 texcoord;
+
+varying vec2 TextureCoord;
+
+void main(void)
+{
+    gl_Position = vec4(position, 0.0, 1.0);
+
+    TextureCoord = texcoord;
+}

=== added file 'data/window.png'
Binary files data/window.png	1970-01-01 00:00:00 +0000 and data/window.png	2012-03-01 23:28:36 +0000 differ
=== modified file 'src/benchmark.cc'
--- src/benchmark.cc	2011-06-22 11:35:50 +0000
+++ src/benchmark.cc	2012-03-09 03:58:54 +0000
@@ -28,6 +28,7 @@ 
 using std::string;
 using std::vector;
 using std::map;
+using std::list;
 
 std::map<string, CompositeTest *> Benchmark::test_map_;
 
@@ -113,12 +114,12 @@ 
 }
 
 CompositeTest &
-Benchmark::setup_test()
+Benchmark::setup_test(list<CompositeWindow*> &window_list)
 {
     test_.reset_options();
     load_options();
 
-    test_.prepare_for_run();
+    test_.prepare_for_run(window_list);
 
     return test_;
 }

=== modified file 'src/benchmark.h'
--- src/benchmark.h	2011-07-15 10:20:09 +0000
+++ src/benchmark.h	2012-03-09 03:58:54 +0000
@@ -40,7 +40,7 @@ 
     // test[:opt1=val1:opt2=val2...]
     Benchmark(const std::string &s);
 
-    CompositeTest &setup_test();
+    CompositeTest &setup_test(std::list<CompositeWindow*> &window_list);
     void teardown_test();
 
     CompositeTest &get_test() { return test_; }

=== modified file 'src/composite-canvas.cc'
--- src/composite-canvas.cc	2011-09-21 13:57:01 +0000
+++ src/composite-canvas.cc	2012-03-09 03:58:54 +0000
@@ -633,7 +633,7 @@ 
          benchIt++)
     {
         Benchmark *benchmark = *benchIt;
-        current_test_ = &benchmark->setup_test();
+        current_test_ = &benchmark->setup_test(this->window_list_);
 
         if (!current_test_->name().empty()) {
             load_current_test_options();

=== modified file 'src/composite-test-default-options.cc'
--- src/composite-test-default-options.cc	2011-06-23 13:52:16 +0000
+++ src/composite-test-default-options.cc	2012-03-09 03:58:54 +0000
@@ -29,8 +29,10 @@ 
  * Prepares the test for a test run.
  */
 void
-CompositeTestDefaultOptions::prepare_for_run()
+CompositeTestDefaultOptions::prepare_for_run(std::list<CompositeWindow*> &window_list)
 {
+    CompositeTest::prepare_for_run(window_list);
+
     const std::map<std::string, CompositeTest *> &tests = Benchmark::tests();
 
     for (std::list<std::pair<std::string, std::string> >::const_iterator iter = default_options_.begin();

=== modified file 'src/composite-test-pixman.cc'
--- src/composite-test-pixman.cc	2011-07-19 13:54:14 +0000
+++ src/composite-test-pixman.cc	2012-03-09 03:58:54 +0000
@@ -49,8 +49,10 @@ 
 }
 
 void
-CompositeTestPixman::prepare_for_run()
+CompositeTestPixman::prepare_for_run(std::list<CompositeWindow*> &window_list)
 {
+    CompositeTest::prepare_for_run(window_list);
+
     if (options_["filter"].value == "bilinear")
         pixman_filter_ = PIXMAN_FILTER_BILINEAR;
     else

=== modified file 'src/composite-test-simple-base.cc'
--- src/composite-test-simple-base.cc	2011-12-05 19:01:24 +0000
+++ src/composite-test-simple-base.cc	2012-03-09 03:58:54 +0000
@@ -138,8 +138,10 @@ 
  * Prepares the test for a test run.
  */
 void
-CompositeTestSimpleBase::prepare_for_run()
+CompositeTestSimpleBase::prepare_for_run(std::list<CompositeWindow*> &window_list)
 {
+    CompositeTest::prepare_for_run(window_list);
+
     vboData_.useBufferObject(options_["use-vbo"].value == "true");
 
     program_.start();

=== added file 'src/composite-test-simple-blur.cc'
--- src/composite-test-simple-blur.cc	1970-01-01 00:00:00 +0000
+++ src/composite-test-simple-blur.cc	2012-03-20 16:24:50 +0000
@@ -0,0 +1,474 @@ 
+/*
+ * Copyright © 2011 Linaro Limited
+ *
+ * This file is part of glcompbench.
+ *
+ * glcompbench 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.
+ *
+ * glcompbench 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 glcompbench.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ *  Jesse Barker <jesse.barker@linaro.org>
+ */
+
+#include <sstream>
+#include <cmath>
+#include "gl-headers.h"
+#include "composite-test.h"
+#include "options.h"
+#include "log.h"
+#include "util.h"
+#include "render-object.h"
+#include "texture.h"
+#include "composite-window-ximage.h"
+
+using std::string;
+using std::map;
+using std::list;
+using LibMatrix::vec2;
+
+//
+// A RenderObject that blurs the target it is drawn to.
+//
+class RenderWindowBlur : public RenderObject
+{
+public:
+    RenderWindowBlur(unsigned int passes, unsigned int radius, bool separable) :
+        RenderObject(),
+        passes_(passes),
+        radius_(radius),
+        separable_(separable),
+        window_contents_() {}
+
+    virtual void init(Program& program)
+    {
+        RenderObject::init(program);
+        window_contents_.init(program);
+    }
+
+    virtual void set_background(unsigned int background_texture)
+    {
+        window_contents_.set_background(background_texture);
+    }
+
+    void refresh_window(unsigned int background_texture)
+    {
+        glClearColor(0.0, 0.0, 0.0, 0.0);
+        window_contents_.set_background(background_texture);
+        window_contents_.clear();
+    }
+
+    virtual void release()
+    {
+        window_contents_.release();
+        RenderObject::release();
+    }
+
+    virtual void resize(const vec2& size)
+    {
+        RenderObject::resize(size);
+        window_contents_.resize(size);
+    }
+
+    virtual void render_to(RenderObject& target)
+    {
+        if (separable_) {
+            Program& blur_program_h1 = blur_program_h(target.size().x());
+            Program& blur_program_v1 = blur_program_v(target.size().y());
+
+            for (unsigned int i = 0; i < passes_; i++) {
+                render_from(target, blur_program_h1);
+                RenderObject::render_to(target, blur_program_v1);
+            }
+        }
+        else {
+            Program& blur_program1 = blur_program(target.size().x(), target.size().y());
+
+            for (unsigned int i = 0; i < passes_; i++) {
+                if (i % 2 == 0)
+                    render_from(target, blur_program1);
+                else
+                    RenderObject::render_to(target, blur_program1);
+            }
+
+            if (passes_ % 2 == 1)
+                RenderObject::render_to(target);
+        }
+
+        // Blend the window contents with the target texture.
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        window_contents_.position(position());
+        window_contents_.render_to(target);
+        glDisable(GL_BLEND);
+    }
+
+private:
+    enum BlurDirection {
+        BlurDirectionHorizontal,
+        BlurDirectionVertical,
+        BlurDirectionBoth
+    };
+
+    void render_from(RenderObject& target, Program& program)
+    {
+        vec2 final_pos(pos_ + size_);
+        vec2 ll_tex(target.normalize_texcoord(pos_));
+        vec2 ur_tex(target.normalize_texcoord(final_pos));
+
+        static const GLfloat position_blur[2 * 4] = {
+            -1.0, -1.0,
+             1.0, -1.0,
+            -1.0,  1.0,
+             1.0,  1.0,
+        };
+        GLfloat texcoord_blur[2 * 4] = {
+            ll_tex.x(), ll_tex.y(),
+            ur_tex.x(), ll_tex.y(),
+            ll_tex.x(), ur_tex.y(),
+            ur_tex.x(), ur_tex.y(),
+        };
+
+        make_current();
+        glBindTexture(GL_TEXTURE_2D, target.texture());
+        draw_quad_with_program(position_blur, texcoord_blur, program);
+    }
+
+    Program& blur_program(unsigned int w, unsigned int h)
+    {
+        /*
+         * If the size of the window has changed we must recreate
+         * the shader to contain the correct texture step values.
+         */
+        if (blur_program_dim_.x() != w || blur_program_dim_.y() != h ||
+            !blur_program_.ready())
+        {
+            blur_program_dim_.x(w);
+            blur_program_dim_.y(h);
+
+            blur_program_.release();
+
+            ShaderSource vtx_source;
+            ShaderSource frg_source;
+            create_blur_shaders(vtx_source, frg_source, radius_,
+                                radius_ / 3.0, BlurDirectionBoth);
+            frg_source.add_const("TextureStepX", 1.0 / w);
+            frg_source.add_const("TextureStepY", 1.0 / h);
+            load_shaders_from_strings(blur_program_, vtx_source.str(),
+                                      frg_source.str());
+        }
+
+        return blur_program_;
+    }
+
+    Program& blur_program_h(unsigned int w)
+    {
+        /*
+         * If the size of the window has changed we must recreate
+         * the shader to contain the correct texture step values.
+         */
+        if (blur_program_dim_.x() != w ||
+            !blur_program_h_.ready())
+        {
+            blur_program_dim_.x(w);
+
+            blur_program_h_.release();
+
+            ShaderSource vtx_source;
+            ShaderSource frg_source;
+            create_blur_shaders(vtx_source, frg_source, radius_,
+                                radius_ / 3.0, BlurDirectionHorizontal);
+            frg_source.add_const("TextureStepX", 1.0 / w);
+            load_shaders_from_strings(blur_program_h_, vtx_source.str(),
+                                      frg_source.str());
+        }
+
+        return blur_program_h_;
+    }
+
+    Program& blur_program_v(unsigned int h)
+    {
+        /*
+         * If the size of the window has changed we must recreate
+         * the shader to contain the correct texture step values.
+         */
+        if (blur_program_dim_.y() != h ||
+            !blur_program_v_.ready())
+        {
+            blur_program_dim_.y(h);
+
+            blur_program_v_.release();
+
+            ShaderSource vtx_source;
+            ShaderSource frg_source;
+            create_blur_shaders(vtx_source, frg_source, radius_,
+                                radius_ / 3.0, BlurDirectionVertical);
+            frg_source.add_const("TextureStepY", 1.0 / h);
+            load_shaders_from_strings(blur_program_v_, vtx_source.str(),
+                                      frg_source.str());
+        }
+
+        return blur_program_v_;
+    }
+
+    static void
+    create_blur_shaders(ShaderSource& vtx_source, ShaderSource& frg_source,
+                        unsigned int radius, float sigma, BlurDirection direction);
+    LibMatrix::uvec2 blur_program_dim_;
+    Program blur_program_;
+    Program blur_program_h_;
+    Program blur_program_v_;
+    unsigned int passes_;
+    unsigned int radius_;
+    bool separable_;
+    RenderClearImage window_contents_;
+};
+
+void
+RenderWindowBlur::create_blur_shaders(ShaderSource& vtx_source,
+    ShaderSource& frg_source, unsigned int radius, float sigma,
+    BlurDirection direction)
+{
+    vtx_source.append_file(GLCOMPBENCH_DATA_PATH"/desktop.vert");
+    frg_source.append_file(GLCOMPBENCH_DATA_PATH"/desktop-blur.frag");
+
+    /* Don't let the gaussian curve become too narrow */
+    if (sigma < 1.0)
+        sigma = 1.0;
+
+    unsigned int side = 2 * radius + 1;
+
+    for (unsigned int i = 0; i < radius + 1; i++) {
+        float s2 = 2.0 * sigma * sigma;
+        float k = 1.0 / std::sqrt(M_PI * s2) * std::exp( - (static_cast<float>(i) * i) / s2);
+        std::stringstream ss_tmp;
+        ss_tmp << "Kernel" << i;
+        frg_source.add_const(ss_tmp.str(), k);
+    }
+
+    std::stringstream ss;
+    ss << "result = " << std::endl;
+
+    if (direction == BlurDirectionHorizontal) {
+        for (unsigned int i = 0; i < side; i++) {
+            int offset = static_cast<int>(i - radius);
+            ss << "texture2D(Texture0, TextureCoord + vec2(" <<
+                  offset << ".0 * TextureStepX, 0.0)) * Kernel" <<
+                  std::abs(offset) << " +" << std::endl;
+        }
+        ss << "0.0 ;" << std::endl;
+    }
+    else if (direction == BlurDirectionVertical) {
+        for (unsigned int i = 0; i < side; i++) {
+            int offset = static_cast<int>(i - radius);
+            ss << "texture2D(Texture0, TextureCoord + vec2(0.0, " <<
+                  offset << ".0 * TextureStepY)) * Kernel" <<
+                  std::abs(offset) << " +" << std::endl;
+        }
+        ss << "0.0 ;" << std::endl;
+    }
+    else if (direction == BlurDirectionBoth) {
+        for (unsigned int i = 0; i < side; i++) {
+            int ioffset = static_cast<int>(i - radius);
+            for (unsigned int j = 0; j < side; j++) {
+                int joffset = static_cast<int>(j - radius);
+                ss << "texture2D(Texture0, TextureCoord + vec2(" <<
+                      ioffset << ".0 * TextureStepX, " <<
+                      joffset << ".0 * TextureStepY))" <<
+                      " * Kernel" << std::abs(ioffset) <<
+                      " * Kernel" << std::abs(joffset) << " +" << std::endl;
+            }
+        }
+        ss << " 0.0;" << std::endl;
+    }
+
+    frg_source.replace("$CONVOLUTION$", ss.str());
+}
+
+//
+// Private structure used to avoid contaminating composite-test.h with all of
+// the CompositeTestSimpleBlur internal classes.
+//
+struct BlurPrivate
+{
+    RenderScreen screen;
+    RenderClearImage desktop;
+    map<unsigned int, RenderWindowBlur*> windowMap;
+    int screen_width;
+    int screen_height;
+    double lastUpdateTime;
+    float window_scale_factor;
+
+    BlurPrivate() : 
+        desktop(GLCOMPBENCH_DATA_PATH"/background.png"),
+        screen_width(0),
+        screen_height(0),
+        lastUpdateTime(0),
+        window_scale_factor(0.4) {}
+
+    ~BlurPrivate() {}
+};
+
+CompositeTestSimpleBlur::CompositeTestSimpleBlur() :
+    CompositeTestSimpleBase("blur",
+                            GLCOMPBENCH_DATA_PATH"/desktop.vert",
+                            GLCOMPBENCH_DATA_PATH"/desktop.frag")
+{
+    options_["use-vbo"] = CompositeTest::Option("use-vbo", "false",
+        "Whether to use VBOs for rendering [true,false]");
+    options_["window-size"] = CompositeTest::Option("window-size", "0.5",
+        "a scaling factor for the window size (relative to screen screen size) [0.0 - 0.5]");
+    options_["passes"] = CompositeTest::Option("passes", "1",
+        "the number of effect passes (effect dependent)");
+    options_["blur-radius"] = CompositeTest::Option("blur-radius", "5",
+        "the blur effect radius (in pixels)");
+    options_["separable"] = CompositeTest::Option("separable", "true",
+        "use separable convolution for the blur effect");
+    priv_ = new BlurPrivate();
+}
+
+CompositeTestSimpleBlur::~CompositeTestSimpleBlur()
+{
+    for (map<unsigned int, RenderWindowBlur*>::iterator winIt = priv_->windowMap.begin();
+         winIt != priv_->windowMap.end();
+         winIt++)
+    {
+        RenderWindowBlur* ro = winIt->second;
+        delete ro;
+    }
+    delete priv_;
+}
+
+//
+// Width/height values used to compute buffer sizes are likely to generate
+// errors in the GL implementation if they are not at least 1, so make sure we
+// have sane values.
+//
+static void
+clamp_size(vec2& size)
+{
+    if (size.x() < 1.0)
+    {
+        size.x(1.0);
+    }
+    if (size.y() < 1.0)
+    {
+        size.y(1.0);
+    }
+}
+
+void
+CompositeTestSimpleBlur::prepare_for_run(list<CompositeWindow*>& window_list)
+{
+    // See how our options tell us to behave...
+    priv_->window_scale_factor = Util::fromString<float>(options_["window-size"].value);
+    unsigned int passes(Util::fromString<unsigned int>(options_["passes"].value));
+    unsigned int radius(Util::fromString<unsigned int>(options_["blur-radius"].value));
+    bool separable(options_["separable"].value == "true");
+
+    // Ensure we get a transparent clear color for all following operations
+    glClearColor(0.0, 0.0, 0.0, 0.0);
+    glDisable(GL_DEPTH_TEST);
+    glDepthMask(GL_FALSE);
+
+    priv_->screen.init(program_);
+    priv_->desktop.init(program_);
+
+    unsigned int visible_windows(num_visible_windows(window_list));
+    float angular_step(2 * M_PI / visible_windows);
+    vec2 screen_size = priv_->screen.size();
+    unsigned int i(0);
+    for(list<CompositeWindow*>::iterator iter = window_list.begin();
+        iter != window_list.end();
+        ++iter)
+    {
+        CompositeWindow* win = *iter;
+        if (!win->get_texture().is_valid())
+        {
+            continue;
+        }
+        vec2 window_size(win->width(), win->height());
+        window_size *= priv_->window_scale_factor;
+        clamp_size(window_size);
+        vec2 corner_offset(window_size / 2.0);
+        RenderWindowBlur* ro = new RenderWindowBlur(passes, radius, separable);
+        ro->init(program_);
+        ro->set_background(win->get_texture().i);
+        vec2 center(screen_size.x() * (0.5 + 0.25 * cos(i * angular_step)),
+                    screen_size.y() * (0.5 + 0.25 * sin(i * angular_step)));
+        ro->position(center - corner_offset);
+        ro->resize(window_size);
+        priv_->windowMap.insert(make_pair(win->get_xwindow(), ro));
+        i++;
+    }
+    //
+    // Ensure the screen is the current rendering target (it might have changed
+    // to a FBO in the previous steps).
+    //
+    priv_->screen.make_current();
+}
+
+void
+CompositeTestSimpleBlur::reshape(int width, int height)
+{
+    CompositeTestSimpleBase::reshape(width, height);
+    vec2 screen_size(width, height);
+    priv_->screen.resize(screen_size);
+    priv_->desktop.resize(screen_size);
+}
+
+void
+CompositeTestSimpleBlur::draw(list<CompositeWindow *> &window_list)
+{
+    unsigned int visible_windows(num_visible_windows(window_list));
+    float angular_step(2 * M_PI / visible_windows);
+    vec2 screen_size = priv_->screen.size();
+    unsigned int i(0);
+
+    // Ensure we get a transparent clear color for all following operations.
+    glClearColor(0.0, 0.0, 0.0, 0.0);
+    priv_->desktop.clear();
+
+    for(list<CompositeWindow*>::iterator cwIt = window_list.begin();
+        cwIt != window_list.end();
+        ++cwIt)
+    {
+        CompositeWindow* cw = *cwIt;
+        if (!cw->get_texture().is_valid())
+        {
+            continue;
+        }
+        unsigned int winHandle(cw->get_xwindow());
+        map<unsigned int, RenderWindowBlur*>::iterator roIt = priv_->windowMap.find(winHandle);
+        if (roIt == priv_->windowMap.end())
+        {
+            Log::debug("Failed to find window 0x%x in window map\n", winHandle);
+            continue;
+        }
+        RenderWindowBlur* ro = roIt->second;
+        ro->refresh_window(cw->get_texture().i);
+        vec2 window_size(cw->width(), cw->height());
+        window_size *= priv_->window_scale_factor;
+        clamp_size(window_size);
+        vec2 corner_offset(window_size / 2.0);
+        vec2 center(screen_size.x() * (0.5 + 0.25 * cos(i * angular_step)),
+                    screen_size.y() * (0.5 + 0.25 * sin(i * angular_step)));
+        ro->position(center - corner_offset);
+        ro->resize(window_size);
+        ro->render_to(priv_->desktop);
+        i++;
+    }
+
+    // Present the results to the screen.
+    priv_->desktop.render_to(priv_->screen);
+}

=== modified file 'src/composite-test-simple-brick.cc'
--- src/composite-test-simple-brick.cc	2011-09-05 15:23:11 +0000
+++ src/composite-test-simple-brick.cc	2012-03-09 03:58:54 +0000
@@ -37,9 +37,9 @@ 
 const vec2 CompositeTestSimpleBrick::brickPct_(0.90, 0.85);
 
 void
-CompositeTestSimpleBrick::prepare_for_run()
+CompositeTestSimpleBrick::prepare_for_run(std::list<CompositeWindow*> &window_list)
 {
-    CompositeTestSimpleBase::prepare_for_run();
+    CompositeTestSimpleBase::prepare_for_run(window_list);
 
     lightPos_ = LibMatrix::vec4(0.0, 1.0, 1.0, 0.0);
 

=== modified file 'src/composite-test-xrender.cc'
--- src/composite-test-xrender.cc	2011-08-03 17:14:34 +0000
+++ src/composite-test-xrender.cc	2012-03-09 03:58:54 +0000
@@ -52,8 +52,10 @@ 
 }
 
 void
-CompositeTestXRender::prepare_for_run()
+CompositeTestXRender::prepare_for_run(std::list<CompositeWindow*> &window_list)
 {
+    CompositeTest::prepare_for_run(window_list);
+
     if (options_["filter"].value == "bilinear")
         xrender_filter_ = FilterBilinear;
     else

=== modified file 'src/composite-test.h'
--- src/composite-test.h	2011-12-05 18:14:07 +0000
+++ src/composite-test.h	2012-03-09 03:58:54 +0000
@@ -45,7 +45,11 @@ 
 
     virtual void init() {}
     virtual void deinit() {}
-    virtual void prepare_for_run() {}
+    virtual void prepare_for_run(std::list<CompositeWindow*> &window_list)
+    {
+        if (window_list.empty())
+            return;
+    }
     virtual void draw(std::list<CompositeWindow*> &window_list)
     {
         (void)window_list;
@@ -105,7 +109,7 @@ 
 public:
     CompositeTestDefaultOptions() : CompositeTest("", "") {}
 
-    virtual void prepare_for_run();
+    virtual void prepare_for_run(std::list<CompositeWindow*> &window_list);
     virtual bool set_option(const std::string &opt, const std::string &val);
 
 protected:
@@ -121,7 +125,7 @@ 
 
     virtual void init();
     virtual void deinit();
-    virtual void prepare_for_run();
+    virtual void prepare_for_run(std::list<CompositeWindow*> &window_list);
     virtual void cleanup();
     virtual void reshape(int width, int height);
 
@@ -168,7 +172,7 @@ 
         {}
 
     virtual void draw(std::list<CompositeWindow*> &window_list);
-    virtual void prepare_for_run();
+    virtual void prepare_for_run(std::list<CompositeWindow*> &window_list);
 private:
     LibMatrix::vec4 lightPos_;
     static const LibMatrix::vec3 mortarColor_;
@@ -187,7 +191,7 @@ 
 
     virtual void init();
     virtual void deinit();
-    virtual void prepare_for_run();
+    virtual void prepare_for_run(std::list<CompositeWindow*> &window_list);
     virtual void draw(std::list<CompositeWindow*> &window_list);
 
 private:
@@ -202,7 +206,7 @@ 
 
     virtual void init();
     virtual void deinit();
-    virtual void prepare_for_run();
+    virtual void prepare_for_run(std::list<CompositeWindow*> &window_list);
     virtual void draw(std::list<CompositeWindow*> &window_list);
 
 private:
@@ -225,4 +229,19 @@ 
     virtual bool init_shaders(ShaderSource& vtx, ShaderSource& frg);
 };
 
+struct BlurPrivate;
+
+class CompositeTestSimpleBlur : public CompositeTestSimpleBase
+{
+    static const std::string fade_bias_name_;
+    BlurPrivate* priv_;
+public:
+    CompositeTestSimpleBlur();
+    ~CompositeTestSimpleBlur();
+
+    virtual void draw(std::list<CompositeWindow*> &window_list);
+    virtual void reshape(int width, int height);
+    virtual void prepare_for_run(std::list<CompositeWindow*> &window_list);
+};
+
 #endif // COMPOSITE_TEST_H_

=== modified file 'src/glcompbench.cc'
--- src/glcompbench.cc	2012-02-17 11:51:23 +0000
+++ src/glcompbench.cc	2012-03-20 16:32:54 +0000
@@ -45,6 +45,7 @@ 
 static const char *default_benchmarks[] = {
     "default",
     "fade",
+    "blur",
     "brick",
     "pixman",
     "xrender",
@@ -195,6 +196,7 @@ 
 
     Benchmark::register_test(*new CompositeTestDefaultOptions());
     Benchmark::register_test(*new CompositeTestSimpleDefault());
+    Benchmark::register_test(*new CompositeTestSimpleBlur());
     Benchmark::register_test(*new CompositeTestSimpleFade());
     Benchmark::register_test(*new CompositeTestSimpleBrick());
     Benchmark::register_test(*new CompositeTestPixman());

=== added file 'src/render-object.cc'
--- src/render-object.cc	1970-01-01 00:00:00 +0000
+++ src/render-object.cc	2012-03-16 18:04:51 +0000
@@ -0,0 +1,282 @@ 
+/*
+ * Copyright © 2012 Linaro Limited
+ *
+ * This file is part of glcompbench.
+ *
+ * glcompbench 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.
+ *
+ * glcompbench 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 glcompbench.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ *  Jesse Barker <jesse.barker@linaro.org>
+ */
+ 
+#include "log.h"
+#include "render-object.h"
+#include "shader-source.h"
+#include "gl-headers.h"
+#include "texture.h"
+
+using LibMatrix::vec2;
+using std::string;
+
+bool
+RenderObject::load_shaders_from_strings(Program &program,
+    const string &vtx_shader, const string &frg_shader)
+{
+    program.init();
+
+    Log::debug("Loading vertex shader:\n%s", vtx_shader.c_str());
+
+    program.addShader(GL_VERTEX_SHADER, vtx_shader);
+    if (!program.valid()) {
+        Log::error("Failed to add vertex shader:\n  %s\n", "RenderObject: ",
+            program.errorMessage().c_str());
+        program.release();
+        return false;
+    }
+
+    Log::debug("Loading fragment shader:\n%s", frg_shader.c_str());
+
+    program.addShader(GL_FRAGMENT_SHADER, frg_shader);
+    if (!program.valid()) {
+        Log::error("Failed to add fragment shader:\n  %s\n",
+            program.errorMessage().c_str());
+        program.release();
+        return false;
+    }
+
+    program.build();
+    if (!program.ready()) {
+        Log::error("Failed to link program:  %s\n",
+            program.errorMessage().c_str());
+        program.release();
+        return false;
+    }
+
+    return true;
+}
+
+void
+RenderObject::init(Program& default_program)
+{
+    default_program_ = &default_program;
+
+    // Create a texture to use as a render target
+    glGenTextures(1, &texture_);
+    glBindTexture(GL_TEXTURE_2D, texture_);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size_.x(), size_.y(), 0,
+                 GL_RGBA, GL_UNSIGNED_BYTE, 0);
+
+    // Create a FBO
+    glGenFramebuffers(1, &fbo_);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                           GL_TEXTURE_2D, texture_, 0);
+
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+    texture_contents_invalid_ = true;
+}
+
+void
+RenderObject::release()
+{
+    // Release resources
+    if (texture_ != 0)
+    {
+        glDeleteTextures(1, &texture_);
+        texture_ = 0;
+    }
+    if (fbo_ != 0)
+    {
+        glDeleteFramebuffers(1, &fbo_);
+        fbo_ = 0;
+    }
+}
+
+void
+RenderObject::make_current()
+{
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
+    glViewport(0, 0, size_.x(), size_.y());
+}
+
+void
+RenderObject::resize(const vec2& size)
+{
+    /// Recreate the backing texture with correct size
+    if (size_ != size) {
+        size_ = size;
+        glBindTexture(GL_TEXTURE_2D, texture_);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size_.x(), size_.y(), 0,
+                     GL_RGBA, GL_UNSIGNED_BYTE, 0);
+        texture_contents_invalid_ = true;
+    }
+
+    if (texture_contents_invalid_) {
+        clear();
+        texture_contents_invalid_ = false;
+    }
+}
+
+void
+RenderObject::clear()
+{
+    make_current();
+    glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void
+RenderObject::render_to(RenderObject& target)
+{
+    render_to(target, *default_program_);
+}
+
+void
+RenderObject::render_to(RenderObject& target, Program& program)
+{
+    vec2 anchor(pos_);
+    vec2 ll(pos_ - anchor);
+    vec2 ur(pos_ + size_ - anchor);
+
+    /* Calculate new position according to rotation value */
+    GLfloat position[2 * 4] = {
+        rotate_x(ll.x(), ll.y()) + anchor.x(), rotate_y(ll.x(), ll.y()) + anchor.y(),
+        rotate_x(ur.x(), ll.y()) + anchor.x(), rotate_y(ur.x(), ll.y()) + anchor.y(),
+        rotate_x(ll.x(), ur.y()) + anchor.x(), rotate_y(ll.x(), ur.y()) + anchor.y(),
+        rotate_x(ur.x(), ur.y()) + anchor.x(), rotate_y(ur.x(), ur.y()) + anchor.y(),
+    };
+
+    /* Normalize position and write back to array */
+    for (unsigned int i = 0; i < 4; i++) {
+        unsigned int idx1(2 * i);
+        unsigned int idx2(2 * i + 1);
+        const vec2& v2(target.normalize_position(vec2(position[idx1], position[idx2])));
+        position[idx1] = v2.x();
+        position[idx2] = v2.y();
+    }
+
+    static const GLfloat texcoord[2 * 4] = {
+        0.0, 0.0,
+        1.0, 0.0,
+        0.0, 1.0,
+        1.0, 1.0,
+    };
+
+    target.make_current();
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, texture_);
+    draw_quad_with_program(position, texcoord, program);
+}
+
+void
+RenderObject::draw_quad_with_program(const float *position,
+    const float *texcoord, Program &program)
+{
+    int pos_index = program["position"].location();
+    int tex_index = program["texcoord"].location();
+
+    program.start();
+
+    glEnableVertexAttribArray(pos_index);
+    glEnableVertexAttribArray(tex_index);
+    glVertexAttribPointer(pos_index, 2,
+                          GL_FLOAT, GL_FALSE, 0, position);
+    glVertexAttribPointer(tex_index, 2,
+                          GL_FLOAT, GL_FALSE, 0, texcoord);
+
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+    glDisableVertexAttribArray(tex_index);
+    glDisableVertexAttribArray(pos_index);
+
+    program.stop();
+}
+
+RenderClearImage::RenderClearImage(const string& image_file) :
+    RenderObject(), background_texture_name_(image_file),
+    background_texture_(0), background_is_image_(false),
+    texcoord_(texcoord_window_)
+{
+    if (!background_texture_name_.empty())
+    {
+        background_is_image_ = true;
+        texcoord_ = texcoord_image_;
+    }
+}
+
+RenderClearImage::RenderClearImage() :
+    RenderObject(),
+    background_texture_(0), background_is_image_(false),
+    texcoord_(texcoord_window_)
+{
+}
+
+void
+RenderClearImage::init(Program& program)
+{
+    RenderObject::init(program);
+    if (background_is_image_)
+    {
+        // Load the image into a texture.
+        Texture::load(background_texture_name_,
+                      &background_texture_, GL_LINEAR, GL_LINEAR, 0);
+    }
+}
+
+void
+RenderClearImage::set_background(unsigned int background_texture)
+{
+    background_texture_ = background_texture;
+}
+
+void
+RenderClearImage::clear()
+{
+    make_current();
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, background_texture_);
+
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    draw_quad_with_program(position_, texcoord_, *default_program_);
+    glDisable(GL_BLEND);
+}
+
+const float RenderClearImage::position_[8] = {
+    -1.0, -1.0,
+     1.0, -1.0,
+    -1.0,  1.0,
+     1.0,  1.0,
+};
+const float RenderClearImage::texcoord_image_[8] = {
+    0.0, 0.0,
+    1.0, 0.0,
+    0.0, 1.0,
+    1.0, 1.0,
+};
+const float RenderClearImage::texcoord_window_[8] = {
+    0.0, 1.0,
+    1.0, 1.0,
+    0.0, 0.0,
+    1.0, 0.0,
+};
+

=== added file 'src/render-object.h'
--- src/render-object.h	1970-01-01 00:00:00 +0000
+++ src/render-object.h	2012-03-16 18:04:51 +0000
@@ -0,0 +1,152 @@ 
+/*
+ * Copyright © 2012 Linaro Limited
+ *
+ * This file is part of glcompbench.
+ *
+ * glcompbench 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.
+ *
+ * glcompbench 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 glcompbench.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ *  Jesse Barker <jesse.barker@linaro.org>
+ */
+#ifndef RENDER_OBJECT_H_
+#define RENDER_OBJECT_H_
+
+#include "vec.h"
+#include "program.h"
+
+//
+// A RenderObject represents a source and target of rendering
+// operations.
+//
+class RenderObject
+{
+public:
+    RenderObject() :
+        texture_(0), fbo_(0), rotation_rad_(0),
+        texture_contents_invalid_(true) {}
+
+    // Public interfaces with default implementations
+    virtual void init(Program& default_program);
+    virtual void release();
+    virtual void resize(const LibMatrix::vec2& size);
+    virtual void clear();
+    virtual void render_to(RenderObject& target);
+    virtual void render_to(RenderObject& target, Program& program);
+
+    // Public methods
+    void make_current();
+
+    // Get/set methods
+    void position(const LibMatrix::vec2& pos) { pos_ = pos; }
+    const LibMatrix::vec2& position() { return pos_; }
+    const LibMatrix::vec2& size() { return size_; }
+    unsigned int texture() { return texture_; }
+
+    // Normalizes a position from [0, size] to [-1.0, 1.0]
+    LibMatrix::vec2 normalize_position(const LibMatrix::vec2& pos)
+    {
+        return pos * 2.0 / size_ - 1.0;
+    }
+
+    // Normalizes a position from [0, size] to [0.0, 1.0]
+    LibMatrix::vec2 normalize_texcoord(const LibMatrix::vec2& pos)
+    {
+        return pos / size_;
+    }
+
+    void rotation(float degrees)
+    {
+        rotation_rad_ = (M_PI * degrees / 180.0);
+    }
+
+protected:
+    static bool load_shaders_from_strings(Program& program,
+        const std::string& vtx_shader, const std::string& frg_shader);
+    static void draw_quad_with_program(const float *position, const float *texcoord,
+                                Program &program);
+
+    LibMatrix::vec2 pos_;
+    LibMatrix::vec2 size_;
+    unsigned int texture_;
+    unsigned int fbo_;
+    Program* default_program_;
+
+private:
+    float rotate_x(float x, float y)
+    {
+        return x * cos(rotation_rad_) - y * sin(rotation_rad_);
+    }
+
+    float rotate_y(float x, float y)
+    {
+        return x * sin(rotation_rad_) + y * cos(rotation_rad_);
+    }
+
+    float rotation_rad_;
+    bool texture_contents_invalid_;
+};
+
+//
+// A RenderObject representing the screen.
+//
+// Rendering to this objects renders to the screen framebuffer.
+//
+class RenderScreen : public RenderObject
+{
+public:
+    // Our own implementation of the init and release interfaces.
+    virtual void init(Program& program) { default_program_ = &program; }
+    virtual void release() {}
+};
+
+//
+// A RenderObject with a background image.
+//
+// The image is drawn to the RenderObject automatically when the
+// object is cleared, resized etc. The background texture for this object
+// can either be an image loaded from a named file (in which case the texture
+// name is created during the load), or it can be set from an existing texture
+// object.
+//
+class RenderClearImage : public RenderObject
+{
+public:
+    RenderClearImage();
+    RenderClearImage(const std::string& image_file);
+
+    // Our own implementation of the init interface.
+    virtual void init(Program& program);
+
+    // Sets the background texture name.
+    void set_background(unsigned int background_texture);
+
+    // "Clears" the object to the contents of the background texture. 
+    void clear();
+
+private:
+    std::string background_texture_name_;
+    unsigned int background_texture_;
+    bool background_is_image_;
+    // Vertex position is always the same for any rendering with this object,
+    // however, texture coordinates vary depending upon whether the texture
+    // came from a static image loaded from a file (for example) or a window
+    // system pixmap (inverted sense of y direction).
+    const float* texcoord_;
+    static const float position_[8];
+    static const float texcoord_image_[8];
+    static const float texcoord_window_[8];
+};
+
+#endif // RENDER_OBJECT_H_

=== added file 'src/texture.cc'
--- src/texture.cc	1970-01-01 00:00:00 +0000
+++ src/texture.cc	2012-03-01 23:28:36 +0000
@@ -0,0 +1,201 @@ 
+/*
+ * Copyright © 2008 Ben Smith
+ * 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:
+ *  Ben Smith (original glmark benchmark)
+ *  Alexandros Frantzis (glmark2)
+ */
+#include "texture.h"
+#include "log.h"
+#include "util.h"
+
+#include <cstdarg>
+#include <png.h>
+#include <memory>
+
+class PNGState
+{
+public:
+    PNGState() :
+        png_(0),
+        info_(0),
+        rows_(0) {}
+    ~PNGState()
+    {
+        if (png_)
+        {
+            png_destroy_read_struct(&png_, &info_, 0);
+        }
+    }
+    bool gotData(const std::string& filename)
+    {
+        static const int png_transforms = PNG_TRANSFORM_STRIP_16 |
+                                          PNG_TRANSFORM_GRAY_TO_RGB |
+                                          PNG_TRANSFORM_PACKING |
+                                          PNG_TRANSFORM_EXPAND;
+
+        Log::debug("Reading PNG file %s\n", filename.c_str());
+
+        const std::auto_ptr<std::istream> is_ptr(Util::get_resource(filename));
+        if (!(*is_ptr)) {
+            Log::error("Cannot open file %s!\n", filename.c_str());
+            return false;
+        }
+
+        /* Set up all the libpng structs we need */
+        png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+        if (!png_) {
+            Log::error("Couldn't create libpng read struct\n");
+            return false;
+        }
+
+        info_ = png_create_info_struct(png_);
+        if (!info_) {
+            Log::error("Couldn't create libpng info struct\n");
+            return false;
+        }
+
+        /* Set up libpng error handling */
+        if (setjmp(png_jmpbuf(png_))) {
+            Log::error("libpng error while reading file %s\n", filename.c_str());
+            return false;
+        }
+
+        /* Read the image information and data */
+        png_set_read_fn(png_, reinterpret_cast<voidp>(is_ptr.get()), png_read_fn);
+
+        png_read_png(png_, info_, png_transforms, 0);
+
+        rows_ = png_get_rows(png_, info_);
+
+        return true;
+    }
+    unsigned int width() const { return png_get_image_width(png_, info_); }
+    unsigned int height() const { return png_get_image_height(png_, info_); }
+    unsigned int pixelBytes() const
+    {
+        if (png_get_color_type(png_, info_) == PNG_COLOR_TYPE_RGB)
+        {
+            return 3;
+        }
+        return 4;
+    }
+    const unsigned char* row(unsigned int idx) const { return rows_[idx]; }
+private:
+    static void png_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
+    {
+        std::istream *is = reinterpret_cast<std::istream*>(png_get_io_ptr(png_ptr));
+        is->read(reinterpret_cast<char *>(data), length);
+    }
+    png_structp png_;
+    png_infop info_;
+    png_bytepp rows_;
+};
+
+class ImageData {
+    void resize(unsigned int w, unsigned int h, unsigned int b)
+    {
+        width = w;
+        height = h;
+        bpp = b;
+        delete [] pixels;
+        pixels = new unsigned char[bpp * width * height];
+    }
+
+public:
+    ImageData() : pixels(0), width(0), height(0), bpp(0) {}
+    ~ImageData() { delete [] pixels; }
+    bool load_png(const std::string &filename);
+
+    unsigned char *pixels;
+    unsigned int width;
+    unsigned int height;
+    unsigned int bpp;
+};
+
+bool
+ImageData::load_png(const std::string &filename)
+{
+    PNGState png;
+    bool ret = png.gotData(filename);
+    if (!ret)
+    {
+        return ret;
+    }
+
+    resize(png.width(), png.height(), png.pixelBytes());
+
+    Log::debug("    Height: %d Width: %d Bpp: %d\n", width, height, bpp);
+
+    /*
+     * Copy the image data to a contiguous memory area suitable for texture
+     * upload.
+     */
+    for (unsigned int i = 0; i < height; i++) {
+        memcpy(&pixels[bpp * width * i],
+               png.row(height - i - 1),
+               width * bpp);
+    }
+
+    return ret;
+}
+
+static void
+setup_texture(GLuint *tex, ImageData &image, GLint min_filter, GLint mag_filter)
+{
+    GLenum format = image.bpp == 3 ? GL_RGB : GL_RGBA;
+
+    glGenTextures(1, tex);
+    glBindTexture(GL_TEXTURE_2D, *tex);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexImage2D(GL_TEXTURE_2D, 0, format, image.width, image.height, 0,
+                 format, GL_UNSIGNED_BYTE, image.pixels);
+
+    if ((min_filter != GL_NEAREST && min_filter != GL_LINEAR) ||
+        (mag_filter != GL_NEAREST && mag_filter != GL_LINEAR))
+    {
+        glGenerateMipmap(GL_TEXTURE_2D);
+    }
+}
+
+bool
+Texture::load(const std::string &filename, GLuint *pTexture, ...)
+{
+    ImageData image;
+
+    if (!image.load_png(filename))
+        return false;
+
+    va_list ap;
+    va_start(ap, pTexture);
+    GLint arg;
+
+    while ((arg = va_arg(ap, GLint)) != 0) {
+        GLint arg2 = va_arg(ap, GLint);
+        setup_texture(pTexture, image, arg, arg2);
+        pTexture++;
+    }
+
+    va_end(ap);
+
+    return true;
+}

=== added file 'src/texture.h'
--- src/texture.h	1970-01-01 00:00:00 +0000
+++ src/texture.h	2012-03-01 23:28:36 +0000
@@ -0,0 +1,37 @@ 
+/*
+ * Copyright © 2008 Ben Smith
+ * 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:
+ *  Ben Smith (original glmark benchmark)
+ *  Alexandros Frantzis (glmark2)
+ */
+#ifndef GLMARK2_TEXTURE_H_
+#define GLMARK2_TEXTURE_H_
+
+#include "gl-headers.h"
+
+#include <string>
+
+class Texture
+{
+public:
+    static bool load(const std::string &filename, GLuint *pTexture, ...);
+};
+
+#endif

=== modified file 'src/wscript_build'
--- src/wscript_build	2011-12-12 13:18:23 +0000
+++ src/wscript_build	2012-03-01 23:28:36 +0000
@@ -49,7 +49,7 @@ 
         source   = common_sources + egl_sources,
         target   = 'glcompbench-egl-gl',
         uselib   = ['egl', 'gl'] + common_libs,
-        use      = ['matrix'],
+        use      = ['matrix', 'libpng12'],
         lib      = ['m'],
         defines  = ['USE_EGL', 'USE_GL']
         )
@@ -60,7 +60,7 @@ 
         source   = common_sources + egl_sources,
         target   = 'glcompbench-egl-es2',
         uselib   = ['egl', 'glesv2'] + common_libs,
-        use      = ['matrix-es2'],
+        use      = ['matrix-es2', 'libpng12'],
         lib      = ['m'],
         defines  = ['USE_EGL', 'USE_GLES2']
         )
@@ -71,7 +71,7 @@ 
         source   = common_sources + glx_sources,
         target   = 'glcompbench-glx',
         uselib   = ['gl'] + common_libs,
-        use      = ['matrix'],
+        use      = ['matrix', 'libpng12'],
         lib      = ['m'],
         defines  = ['USE_GLX', 'USE_GL']
         )
@@ -82,7 +82,7 @@ 
         source   = common_sources + egl_sources + glx_sources,
         target   = 'glcompbench',
         uselib   = ['glproxy'] + common_libs,
-        use      = ['matrix-glproxy'],
+        use      = ['matrix-glproxy', 'libpng12'],
         lib      = ['m'],
         defines  = ['USE_EGL', 'USE_GLES2', 'USE_GLX', 'USE_GL', 'USE_GLPROXY']
         )

=== modified file 'wscript'
--- wscript	2012-02-16 12:19:54 +0000
+++ wscript	2012-03-20 16:32:54 +0000
@@ -64,7 +64,7 @@ 
     # Check required packages
     req_pkgs = [('x11', 'x11'), ('xdamage', 'xdamage'),
                 ('xcomposite', 'xcomposite'), ('pixman-1', 'pixman'),
-                ('xext', 'xext'), ('xrender', 'xrender')]
+                ('xext', 'xext'), ('xrender', 'xrender'), ('libpng12', 'libpng12')]
     for (pkg, uselib) in req_pkgs:
         ctx.check_cfg(package = pkg, uselib_store = uselib, args = '--cflags --libs',
                 mandatory = True)