=== added file 'data/shaders/effect-2d-convolution.frag'
@@ -0,0 +1,16 @@
+#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/effect-2d.vert'
@@ -0,0 +1,10 @@
+attribute vec3 position;
+
+varying vec2 TextureCoord;
+
+void main(void)
+{
+ gl_Position = vec4(position, 1.0);
+
+ TextureCoord = position.xy * 0.5 + 0.5;
+}
=== added file 'data/textures/effect-2d.png'
Binary files data/textures/effect-2d.png 1970-01-01 00:00:00 +0000 and data/textures/effect-2d.png 2011-07-28 14:06:56 +0000 differ
=== modified file 'src/main.cpp'
@@ -50,6 +50,8 @@
"shading:shading=phong",
"bump:bump-render=high-poly",
"bump:bump-render=normals",
+ "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;",
"conditionals:vertex-steps=0:fragment-steps=0",
"conditionals:vertex-steps=0:fragment-steps=5",
"conditionals:vertex-steps=5:fragment-steps=0",
@@ -221,6 +223,7 @@
Benchmark::register_scene(*new SceneFunction(canvas));
Benchmark::register_scene(*new SceneLoop(canvas));
Benchmark::register_scene(*new SceneBump(canvas));
+ Benchmark::register_scene(*new SceneEffect2D(canvas));
if (Options::list_scenes) {
list_scenes();
=== modified file 'src/scene-build.cpp'
@@ -64,8 +64,8 @@
ShaderSource vtx_source(vtx_shader_filename);
ShaderSource frg_source(frg_shader_filename);
- vtx_source.add_global_const("LightSourcePosition", lightPosition);
- vtx_source.add_global_const("MaterialDiffuse", materialDiffuse);
+ vtx_source.add_const("LightSourcePosition", lightPosition);
+ vtx_source.add_const("MaterialDiffuse", materialDiffuse);
if (!Scene::load_shaders_from_strings(mProgram, vtx_source.str(),
frg_source.str()))
=== modified file 'src/scene-bump.cpp'
@@ -86,8 +86,8 @@
ShaderSource frg_source(frg_shader_filename);
/* Add constants to shaders */
- frg_source.add_global_const("LightSourcePosition", lightPosition);
- frg_source.add_global_const("LightSourceHalfVector", halfVector);
+ frg_source.add_const("LightSourcePosition", lightPosition);
+ frg_source.add_const("LightSourceHalfVector", halfVector);
if (!Scene::load_shaders_from_strings(mProgram, vtx_source.str(),
frg_source.str()))
@@ -133,8 +133,8 @@
ShaderSource frg_source(frg_shader_filename);
/* Add constants to shaders */
- frg_source.add_global_const("LightSourcePosition", lightPosition);
- frg_source.add_global_const("LightSourceHalfVector", halfVector);
+ frg_source.add_const("LightSourcePosition", lightPosition);
+ frg_source.add_const("LightSourceHalfVector", halfVector);
if (!Scene::load_shaders_from_strings(mProgram, vtx_source.str(),
frg_source.str()))
=== added file 'src/scene-effect-2d.cpp'
@@ -0,0 +1,405 @@
+/*
+ * 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 <climits>
+#include <numeric>
+
+#include "scene.h"
+#include "mat.h"
+#include "stack.h"
+#include "vec.h"
+#include "log.h"
+#include "program.h"
+#include "shader-source.h"
+
+
+SceneEffect2D::SceneEffect2D(Canvas &pCanvas) :
+ Scene(pCanvas, "effect2d")
+{
+ mOptions["kernel"] = Scene::Option("kernel",
+ "0,0,0;0,1,0;0,0,0",
+ "The convolution kernel matrix to use [format: \"a,b,c...;d,e,f...\"");;
+ mOptions["normalize"] = Scene::Option("normalize", "true",
+ "Whether to normalize the supplied convolution kernel matrix [true,false]");
+}
+
+SceneEffect2D::~SceneEffect2D()
+{
+}
+
+/*
+ * Calculates the offset of the coefficient with index i
+ * from the center of the kernel matrix. Note that we are
+ * using the standard OpenGL texture coordinate system
+ * (x grows rightwards, y grows upwards).
+ */
+static LibMatrix::vec2
+calc_offset(unsigned int i, unsigned int width, unsigned int height)
+{
+ int x = i % width - (width - 1) / 2;
+ int y = -(i / width - (height - 1) / 2);
+
+ return LibMatrix::vec2(static_cast<float>(x),
+ static_cast<float>(y));
+
+}
+
+/**
+ * Creates a fragment shader implementing 2D image convolution.
+ *
+ * In the mathematical definition of 2D convolution, the kernel/filter (2D
+ * impulse response) is essentially mirrored in both directions (that is,
+ * rotated 180 degrees) when being applied on a 2D block of data (eg pixels).
+ *
+ * Most image manipulation programs, however, use the term kernel/filter to
+ * describe a 180 degree rotation of the 2D impulse response. This is more
+ * intuitive from a human understanding perspective because this rotated matrix
+ * can be regarded as a stencil that can be directly applied by just "placing"
+ * it on the image.
+ *
+ * In order to be compatible with image manipulation programs, we will
+ * use the same definition of kernel/filter (180 degree rotation of impulse
+ * response). This also means that we don't need to perform the (implicit)
+ * rotation of the kernel in our convolution implementation.
+ *
+ * @param array the array holding the filter coefficients in row-major
+ * order
+ * @param width the width of the filter
+ * @param width the height of the filter
+ *
+ * @return a string containing the frament source code
+ */
+static std::string
+create_convolution_fragment_shader(std::vector<float> &array,
+ unsigned int width, unsigned int height)
+{
+ static const std::string frg_shader_filename(GLMARK_DATA_PATH"/shaders/effect-2d-convolution.frag");
+ ShaderSource source(frg_shader_filename);
+
+ if (width * height != array.size()) {
+ Log::error("Convolution filter size doesn't match supplied dimensions\n");
+ return "";
+ }
+
+ /* Steps are needed to be able to access nearby pixels */
+ source.add_const("TextureStepX", 1.0f/800.0f);
+ source.add_const("TextureStepY", 1.0f/600.0f);
+
+ std::stringstream ss_def;
+ std::stringstream ss_convolution;
+
+ /* Set up stringstream floating point options */
+ ss_def << std::fixed;
+ ss_convolution.precision(1);
+ ss_convolution << std::fixed;
+
+ ss_convolution << "result = ";
+
+ for(std::vector<float>::const_iterator iter = array.begin();
+ iter != array.end();
+ iter++)
+ {
+ unsigned int i = iter - array.begin();
+
+ /* Add Filter coefficient const definitions */
+ ss_def << "const float Kernel" << i << " = "
+ << *iter << ";" << std::endl;
+
+ /* Add convolution term using the current filter coefficient */
+ LibMatrix::vec2 offset(calc_offset(i, width, height));
+ ss_convolution << "texture2D(Texture0, TextureCoord + vec2("
+ << offset.x() << " * TextureStepX, "
+ << offset.y() << " * TextureStepY)) * Kernel" << i;
+ if (iter + 1 != array.end())
+ ss_convolution << " +" << std::endl;
+ }
+
+ ss_convolution << ";" << std::endl;
+
+ source.add(ss_def.str());
+ source.replace("$CONVOLUTION$", ss_convolution.str());
+
+ return source.str();
+}
+
+/**
+ * Creates a string containing a printout of a kernel matrix.
+ *
+ * @param filter the vector containing the filter coefficients
+ * @param width the width of the filter
+ *
+ * @return the printout
+ */
+static std::string
+kernel_printout(const std::vector<float> &kernel,
+ unsigned int width)
+{
+ std::stringstream ss;
+ ss << std::fixed;
+
+ for (std::vector<float>::const_iterator iter = kernel.begin();
+ iter != kernel.end();
+ iter++)
+ {
+ ss << *iter << " ";
+ if ((iter - kernel.begin()) % width == width - 1)
+ ss << std::endl;
+ }
+
+ return ss.str();
+}
+
+/**
+ * Splits a string using a delimiter
+ *
+ * @param s the string to split
+ * @param delim the delimitir to use
+ * @param elems the string vector to populate
+ */
+static void
+split(const std::string &s, char delim, std::vector<std::string> &elems)
+{
+ std::stringstream ss(s);
+
+ std::string item;
+ while(std::getline(ss, item, delim))
+ elems.push_back(item);
+}
+
+/**
+ * Parses a string representation of a matrix and returns it
+ * in row-major format.
+ *
+ * In the string representation, elements are delimited using
+ * commas (',') and rows are delimited using semi-colons (';').
+ * eg 0,0,0;0,1.0,0;0,0,0
+ *
+ * @param str the matrix string representation to parse
+ * @param matrix the float vector to populate
+ * @param[out] width the width of the matrix
+ * @param[out] height the height of the matrix
+ *
+ * @return whether parsing succeeded
+ */
+static bool
+parse_matrix(std::string &str, std::vector<float> &matrix,
+ unsigned int &width, unsigned int &height)
+{
+ std::vector<std::string> rows;
+ unsigned int w = UINT_MAX;
+
+ split(str, ';', rows);
+
+ Log::debug("Parsing kernel matrix:\n");
+
+ for (std::vector<std::string>::const_iterator iter = rows.begin();
+ iter != rows.end();
+ iter++)
+ {
+ std::vector<std::string> elems;
+ split(*iter, ',', elems);
+
+ if (w != UINT_MAX && elems.size() != w) {
+ Log::error("Matrix row %u contains %u elements, whereas previous"
+ " rows had %u\n",
+ iter - rows.begin(), elems.size(), w);
+ return false;
+ }
+
+ w = elems.size();
+
+ for (std::vector<std::string>::const_iterator iter_el = elems.begin();
+ iter_el != elems.end();
+ iter_el++)
+ {
+ std::stringstream ss(*iter_el);
+ float f;
+
+ ss >> f;
+ matrix.push_back(f);
+ Log::debug("%f ", f);
+ }
+
+ Log::debug("\n");
+ }
+
+ width = w;
+ height = rows.size();
+
+ return true;
+}
+
+/**
+ * Normalizes a convolution kernel matrix.
+ *
+ * @param filter the filter to normalize
+ */
+static void
+normalize(std::vector<float> &kernel)
+{
+ float sum = std::accumulate(kernel.begin(), kernel.end(), 0.0);
+
+ /*
+ * If sum is essentially zero, perform a zero-sum normalization.
+ * This normalizes positive and negative values separately,
+ */
+ if (fabs(sum) < 0.00000001) {
+ sum = 0.0;
+ for (std::vector<float>::iterator iter = kernel.begin();
+ iter != kernel.end();
+ iter++)
+ {
+ if (*iter > 0.0)
+ sum += *iter;
+ }
+ }
+
+ /*
+ * We can simply compare with 0.0f here, because we just care about
+ * avoiding division-by-zero.
+ */
+ if (sum == 0.0)
+ return;
+
+ for (std::vector<float>::iterator iter = kernel.begin();
+ iter != kernel.end();
+ iter++)
+ {
+ *iter /= sum;
+ }
+
+}
+
+int SceneEffect2D::load()
+{
+ Texture::load(GLMARK_DATA_PATH"/textures/effect-2d.png", &texture_,
+ GL_NEAREST, GL_NEAREST, 0);
+ mRunning = false;
+
+ return 1;
+}
+
+void SceneEffect2D::unload()
+{
+ glDeleteTextures(1, &texture_);
+}
+
+void SceneEffect2D::setup()
+{
+ Scene::setup();
+
+ static const std::string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/effect-2d.vert");
+
+ std::vector<float> kernel;
+ unsigned int kernel_width;
+ unsigned int kernel_height;
+
+ /* Parse the kernel matrix from the options */
+ if (!parse_matrix(mOptions["kernel"].value, kernel,
+ kernel_width, kernel_height))
+ {
+ return;
+ }
+
+ /* Normalize the kernel matrix if needed */
+ if (mOptions["normalize"].value == "true") {
+ normalize(kernel);
+ Log::debug("Normalized kernel matrix:\n%s",
+ kernel_printout(kernel, kernel_width).c_str());
+ }
+
+ /* Create and load the shaders */
+ ShaderSource vtx_source(vtx_shader_filename);
+ ShaderSource frg_source;
+ frg_source.append(create_convolution_fragment_shader(kernel,
+ kernel_width,
+ kernel_height));
+
+ if (frg_source.str().empty())
+ return;
+
+ if (!Scene::load_shaders_from_strings(program_, vtx_source.str(),
+ frg_source.str()))
+ {
+ return;
+ }
+
+ std::vector<int> vertex_format;
+ vertex_format.push_back(3);
+ mesh_.set_vertex_format(vertex_format);
+
+ mesh_.make_grid(1, 1, 2.0, 2.0, 0.0);
+ mesh_.build_vbo();
+
+ std::vector<GLint> attrib_locations;
+ attrib_locations.push_back(program_.getAttribIndex("position"));
+ mesh_.set_attrib_locations(attrib_locations);
+
+ program_.start();
+
+ // Load texture sampler value
+ program_.loadUniformScalar(0, "Texture0");
+
+ mCurrentFrame = 0;
+ mRunning = true;
+ mStartTime = Scene::get_timestamp_us() / 1000000.0;
+ mLastUpdateTime = mStartTime;
+}
+
+void SceneEffect2D::teardown()
+{
+ mesh_.reset();
+
+ program_.stop();
+ program_.release();
+
+ Scene::teardown();
+}
+
+void SceneEffect2D::update()
+{
+ double current_time = Scene::get_timestamp_us() / 1000000.0;
+ double elapsed_time = current_time - mStartTime;
+
+ mLastUpdateTime = current_time;
+
+ if (elapsed_time >= mDuration) {
+ mAverageFPS = mCurrentFrame / elapsed_time;
+ mRunning = false;
+ }
+
+ mCurrentFrame++;
+}
+
+void SceneEffect2D::draw()
+{
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture_);
+
+ mesh_.render_vbo();
+}
+
+Scene::ValidationResult
+SceneEffect2D::validate()
+{
+ return ValidationUnknown;
+}
=== modified file 'src/scene-shading.cpp'
@@ -106,11 +106,11 @@
ShaderSource frg_source(frg_shader_filename);
// Add constants to shaders
- vtx_source.add_global_const("LightSourcePosition", lightPosition);
- vtx_source.add_global_const("MaterialDiffuse", materialDiffuse);
+ vtx_source.add_const("LightSourcePosition", lightPosition);
+ vtx_source.add_const("MaterialDiffuse", materialDiffuse);
- frg_source.add_global_const("LightSourcePosition", lightPosition);
- frg_source.add_global_const("LightSourceHalfVector", halfVector);
+ frg_source.add_const("LightSourcePosition", lightPosition);
+ frg_source.add_const("LightSourceHalfVector", halfVector);
if (!Scene::load_shaders_from_strings(mProgram, vtx_source.str(),
frg_source.str()))
=== modified file 'src/scene-texture.cpp'
@@ -63,8 +63,8 @@
ShaderSource frg_source(frg_shader_filename);
// Add constants to shaders
- vtx_source.add_global_const("LightSourcePosition", lightPosition);
- vtx_source.add_global_const("MaterialDiffuse", materialDiffuse);
+ vtx_source.add_const("LightSourcePosition", lightPosition);
+ vtx_source.add_const("MaterialDiffuse", materialDiffuse);
if (!Scene::load_shaders_from_strings(mProgram, vtx_source.str(),
frg_source.str()))
=== modified file 'src/scene.h'
@@ -280,4 +280,24 @@
void setup_model_normals();
};
+class SceneEffect2D : public Scene
+{
+public:
+ SceneEffect2D(Canvas &pCanvas);
+ int load();
+ void unload();
+ void setup();
+ void teardown();
+ void update();
+ void draw();
+ ValidationResult validate();
+
+ ~SceneEffect2D();
+
+protected:
+ Program program_;
+
+ Mesh mesh_;
+ GLuint texture_;
+};
#endif
=== modified file 'src/shader-source.cpp'
@@ -156,35 +156,195 @@
}
/**
- * Adds a global (per shader) vec3 constant definition.
- *
- * @param name the name of the constant
- * @param v the value of the constant
- */
-void
-ShaderSource::add_global_const(const std::string &name, const LibMatrix::vec3 &v)
+ * Adds a string (usually containing a constant definition) at
+ * global (per shader) scope.
+ *
+ * The string is placed after any default precision qualifiers.
+ *
+ * @param function the function to add the string into
+ * @param str the string to add
+ */
+void
+ShaderSource::add_local(const std::string &str, const std::string &function)
+{
+ std::string::size_type pos = 0;
+ std::string source(source_.str());
+
+ /* Find the function */
+ pos = source.find(function);
+ pos = source.find('{', pos);
+
+ /* Go to the next line */
+ pos = source.find("\n", pos);
+ if (pos != std::string::npos)
+ pos++;
+
+ source.insert(pos, str);
+
+ source_.clear();
+ source_.str(source);
+}
+
+/**
+ * Adds a string (usually containing a constant definition) to a shader source
+ *
+ * If the function parameter is empty, the string will be added to global
+ * scope, after any precision definitions.
+ *
+ * @param str the string to add
+ * @param function if not empty, the function to add the string into
+ */
+void
+ShaderSource::add(const std::string &str, const std::string &function)
+{
+ if (!function.empty())
+ add_local(str, function);
+ else
+ add_global(str);
+}
+
+/**
+ * Adds a float constant definition.
+ *
+ * @param name the name of the constant
+ * @param f the value of the constant
+ * @param function if not empty, the function to put the definition in
+ */
+void
+ShaderSource::add_const(const std::string &name, float f,
+ const std::string &function)
+{
+ std::stringstream ss;
+
+ ss << "const float " << name << " = " << std::fixed << f << ";" << std::endl;
+
+ add(ss.str(), function);
+}
+
+/**
+ * Adds a float array constant definition.
+ *
+ * Note that various GLSL versions (including ES) don't support
+ * array constants.
+ *
+ * @param name the name of the constant
+ * @param v the value of the constant
+ * @param function if not empty, the function to put the definition in
+ */
+void
+ShaderSource::add_const(const std::string &name, std::vector<float> &array,
+ const std::string &function)
+{
+ std::stringstream ss;
+
+ ss << "const float " << name << "[" << array.size() << "] = {" << std::fixed;
+ for(std::vector<float>::const_iterator iter = array.begin();
+ iter != array.end();
+ iter++)
+ {
+ ss << *iter;
+ if (iter + 1 != array.end())
+ ss << ", " << std::endl;
+ }
+
+ ss << "};" << std::endl;
+
+ add(ss.str(), function);
+}
+
+/**
+ * Adds a vec3 constant definition.
+ *
+ * @param name the name of the constant
+ * @param v the value of the constant
+ * @param function if not empty, the function to put the definition in
+ */
+void
+ShaderSource::add_const(const std::string &name, const LibMatrix::vec3 &v,
+ const std::string &function)
{
std::stringstream ss;
ss << "const vec3 " << name << " = vec3(" << std::fixed;
ss << v.x() << ", " << v.y() << ", " << v.z() << ");" << std::endl;
- add_global(ss.str());
+ add(ss.str(), function);
}
/**
- * Adds a global (per shader) vec4 constant definition.
+ * Adds a vec4 constant definition.
*
* @param name the name of the constant
* @param v the value of the constant
+ * @param function if not empty, the function to put the definition in
*/
void
-ShaderSource::add_global_const(const std::string &name, const LibMatrix::vec4 &v)
+ShaderSource::add_const(const std::string &name, const LibMatrix::vec4 &v,
+ const std::string &function)
{
std::stringstream ss;
ss << "const vec4 " << name << " = vec4(" << std::fixed;
ss << v.x() << ", " << v.y() << ", " << v.z() << ", " << v.w() << ");" << std::endl;
- add_global(ss.str());
+ add(ss.str(), function);
+}
+
+/**
+ * Adds a mat3 constant definition.
+ *
+ * @param name the name of the constant
+ * @param v the value of the constant
+ * @param function if not empty, the function to put the definition in
+ */
+void
+ShaderSource::add_const(const std::string &name, const LibMatrix::mat3 &m,
+ const std::string &function)
+{
+ std::stringstream ss;
+
+ ss << "const mat3 " << name << " = mat3(" << std::fixed;
+ ss << m[0][0] << ", " << m[1][0] << ", " << m[2][0] << "," << std::endl;
+ ss << m[0][1] << ", " << m[1][1] << ", " << m[2][1] << "," << std::endl;
+ ss << m[0][2] << ", " << m[1][2] << ", " << m[2][2] << std::endl;
+ ss << ");" << std::endl;
+
+ add(ss.str(), function);
+}
+
+/**
+ * Adds a float array declaration and initialization.
+ *
+ * @param name the name of the array
+ * @param array the array values
+ * @param init_function the function to put the initialization in
+ * @param decl_function if not empty, the function to put the declaration in
+ */
+void
+ShaderSource::add_array(const std::string &name, std::vector<float> &array,
+ const std::string &init_function,
+ const std::string &decl_function)
+{
+ if (init_function.empty() || name.empty())
+ return;
+
+ std::stringstream ss;
+ ss << "float " << name << "[" << array.size() << "];" << std::endl;
+
+ std::string decl(ss.str());
+
+ ss.clear();
+ ss.str("");
+ ss << std::fixed;
+
+ for(std::vector<float>::const_iterator iter = array.begin();
+ iter != array.end();
+ iter++)
+ {
+ ss << name << "[" << iter - array.begin() << "] = " << *iter << ";" << std::endl;
+ }
+
+ add(ss.str(), init_function);
+
+ add(decl, decl_function);
}
=== modified file 'src/shader-source.h'
@@ -22,7 +22,9 @@
#include <string>
#include <sstream>
+#include <vector>
#include "vec.h"
+#include "mat.h"
/**
* Helper class for loading and manipulating shader sources.
@@ -39,13 +41,28 @@
void replace(const std::string &remove, const std::string &insert);
void replace_with_file(const std::string &remove, const std::string &filename);
- void add_global(const std::string &str);
- void add_global_const(const std::string &name, const LibMatrix::vec3 &v);
- void add_global_const(const std::string &name, const LibMatrix::vec4 &v);
+ void add(const std::string &str, const std::string &function = "");
+
+ void add_const(const std::string &name, float f,
+ const std::string &function = "");
+ void add_const(const std::string &name, std::vector<float> &f,
+ const std::string &function = "");
+ void add_const(const std::string &name, const LibMatrix::vec3 &v,
+ const std::string &function = "");
+ void add_const(const std::string &name, const LibMatrix::vec4 &v,
+ const std::string &function = "");
+ void add_const(const std::string &name, const LibMatrix::mat3 &m,
+ const std::string &function = "");
+
+ void add_array(const std::string &name, std::vector<float> &array,
+ const std::string &init_function,
+ const std::string &decl_function = "");
std::string str() { return source_.str(); }
private:
+ void add_global(const std::string &str);
+ void add_local(const std::string &str, const std::string &function);
bool load_file(const std::string& filename, std::string& str);
std::stringstream source_;