blob: ef3a0b933e85645d30e9636e3cbcb24175ffd016 [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
*/
#include "scene.h"
#include "log.h"
#include "mat.h"
#include "stack.h"
#include "shader-source.h"
#include "util.h"
#include "gl-headers.h"
#include <cmath>
/***********************
* Wave implementation *
***********************/
/**
* A callback used to set up the grid by the Wave class.
* It is called for each "quad" of the grid.
*/
static void
wave_grid_conf(Mesh &mesh, int x, int y, int n_x, int n_y,
LibMatrix::vec3 &ul,
LibMatrix::vec3 &ll,
LibMatrix::vec3 &ur,
LibMatrix::vec3 &lr)
{
// These parameters are unused in this instance of a virtual callback
// function.
static_cast<void>(x);
static_cast<void>(y);
static_cast<void>(n_x);
static_cast<void>(n_y);
/*
* Order matters here, so that Wave::vertex_length_index() can work.
* Vertices of the triangles at index i that belong to length index i
* are even, those that belong to i + 1 are odd.
*/
const LibMatrix::vec3* t[] = {
&ll, &ur, &ul, &ur, &ll, &lr
};
for (int i = 0; i < 6; i++) {
mesh.next_vertex();
/*
* Set the vertex position and the three vertex positions
* of the triangle this vertex belongs to.
*/
mesh.set_attrib(0, *t[i]);
mesh.set_attrib(1, *t[3 * (i / 3)]);
mesh.set_attrib(2, *t[3 * (i / 3) + 1]);
mesh.set_attrib(3, *t[3 * (i / 3) + 2]);
}
}
/**
* Renders a grid mesh modulated by a sine wave
*/
class WaveMesh
{
public:
/**
* Creates a wave mesh.
*
* @param length the total length of the grid (in model coordinates)
* @param width the total width of the grid (in model coordinates)
* @param nlength the number of length-wise grid subdivisions
* @param nwidth the number of width-wise grid subdivisions
* @param wavelength the wave length as a proportion of the length
* @param duty_cycle the duty cycle ()
*/
WaveMesh(double length, double width, size_t nlength, size_t nwidth,
double wavelength, double duty_cycle) :
length_(length), width_(width), nlength_(nlength), nwidth_(nwidth),
wave_k_(2 * M_PI / (wavelength * length)),
wave_period_(2.0 * M_PI / wave_k_),
wave_full_period_(wave_period_ / duty_cycle),
wave_velocity_(0.1 * length), displacement_(nlength + 1)
{
create_program();
create_mesh();
}
~WaveMesh() { reset(); }
/**
* Updates the state of a wave mesh.
*
* @param elapsed the time elapsed since the beginning of the rendering
*/
void update(double elapsed)
{
std::vector<std::vector<float> >& vertices(mesh_.vertices());
/* Figure out which length index ranges need update */
std::vector<std::pair<size_t, size_t> > ranges;
for (size_t n = 0; n <= nlength_; n++) {
double d(displacement(n, elapsed));
if (d != displacement_[n]) {
if (ranges.size() > 0 && ranges.back().second == n - 1) {
ranges.back().second = n;
}
else {
ranges.push_back(
std::pair<size_t, size_t>(n > 0 ? n - 1 : 0, n)
);
}
}
displacement_[n] = d;
}
/* Update the vertex data of the changed ranges */
for (std::vector<std::pair<size_t, size_t> >::iterator iter = ranges.begin();
iter != ranges.end();
iter++)
{
/* First vertex of length index range */
size_t vstart(iter->first * nwidth_ * 6 + (iter->first % 2));
/*
* First vertex not included in the range. We should also update all
* vertices of triangles touching index i.
*/
size_t vend((iter->second + (iter->second < nlength_)) * nwidth_ * 6);
for (size_t v = vstart; v < vend; v++) {
size_t vt = 3 * (v / 3);
vertices[v][0 * 3 + 2] = displacement_[vertex_length_index(v)];
vertices[v][1 * 3 + 2] = displacement_[vertex_length_index(vt)];
vertices[v][2 * 3 + 2] = displacement_[vertex_length_index(vt + 1)];
vertices[v][3 * 3 + 2] = displacement_[vertex_length_index(vt + 2)];
}
/* Update pair with actual vertex range */
iter->first = vstart;
iter->second = vend - 1;
}
mesh_.update_vbo(ranges);
}
Mesh& mesh() { return mesh_; }
Program& program() { return program_; }
void reset()
{
program_.stop();
program_.release();
mesh_.reset();
}
private:
Mesh mesh_;
Program program_;
double length_;
double width_;
size_t nlength_;
size_t nwidth_;
/* Wave parameters */
double wave_k_;
double wave_period_;
double wave_full_period_;
double wave_fill_;
double wave_velocity_;
std::vector<double> displacement_;
/**
* Calculates the length index of a vertex.
*/
size_t vertex_length_index(size_t v)
{
return v / (6 * nwidth_) + (v % 2);
}
/**
* The sine wave function with duty-cycle.
*
* @param x the space coordinate
*
* @return the operation error code
*/
double wave_func(double x)
{
double r(fmod(x, wave_full_period_));
if (r < 0)
r += wave_full_period_;
/*
* Return either the sine value or 0.0, depending on the
* wave duty cycle.
*/
if (r > wave_period_)
{
return 0;
}
else
{
return 0.2 * std::sin(wave_k_ * r);
}
}
/**
* Calculates the displacement of the wave.
*
* @param n the length index
* @param elapsed the time elapsed since the beginning of the rendering
*
* @return the displacement at point n at time elapsed
*/
double displacement(size_t n, double elapsed)
{
double x(n * length_ / nlength_);
return wave_func(x - wave_velocity_ * elapsed);
}
/**
* Creates the GL shader program.
*/
void create_program()
{
/* Set up shaders */
static const std::string vtx_shader_filename(
GLMARK_DATA_PATH"/shaders/buffer-wireframe.vert");
static const std::string frg_shader_filename(
GLMARK_DATA_PATH"/shaders/buffer-wireframe.frag");
ShaderSource vtx_source(vtx_shader_filename);
ShaderSource frg_source(frg_shader_filename);
if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
frg_source.str()))
{
return;
}
}
/**
* Creates the grid mesh.
*/
void create_mesh()
{
/*
* We need to pass the positions of all vertex of the triangle
* in order to draw the wireframe.
*/
std::vector<int> vertex_format;
vertex_format.push_back(3); // Position of vertex
vertex_format.push_back(3); // Position of triangle vertex 0
vertex_format.push_back(3); // Position of triangle vertex 1
vertex_format.push_back(3); // Position of triangle vertex 2
mesh_.set_vertex_format(vertex_format);
std::vector<GLint> attrib_locations;
attrib_locations.push_back(program_["position"].location());
attrib_locations.push_back(program_["tvertex0"].location());
attrib_locations.push_back(program_["tvertex1"].location());
attrib_locations.push_back(program_["tvertex2"].location());
mesh_.set_attrib_locations(attrib_locations);
mesh_.make_grid(nlength_, nwidth_, length_, width_,
0.0, wave_grid_conf);
}
};
/******************************
* SceneBuffer implementation *
******************************/
struct SceneBufferPrivate {
WaveMesh *wave;
SceneBufferPrivate() : wave(0) {}
~SceneBufferPrivate() { delete wave; }
};
SceneBuffer::SceneBuffer(Canvas &pCanvas) :
Scene(pCanvas, "buffer")
{
priv_ = new SceneBufferPrivate();
options_["interleave"] = Scene::Option("interleave", "false",
"Whether to interleave vertex attribute data",
"false,true");
options_["update-method"] = Scene::Option("update-method", "map",
"Which method to use to update vertex data",
"map,subdata");
options_["update-fraction"] = Scene::Option("update-fraction", "1.0",
"The fraction of the mesh length that is updated at every iteration (0.0-1.0)");
options_["update-dispersion"] = Scene::Option("update-dispersion", "0.0",
"How dispersed the updates are [0.0 - 1.0]");
options_["columns"] = Scene::Option("columns", "100",
"The number of mesh subdivisions length-wise");
options_["rows"] = Scene::Option("rows", "20",
"The number of mesh subdisivisions width-wise");
options_["buffer-usage"] = Scene::Option("buffer-usage", "static",
"How the buffer will be used",
"static,stream,dynamic");
}
SceneBuffer::~SceneBuffer()
{
delete priv_;
}
bool
SceneBuffer::supported(bool show_errors)
{
if (options_["update-method"].value != "subdata" &&
(GLExtensions::MapBuffer == 0 || GLExtensions::UnmapBuffer == 0))
{
if (show_errors) {
Log::error("Requested MapBuffer VBO update method but GL_OES_mapbuffer"
" is not supported!\n");
}
return false;
}
return true;
}
bool
SceneBuffer::load()
{
running_ = false;
return true;
}
void
SceneBuffer::unload()
{
}
bool
SceneBuffer::setup()
{
using LibMatrix::vec3;
if (!Scene::setup())
return false;
bool interleave = (options_["interleave"].value == "true");
Mesh::VBOUpdateMethod update_method;
Mesh::VBOUsage usage;
double update_fraction;
double update_dispersion;
size_t nlength;
size_t nwidth;
if (options_["update-method"].value == "map")
update_method = Mesh::VBOUpdateMethodMap;
else if (options_["update-method"].value == "subdata")
update_method = Mesh::VBOUpdateMethodSubData;
else
update_method = Mesh::VBOUpdateMethodMap;
if (options_["buffer-usage"].value == "static")
usage = Mesh::VBOUsageStatic;
else if (options_["buffer-usage"].value == "stream")
usage = Mesh::VBOUsageStream;
else
usage = Mesh::VBOUsageDynamic;
update_fraction = Util::fromString<double>(options_["update-fraction"].value);
update_dispersion = Util::fromString<double>(options_["update-dispersion"].value);
nlength = Util::fromString<size_t>(options_["columns"].value);
nwidth = Util::fromString<size_t>(options_["rows"].value);
priv_->wave = new WaveMesh(5.0, 2.0, nlength, nwidth,
update_fraction * (1.0 - update_dispersion + 0.0001),
update_fraction);
priv_->wave->mesh().interleave(interleave);
priv_->wave->mesh().vbo_update_method(update_method);
priv_->wave->mesh().vbo_usage(usage);
priv_->wave->mesh().build_vbo();
priv_->wave->program().start();
priv_->wave->program()["Viewport"] = LibMatrix::vec2(canvas_.width(), canvas_.height());
glDisable(GL_CULL_FACE);
currentFrame_ = 0;
running_ = true;
startTime_ = Util::get_timestamp_us() / 1000000.0;
lastUpdateTime_ = startTime_;
return true;
}
void
SceneBuffer::teardown()
{
delete priv_->wave;
priv_->wave = 0;
glEnable(GL_CULL_FACE);
Scene::teardown();
}
void
SceneBuffer::update()
{
Scene::update();
double elapsed_time = lastUpdateTime_ - startTime_;
priv_->wave->update(elapsed_time);
}
void
SceneBuffer::draw()
{
LibMatrix::Stack4 model_view;
// Load the ModelViewProjectionMatrix uniform in the shader
LibMatrix::mat4 model_view_proj(canvas_.projection());
model_view.translate(0.0, 0.0, -4.0);
model_view.rotate(45.0, -1.0, 0.0, 0.0);
model_view_proj *= model_view.getCurrent();
priv_->wave->program()["ModelViewProjectionMatrix"] = model_view_proj;
priv_->wave->mesh().render_vbo();
}
Scene::ValidationResult
SceneBuffer::validate()
{
static const double radius_3d(std::sqrt(3.0 * 2.0 * 2.0));
Canvas::Pixel ref(0x34, 0x99, 0xd7, 0xff);
Canvas::Pixel pixel = canvas_.read_pixel(402, 189);
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;
}