blob: b1431f821054844a31f144f6bbd1c5896d8db6fd [file] [log] [blame]
/*
* 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)
* Jesse Barker (glmark2)
*/
#include <cmath>
#include <cstdlib>
#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"
#include "texture.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 (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());
}
/**
* 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) { }
virtual ~RenderObject() {}
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());
}
texture_contents_invalid_ = true;
RenderObject::use_count++;
}
virtual void release()
{
/* Release resources */
if (texture_ != 0)
{
glDeleteTextures(1, &texture_);
texture_ = 0;
}
if (fbo_ != 0)
{
glDeleteFramebuffers(1, &fbo_);
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);
texture_contents_invalid_ = true;
}
if (texture_contents_invalid_) {
clear();
texture_contents_invalid_ = false;
}
}
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)
{
render_to(target, main_program);
}
virtual void render_to(RenderObject& target, Program& program)
{
LibMatrix::vec2 anchor(pos_);
LibMatrix::vec2 ll(pos_ - anchor);
LibMatrix::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 (int i = 0; i < 4; i++) {
const LibMatrix::vec2& v2(
target.normalize_position(
LibMatrix::vec2(position[2 * i], position[2 * i + 1])
)
);
position[2 * i] = v2.x();
position[2 * i + 1] = 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);
}
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(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:
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:
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_;
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:
RenderScreen(Canvas &canvas) { fbo_ = canvas.fbo(); }
virtual void init() {}
virtual void release() {}
};
/**
* 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)
{
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);
/*
* Blend the colors normally, but don't change the
* destination alpha value.
*/
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
GL_ZERO, GL_ONE);
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_;
};
/**
* A RenderObject that draws a drop shadow around the window.
*/
class RenderWindowShadow : public RenderObject
{
public:
using RenderObject::size;
RenderWindowShadow(unsigned int shadow_size, bool draw_contents = true) :
RenderObject(), shadow_size_(shadow_size), draw_contents_(draw_contents) {}
virtual void init()
{
RenderObject::init();
/*
* Only have one instance of the resources.
* This works only if all windows have the same size, which
* is currently the case for this scene. If this condition
* ceases to be true we will need to create the resources per
* object.
*/
if (RenderWindowShadow::use_count == 0) {
shadow_h_.init();
shadow_v_.init();
shadow_corner_.init();
if (draw_contents_)
window_contents_.init();
}
RenderWindowShadow::use_count++;
}
virtual void release()
{
RenderWindowShadow::use_count--;
/* Only have one instance of the data */
if (RenderWindowShadow::use_count == 0) {
shadow_h_.release();
shadow_v_.release();
shadow_corner_.release();
if (draw_contents_)
window_contents_.release();
}
RenderObject::release();
}
virtual void size(const LibMatrix::vec2& size)
{
RenderObject::size(size);
shadow_h_.size(LibMatrix::vec2(size.x() - shadow_size_,
static_cast<double>(shadow_size_)));
shadow_v_.size(LibMatrix::vec2(size.y() - shadow_size_,
static_cast<double>(shadow_size_)));
shadow_corner_.size(LibMatrix::vec2(static_cast<double>(shadow_size_),
static_cast<double>(shadow_size_)));
if (draw_contents_)
window_contents_.size(size);
}
virtual void render_to(RenderObject& target)
{
glEnable(GL_BLEND);
/*
* Blend the colors normally, but don't change the
* destination alpha value.
*/
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
GL_ZERO, GL_ONE);
/* Bottom shadow */
shadow_h_.rotation(0.0);
shadow_h_.position(position() +
LibMatrix::vec2(shadow_size_,
-shadow_h_.size().y()));
shadow_h_.render_to(target);
/* Right shadow */
shadow_v_.rotation(90.0);
shadow_v_.position(position() +
LibMatrix::vec2(size().x() + shadow_v_.size().y(), 0.0));
shadow_v_.render_to(target);
/* Bottom right shadow */
shadow_corner_.rotation(0.0);
shadow_corner_.position(position() +
LibMatrix::vec2(size().x(),
-shadow_corner_.size().y()));
shadow_corner_.render_to(target);
/* Top right shadow */
shadow_corner_.rotation(90.0);
shadow_corner_.position(position() + size() +
LibMatrix::vec2(shadow_corner_.size().x(),
-shadow_corner_.size().y()));
shadow_corner_.render_to(target);
/* Bottom left shadow */
shadow_corner_.rotation(-90.0);
shadow_corner_.position(position());
shadow_corner_.render_to(target);
/*
* Blend the window contents with the target texture.
*/
if (draw_contents_) {
window_contents_.position(position());
window_contents_.render_to(target);
}
glDisable(GL_BLEND);
}
private:
unsigned int shadow_size_;
bool draw_contents_;
static int use_count;
static RenderClearImage window_contents_;
static RenderClearImage shadow_h_;
static RenderClearImage shadow_v_;
static RenderClearImage shadow_corner_;
};
int RenderWindowBlur::use_count = 0;
RenderClearImage RenderWindowBlur::window_contents_("desktop-window");
int RenderWindowShadow::use_count = 0;
RenderClearImage RenderWindowShadow::window_contents_("desktop-window");
RenderClearImage RenderWindowShadow::shadow_h_("desktop-shadow");
RenderClearImage RenderWindowShadow::shadow_v_("desktop-shadow");
RenderClearImage RenderWindowShadow::shadow_corner_("desktop-shadow-corner");
/*******************************
* 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(Canvas &canvas) :
screen(canvas), desktop("effect-2d") {}
~SceneDesktopPrivate() { Util::dispose_pointer_vector(windows); }
};
SceneDesktop::SceneDesktop(Canvas &canvas) :
Scene(canvas, "desktop")
{
priv_ = new SceneDesktopPrivate(canvas);
options_["effect"] = Scene::Option("effect", "blur", "The effect to use",
"blur,shadow");
options_["windows"] = Scene::Option("windows", "4",
"the number of windows");
options_["window-size"] = Scene::Option("window-size", "0.35",
"the window size as a percentage of the minimum screen dimension [0.0 - 0.5]");
options_["passes"] = Scene::Option("passes", "1",
"the number of effect passes (effect dependent)");
options_["blur-radius"] = Scene::Option("blur-radius", "5",
"the blur effect radius (in pixels)");
options_["separable"] = Scene::Option("separable", "true",
"use separable convolution for the blur effect",
"false,true");
options_["shadow-size"] = Scene::Option("shadow-size", "20",
"the size of the shadow (in pixels)");
}
SceneDesktop::~SceneDesktop()
{
delete priv_;
}
bool
SceneDesktop::load()
{
return true;
}
void
SceneDesktop::unload()
{
}
bool
SceneDesktop::setup()
{
if (!Scene::setup())
return false;
/* Parse the options */
unsigned int windows(0);
unsigned int passes(0);
unsigned int blur_radius(0);
float window_size_factor(0.0);
unsigned int shadow_size(0);
bool separable(options_["separable"].value == "true");
windows = Util::fromString<unsigned int>(options_["windows"].value);
window_size_factor = Util::fromString<float>(options_["window-size"].value);
passes = Util::fromString<unsigned int>(options_["passes"].value);
blur_radius = Util::fromString<unsigned int>(options_["blur-radius"].value);
shadow_size = Util::fromString<unsigned int>(options_["shadow-size"].value);
// Make sure the Texture object knows where to find our images.
Texture::find_textures();
/* 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.init();
priv_->desktop.init();
priv_->screen.size(LibMatrix::vec2(canvas_.width(), canvas_.height()));
priv_->desktop.size(LibMatrix::vec2(canvas_.width(), canvas_.height()));
/* Create the windows */
const float angular_step(2.0 * M_PI / windows);
unsigned int min_dimension = std::min(canvas_.width(), canvas_.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(canvas_.width() * (0.5 + 0.25 * cos(i * angular_step)),
canvas_.height() * (0.5 + 0.25 * sin(i * angular_step)));
RenderObject* win;
if (options_["effect"].value == "shadow")
win = new RenderWindowShadow(shadow_size);
else
win = new RenderWindowBlur(passes, blur_radius, separable);
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) * canvas_.width() / 3,
sin(0.1 + i * M_PI / 6.0) * canvas_.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();
currentFrame_ = 0;
running_ = true;
startTime_ = Util::get_timestamp_us() / 1000000.0;
lastUpdateTime_ = startTime_;
return true;
}
void
SceneDesktop::teardown()
{
for (std::vector<RenderObject*>::iterator winIt = priv_->windows.begin();
winIt != priv_->windows.end();
winIt++)
{
RenderObject* curObj = *winIt;
curObj->release();
delete curObj;
}
priv_->windows.clear();
priv_->screen.make_current();
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
priv_->desktop.release();
priv_->screen.release();
Scene::teardown();
}
void
SceneDesktop::update()
{
double current_time = Util::get_timestamp_us() / 1000000.0;
double dt = current_time - lastUpdateTime_;
Scene::update();
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() > static_cast<float>(canvas_.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() > static_cast<float>(canvas_.height()))
{
win->speed(LibMatrix::vec2(win->speed().x(), -win->speed().y()));
should_update = false;
}
if (should_update)
win->position(new_pos);
}
}
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()
{
static const double radius_3d(std::sqrt(3.0 * 2.0 * 2.0));
Canvas::Pixel ref;
/* Parse the options */
unsigned int windows(0);
unsigned int passes(0);
unsigned int blur_radius(0);
float window_size_factor(0.0);
unsigned int shadow_size(0);
windows = Util::fromString<unsigned int>(options_["windows"].value);
window_size_factor = Util::fromString<float>(options_["window-size"].value);
passes = Util::fromString<unsigned int>(options_["passes"].value);
blur_radius = Util::fromString<unsigned int>(options_["blur-radius"].value);
shadow_size = Util::fromString<unsigned int>(options_["shadow-size"].value);
if (options_["effect"].value == "blur")
{
if (windows == 4 && passes == 1 && blur_radius == 5)
ref = Canvas::Pixel(0x89, 0xa3, 0x53, 0xff);
else
return Scene::ValidationUnknown;
}
else if (options_["effect"].value == "shadow")
{
if (windows == 4 && fabs(window_size_factor - 0.35) < 0.0001 &&
shadow_size == 20)
{
ref = Canvas::Pixel(0x1f, 0x27, 0x0d, 0xff);
}
else
{
return Scene::ValidationUnknown;
}
}
Canvas::Pixel pixel = canvas_.read_pixel(512, 209);
double dist = pixel.distance_rgb(ref);
if (dist < radius_3d + 0.01) {
return Scene::ValidationSuccess;
}
else {
Log::debug("Validation failed! Expected: 0x%x Actual: 0x%x Distance: %f\n",
ref.to_le32(), pixel.to_le32(), dist);
return Scene::ValidationFailure;
}
return Scene::ValidationUnknown;
}