=== added file 'data/shaders/desktop-blur.frag'
--- data/shaders/desktop-blur.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/desktop-blur.frag	2011-09-12 15:39:23 +0000
@@ -0,0 +1,17 @@
+#ifdef GL_ES
+precision mediump float;
+#endif
+
+uniform sampler2D Texture0;
+
+varying vec2 TextureCoord;
+
+void main(void)
+{
+    vec4 result;
+
+    $CONVOLUTION$
+
+    gl_FragColor = result;
+}
+

=== added file 'data/shaders/desktop.frag'
--- data/shaders/desktop.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/desktop.frag	2011-09-09 13:57:53 +0000
@@ -0,0 +1,14 @@
+#ifdef GL_ES
+precision mediump float;
+#endif
+
+uniform sampler2D MaterialTexture0;
+
+varying vec2 TextureCoord;
+
+void main(void)
+{
+    vec4 texel = texture2D(MaterialTexture0, TextureCoord);
+    gl_FragColor = texel;
+}
+

=== added file 'data/shaders/desktop.vert'
--- data/shaders/desktop.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/desktop.vert	2011-09-09 13:57:53 +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/textures/desktop-window.png'
Binary files data/textures/desktop-window.png	1970-01-01 00:00:00 +0000 and data/textures/desktop-window.png	2011-09-09 13:57:53 +0000 differ
=== modified file 'src/android.cpp'
--- src/android.cpp	2011-08-18 15:03:40 +0000
+++ src/android.cpp	2011-09-12 16:08:24 +0000
@@ -44,6 +44,7 @@
     "effect2d:kernel=0,1,0;1,-4,1;0,1,0;",
     "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",
     "conditionals:vertex-steps=0:fragment-steps=0",
     "conditionals:vertex-steps=0:fragment-steps=5",
     "conditionals:vertex-steps=5:fragment-steps=0",
@@ -85,6 +86,7 @@
     Benchmark::register_scene(*new SceneBump(*g_canvas));
     Benchmark::register_scene(*new SceneEffect2D(*g_canvas));
     Benchmark::register_scene(*new ScenePulsar(*g_canvas));
+    Benchmark::register_scene(*new SceneDesktop(canvas));
 
     add_default_benchmarks(g_benchmarks);
 

=== modified file 'src/canvas-android.cpp'
--- src/canvas-android.cpp	2011-08-10 18:00:03 +0000
+++ src/canvas-android.cpp	2011-09-09 11:24:17 +0000
@@ -35,8 +35,6 @@
     if (!eglSwapInterval(eglGetCurrentDisplay(), 0))
         Log::info("** Failed to set swap interval. Results may be bounded above by refresh rate.\n");
 
-    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
-    glClearDepthf(1.0f);
     glEnable(GL_DEPTH_TEST);
     glDepthFunc(GL_LEQUAL);
     glEnable(GL_CULL_FACE);
@@ -56,6 +54,8 @@
 void
 CanvasAndroid::clear()
 {
+    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
+    glClearDepthf(1.0f);
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 }
 

=== modified file 'src/canvas-x11.cpp'
--- src/canvas-x11.cpp	2011-08-09 10:51:03 +0000
+++ src/canvas-x11.cpp	2011-09-09 11:24:17 +0000
@@ -84,12 +84,6 @@
     if (!make_current())
         return false;
 
-    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
-#if USE_GL
-    glClearDepth(1.0f);
-#elif USE_GLESv2
-    glClearDepthf(1.0f);
-#endif
     glEnable(GL_DEPTH_TEST);
     glDepthFunc(GL_LEQUAL);
     glEnable(GL_CULL_FACE);
@@ -110,6 +104,12 @@
 void
 CanvasX11::clear()
 {
+    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
+#if USE_GL
+    glClearDepth(1.0f);
+#elif USE_GLESv2
+    glClearDepthf(1.0f);
+#endif
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 }
 

=== modified file 'src/main.cpp'
--- src/main.cpp	2011-09-07 09:40:57 +0000
+++ src/main.cpp	2011-09-12 16:08:24 +0000
@@ -54,6 +54,7 @@
     "effect2d:kernel=0,1,0;1,-4,1;0,1,0;",
     "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",
     "conditionals:vertex-steps=0:fragment-steps=0",
     "conditionals:vertex-steps=0:fragment-steps=5",
     "conditionals:vertex-steps=5:fragment-steps=0",
@@ -96,6 +97,7 @@
     scenes.push_back(new SceneBump(canvas));
     scenes.push_back(new SceneEffect2D(canvas));
     scenes.push_back(new ScenePulsar(canvas));
+    scenes.push_back(new SceneDesktop(canvas));
 
     for (vector<Scene*>::const_iterator iter = scenes.begin();
          iter != scenes.end();

=== added file 'src/scene-desktop.cpp'
--- src/scene-desktop.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-desktop.cpp	2011-09-13 09:36:32 +0000
@@ -0,0 +1,775 @@
+/*
+ * Copyright © 2010-2011 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Alexandros Frantzis (glmark2)
+ */
+#include <cmath>
+
+#include "scene.h"
+#include "mat.h"
+#include "stack.h"
+#include "vec.h"
+#include "log.h"
+#include "program.h"
+#include "shader-source.h"
+#include "util.h"
+
+enum BlurDirection {
+    BlurDirectionHorizontal,
+    BlurDirectionVertical,
+    BlurDirectionBoth
+};
+
+static void
+create_blur_shaders(ShaderSource& vtx_source, ShaderSource& frg_source,
+                    unsigned int radius, float sigma, BlurDirection direction)
+{
+    vtx_source.append_file(GLMARK_DATA_PATH"/shaders/desktop.vert");
+    frg_source.append_file(GLMARK_DATA_PATH"/shaders/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 (size_t i = 0; i < radius + 1; i++) {
+        float s2 = 2.0 * sigma * sigma;
+        float k = 1.0 / std::sqrt(M_PI * s2) * std::exp( - ((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 (size_t i = 0; i < side; i++) {
+            int offset = (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 (size_t i = 0; i < side; i++) {
+            int offset = (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 (size_t i = 0; i < side; i++) {
+            int ioffset = (int)(i - radius);
+            for (size_t j = 0; j < side; j++) {
+                int joffset = (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());
+}
+
+/** 
+ * A RenderObject represents a source and target of rendering
+ * operations.
+ */
+class RenderObject
+{
+public:
+    RenderObject() : texture_(0), fbo_(0) { }
+
+    virtual ~RenderObject() { release(); }
+
+    virtual void init()
+    {
+        /* Create a texture to draw to */
+        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);
+
+        /* Load the shader program when this class if first used */
+        if (RenderObject::use_count == 0) {
+            ShaderSource vtx_source(GLMARK_DATA_PATH"/shaders/desktop.vert");
+            ShaderSource frg_source(GLMARK_DATA_PATH"/shaders/desktop.frag");
+            Scene::load_shaders_from_strings(main_program, vtx_source.str(),
+                                             frg_source.str());
+        }
+
+        RenderObject::use_count++;
+    }
+
+    virtual void release()
+    {
+        /* Release resources */
+        glDeleteTextures(1, &texture_);
+        glDeleteFramebuffers(1, &fbo_);
+        texture_ = 0;
+        fbo_ = 0;
+
+        /* 
+         * Release the shader program when object of this class
+         * are no longer in use.
+         */
+        RenderObject::use_count--;
+        if (RenderObject::use_count == 0)
+            RenderObject::main_program.release();
+    }
+
+    void make_current()
+    {
+        glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
+        glViewport(0, 0, size_.x(), size_.y());
+    }
+
+    void position(const LibMatrix::vec2& pos) { pos_ = pos; }
+    const LibMatrix::vec2& position() { return pos_; }
+
+
+    virtual void size(const LibMatrix::vec2& size)
+    {
+        /* Recreate the backing texture with correct size */
+        if (size_.x() != size.x() || size_.y() != size.y()) {
+            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);
+            clear();
+        }
+    }
+
+    const LibMatrix::vec2& size() { return size_; }
+
+    const LibMatrix::vec2& speed() { return speed_; }
+    void speed(const LibMatrix::vec2& speed) { speed_ = speed; }
+
+    GLuint texture() { return texture_; }
+
+    virtual void clear()
+    {
+        make_current();
+        glClear(GL_COLOR_BUFFER_BIT);
+    }
+
+    virtual void render_to(RenderObject& target, Program& program = main_program)
+    {
+        LibMatrix::vec2 final_pos(pos_ + size_);
+        LibMatrix::vec2 ll(target.normalize_position(pos_));
+        LibMatrix::vec2 ur(target.normalize_position(final_pos));
+
+        GLfloat position[2 * 4] = {
+            ll.x(), ll.y(),
+            ur.x(), ll.y(),
+            ll.x(), ur.y(),
+            ur.x(), ur.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);
+    }
+
+    virtual void render_from(RenderObject& target, Program& program = main_program)
+    {
+        LibMatrix::vec2 final_pos(pos_ + size_);
+        LibMatrix::vec2 ll_tex(target.normalize_texcoord(pos_));
+        LibMatrix::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);
+    }
+
+    /** 
+     * Normalizes a position from [0, size] to [-1.0, 1.0]
+     */
+    LibMatrix::vec2 normalize_position(LibMatrix::vec2& pos)
+    {
+        return LibMatrix::vec2(2.0 * pos.x() / size_.x() - 1.0,
+                               2.0 * pos.y() / size_.y() - 1.0);
+    }
+
+    /** 
+     * Normalizes a position from [0, size] to [0.0, 1.0]
+     */
+    LibMatrix::vec2 normalize_texcoord(LibMatrix::vec2& pos)
+    {
+        return LibMatrix::vec2(pos.x() / size_.x(),
+                               pos.y() / size_.y());
+    }
+
+
+protected:
+    void draw_quad_with_program(const GLfloat *position, const GLfloat *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();
+    }
+
+    static Program main_program;
+
+    LibMatrix::vec2 pos_;
+    LibMatrix::vec2 size_;
+    LibMatrix::vec2 speed_;
+    GLuint texture_;
+    GLuint fbo_;
+
+private:
+    static int use_count;
+
+};
+
+int RenderObject::use_count = 0;
+Program RenderObject::main_program;
+
+/** 
+ * A RenderObject representing the screen.
+ *
+ * Rendering to this objects renders to the screen framebuffer.
+ */
+class RenderScreen : public RenderObject
+{
+public:
+    virtual void init() {}
+};
+
+/** 
+ * A RenderObject with a background image.
+ *
+ * The image is drawn to the RenderObject automatically when the
+ * object is cleared, resized etc
+ */
+class RenderClearImage : public RenderObject
+{
+public:
+    RenderClearImage(const std::string& texture) :
+        RenderObject(), background_texture_name(texture),
+        background_texture_(0) {}
+
+    virtual void init()
+    {
+        RenderObject::init();
+
+        /* Load the image into a texture */
+        Texture::load(background_texture_name,
+                      &background_texture_, GL_LINEAR, GL_LINEAR, 0);
+
+    }
+
+    virtual void release()
+    {
+        glDeleteTextures(1, &background_texture_);
+        background_texture_ = 0;
+
+        RenderObject::release();
+    }
+
+    virtual void clear()
+    {
+        static const GLfloat position[2 * 4] = {
+            -1.0, -1.0,
+             1.0, -1.0,
+            -1.0,  1.0,
+             1.0,  1.0,
+        };
+        static const GLfloat texcoord[2 * 4] = {
+            0.0, 0.0,
+            1.0, 0.0,
+            0.0, 1.0,
+            1.0, 1.0,
+        };
+
+        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, main_program);
+        glDisable(GL_BLEND);
+    }
+
+private:
+    std::string background_texture_name;
+    GLuint background_texture_;
+};
+
+/** 
+ * A RenderObject that blurs the target it is drawn to.
+ */
+class RenderWindowBlur : public RenderObject
+{
+public:
+    RenderWindowBlur(unsigned int passes, unsigned int radius, bool separable,
+                     bool draw_contents = true) :
+        RenderObject(), passes_(passes), radius_(radius), separable_(separable),
+        draw_contents_(draw_contents) {}
+
+    virtual void init()
+    {
+        RenderObject::init();
+
+        /* Only have one instance of the window contents data */
+        if (draw_contents_ && RenderWindowBlur::use_count == 0)
+            window_contents_.init();
+
+        RenderWindowBlur::use_count++; 
+    }
+
+    virtual void release()
+    {
+        RenderWindowBlur::use_count--;
+
+        /* Only have one instance of the window contents data */
+        if (draw_contents_ && RenderWindowBlur::use_count == 0)
+            window_contents_.release();
+
+        RenderObject::release();
+    }
+
+    virtual void size(const LibMatrix::vec2& size)
+    {
+        RenderObject::size(size);
+        if (draw_contents_)
+            window_contents_.size(size);
+    }
+
+    virtual void render_to(RenderObject& target, Program& program)
+    {
+        (void)program;
+
+        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.
+         */
+        if (draw_contents_) {
+            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:
+    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);
+            Scene::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);
+            Scene::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);
+            Scene::load_shaders_from_strings(blur_program_v_, vtx_source.str(),
+                                             frg_source.str());
+        }
+
+        return blur_program_v_;
+    }
+
+    LibMatrix::uvec2 blur_program_dim_;
+    Program blur_program_;
+    Program blur_program_h_;
+    Program blur_program_v_;
+    unsigned int passes_;
+    unsigned int radius_;
+    bool separable_;
+    bool draw_contents_;
+
+    static int use_count;
+    static RenderClearImage window_contents_;
+
+};
+
+int RenderWindowBlur::use_count = 0;
+RenderClearImage RenderWindowBlur::window_contents_(GLMARK_DATA_PATH"/textures/desktop-window.png");
+
+/*******************************
+ * SceneDesktop implementation * 
+ *******************************/
+
+/** 
+ * Private structure used to avoid contaminating scene.h with all of the
+ * SceneDesktop internal classes.
+ */
+struct SceneDesktopPrivate
+{
+    RenderScreen screen;
+    RenderClearImage desktop;
+    std::vector<RenderObject *> windows;
+
+    SceneDesktopPrivate() :
+        desktop(GLMARK_DATA_PATH"/textures/effect-2d.png") {}
+
+    ~SceneDesktopPrivate() { Util::dispose_pointer_vector(windows); }
+    
+};
+
+
+SceneDesktop::SceneDesktop(Canvas &canvas) :
+    Scene(canvas, "desktop")
+{
+    priv_ = new SceneDesktopPrivate();
+    mOptions["effect"] = Scene::Option("effect", "blur",
+                                       "the effect to use [blur]");
+    mOptions["windows"] = Scene::Option("windows", "4",
+                                        "the number of windows");
+    mOptions["window-size"] = Scene::Option("window-size", "0.35",
+                                            "the window size as a percentage of the minimum screen dimension [0.0 - 0.5]");
+    mOptions["passes"] = Scene::Option("passes", "1",
+                                       "the number of effect passes (effect dependent)");
+    mOptions["blur-radius"] = Scene::Option("blur-radius", "5",
+                                            "the blur effect radius (in pixels)");
+    mOptions["separable"] = Scene::Option("separable", "true",
+                                          "use separable convolution for the blur effect");
+}
+
+SceneDesktop::~SceneDesktop()
+{
+    delete priv_;
+}
+
+int
+SceneDesktop::load()
+{
+    priv_->screen.init();
+    priv_->desktop.init();
+    return 1;
+}
+
+void
+SceneDesktop::unload()
+{
+    priv_->desktop.release();
+    priv_->screen.release();
+}
+
+void
+SceneDesktop::setup()
+{
+    Scene::setup();
+
+    std::stringstream ss;
+    unsigned int windows(0);
+    unsigned int passes(0);
+    unsigned int blur_radius(0);
+    float window_size_factor(0.0);
+    bool separable(mOptions["separable"].value == "true");
+
+    ss << mOptions["windows"].value;
+    ss >> windows;
+    ss.clear();
+    ss << mOptions["window-size"].value;
+    ss >> window_size_factor;
+    ss.clear();
+    ss << mOptions["passes"].value;
+    ss >> passes;
+    ss.clear();
+    ss << mOptions["blur-radius"].value;
+    ss >> blur_radius;
+
+    /* 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);
+
+    /* Set up the screen and desktop RenderObjects */
+    priv_->screen.size(LibMatrix::vec2(mCanvas.width(), mCanvas.height()));
+    priv_->desktop.size(LibMatrix::vec2(mCanvas.width(), mCanvas.height()));
+
+    /* Create the windows */
+    float angular_step(2.0 * M_PI / windows);
+    unsigned int min_dimension = std::min(mCanvas.width(), mCanvas.height());
+    float window_size(min_dimension * window_size_factor);
+    static const LibMatrix::vec2 corner_offset(window_size / 2.0,
+                                               window_size / 2.0);
+
+    for (unsigned int i = 0; i < windows; i++) {
+        LibMatrix::vec2 center(mCanvas.width() * (0.5 + 0.25 * cos(i * angular_step)),
+                               mCanvas.height() * (0.5 + 0.25 * sin(i * angular_step)));
+        RenderObject* win(new RenderWindowBlur(passes, blur_radius, separable));
+        (void)angular_step;
+
+        win->init();
+        win->position(center - corner_offset);
+        win->size(LibMatrix::vec2(window_size, window_size));
+        /* 
+         * Set the speed in increments of about 30 degrees (but not exactly,
+         * so we don't get windows moving just on the X axis or Y axis).
+         */
+        win->speed(LibMatrix::vec2(cos(0.1 + i * M_PI / 6.0) * mCanvas.width() / 3,
+                                   sin(0.1 + i * M_PI / 6.0) * mCanvas.height() / 3));
+        /* 
+         * Perform a dummy rendering to ensure internal shaders are initialized
+         * now, in order not to affect the benchmarking.
+         */
+        win->render_to(priv_->desktop);
+        priv_->windows.push_back(win);
+    }
+
+    /* 
+     * Ensure the screen is the current rendering target (it might have changed
+     * to a FBO in the previous steps).
+     */
+    priv_->screen.make_current();
+
+    mCurrentFrame = 0;
+    mRunning = true;
+    mStartTime = Scene::get_timestamp_us() / 1000000.0;
+    mLastUpdateTime = mStartTime;
+}
+
+void
+SceneDesktop::teardown()
+{
+    Util::dispose_pointer_vector(priv_->windows);
+    priv_->screen.make_current();
+
+    glEnable(GL_DEPTH_TEST);
+    glDepthMask(GL_TRUE);
+
+    Scene::teardown();
+}
+
+void
+SceneDesktop::update()
+{
+    double current_time = Scene::get_timestamp_us() / 1000000.0;
+    double dt = current_time - mLastUpdateTime;
+    double elapsed_time = current_time - mStartTime;
+
+    mLastUpdateTime = current_time;
+
+    std::vector<RenderObject *>& windows(priv_->windows);
+
+    /*
+     * Move the windows around the screen, bouncing them back when
+     * they reach the edge.
+     */
+    for (std::vector<RenderObject *>::const_iterator iter = windows.begin();
+         iter != windows.end();
+         iter++)
+    {
+        bool should_update = true;
+        RenderObject *win = *iter;
+        LibMatrix::vec2 new_pos(
+                win->position().x() + win->speed().x() * dt,
+                win->position().y() + win->speed().y() * dt);
+
+        if (new_pos.x() < 0.0 ||
+            new_pos.x() + win->size().x() > ((float)mCanvas.width()))
+        {
+            win->speed(LibMatrix::vec2(-win->speed().x(), win->speed().y()));
+            should_update = false;
+        }
+
+        if (new_pos.y() < 0.0 ||
+            new_pos.y() + win->size().y() > ((float)mCanvas.height()))
+        {
+            win->speed(LibMatrix::vec2(win->speed().x(), -win->speed().y()));
+            should_update = false;
+        }
+
+        if (should_update)
+            win->position(new_pos);
+    }
+
+    if (elapsed_time >= mDuration) {
+        mAverageFPS = mCurrentFrame / elapsed_time;
+        mRunning = false;
+    }
+
+    mCurrentFrame++;
+}
+
+void
+SceneDesktop::draw()
+{
+    std::vector<RenderObject *>& windows(priv_->windows);
+
+    /* Ensure we get a transparent clear color for all following operations */
+    glClearColor(0.0, 0.0, 0.0, 0.0);
+
+    priv_->desktop.clear();
+
+    for (std::vector<RenderObject *>::const_iterator iter = windows.begin();
+         iter != windows.end();
+         iter++)
+    {
+        RenderObject *win = *iter;
+        win->render_to(priv_->desktop);
+    }
+
+    priv_->desktop.render_to(priv_->screen);
+
+}
+
+Scene::ValidationResult
+SceneDesktop::validate()
+{
+    return ValidationUnknown;
+}

=== modified file 'src/scene.h'
--- src/scene.h	2011-08-18 13:20:04 +0000
+++ src/scene.h	2011-09-09 13:57:53 +0000
@@ -331,4 +331,23 @@
     void create_and_setup_mesh();
 };
 
+struct SceneDesktopPrivate;
+
+class SceneDesktop : public Scene
+{
+public:
+    SceneDesktop(Canvas &canvas);
+    int load();
+    void unload();
+    void setup();
+    void teardown();
+    void update();
+    void draw();
+    ValidationResult validate();
+
+    ~SceneDesktop();
+
+private:
+    SceneDesktopPrivate *priv_;
+};
 #endif

