=== 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'
@@ -0,0 +1,13 @@
+uniform sampler2D Texture0;
+
+varying vec2 TextureCoord;
+
+void main(void)
+{
+ vec4 result;
+
+ $CONVOLUTION$
+
+ gl_FragColor = result;
+}
+
=== added file 'data/desktop.frag'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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)