From patchwork Tue Aug 2 14:42:26 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: alexandros.frantzis@linaro.org X-Patchwork-Id: 3219 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 05B3923F3F for ; Tue, 2 Aug 2011 14:42:30 +0000 (UTC) Received: from mail-qy0-f180.google.com (mail-qy0-f180.google.com [209.85.216.180]) by fiordland.canonical.com (Postfix) with ESMTP id 7D554A188D7 for ; Tue, 2 Aug 2011 14:42:29 +0000 (UTC) Received: by qyk30 with SMTP id 30so4792498qyk.11 for ; Tue, 02 Aug 2011 07:42:29 -0700 (PDT) Received: by 10.229.241.19 with SMTP id lc19mr2618774qcb.45.1312296148888; Tue, 02 Aug 2011 07:42:28 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.229.6.73 with SMTP id 9cs126134qcy; Tue, 2 Aug 2011 07:42:28 -0700 (PDT) Received: by 10.216.162.211 with SMTP id y61mr1921105wek.19.1312296147091; Tue, 02 Aug 2011 07:42:27 -0700 (PDT) Received: from adelie.canonical.com (adelie.canonical.com [91.189.90.139]) by mx.google.com with ESMTP id l37si11445426weq.108.2011.08.02.07.42.26; Tue, 02 Aug 2011 07:42:27 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.139 as permitted sender) client-ip=91.189.90.139; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.139 as permitted sender) smtp.mail=bounces@canonical.com Received: from loganberry.canonical.com ([91.189.90.37]) by adelie.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1QoGAs-0002Cp-7s for ; Tue, 02 Aug 2011 14:42:26 +0000 Received: from loganberry.canonical.com (localhost [127.0.0.1]) by loganberry.canonical.com (Postfix) with ESMTP id 379782EA03D for ; Tue, 2 Aug 2011 14:42:26 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: glmark2 X-Launchpad-Branch: ~glmark2-dev/glmark2/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 115 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~glmark2-dev/glmark2/trunk] Rev 115: Implement a benchmarking scene for GPU-based 2D image filtering. Message-Id: <20110802144226.4934.52250.launchpad@loganberry.canonical.com> Date: Tue, 02 Aug 2011 14:42:26 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="13573"; Instance="initZopeless config overlay" X-Launchpad-Hash: b8e88b68614876c564f4c1353a97eb318629bd3f Merge authors: Alexandros Frantzis (afrantzis) ------------------------------------------------------------ revno: 115 [merge] committer: Alexandros Frantzis branch nick: trunk timestamp: Tue 2011-08-02 15:40:55 +0100 message: Implement a benchmarking scene for GPU-based 2D image filtering. added: data/shaders/effect-2d-convolution.frag data/shaders/effect-2d.vert data/textures/effect-2d.png src/scene-effect-2d.cpp modified: src/main.cpp src/scene-build.cpp src/scene-bump.cpp src/scene-shading.cpp src/scene-texture.cpp src/scene.h src/shader-source.cpp src/shader-source.h --- lp:glmark2 https://code.launchpad.net/~glmark2-dev/glmark2/trunk You are subscribed to branch lp:glmark2. To unsubscribe from this branch go to https://code.launchpad.net/~glmark2-dev/glmark2/trunk/+edit-subscription === added file 'data/shaders/effect-2d-convolution.frag' --- data/shaders/effect-2d-convolution.frag 1970-01-01 00:00:00 +0000 +++ data/shaders/effect-2d-convolution.frag 2011-07-28 14:06:56 +0000 @@ -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' --- data/shaders/effect-2d.vert 1970-01-01 00:00:00 +0000 +++ data/shaders/effect-2d.vert 2011-07-28 14:06:56 +0000 @@ -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' --- src/main.cpp 2011-07-19 10:18:35 +0000 +++ src/main.cpp 2011-08-02 14:29:51 +0000 @@ -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' --- src/scene-build.cpp 2011-07-27 15:02:27 +0000 +++ src/scene-build.cpp 2011-07-28 08:44:31 +0000 @@ -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' --- src/scene-bump.cpp 2011-07-27 15:02:27 +0000 +++ src/scene-bump.cpp 2011-07-28 08:44:31 +0000 @@ -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' --- src/scene-effect-2d.cpp 1970-01-01 00:00:00 +0000 +++ src/scene-effect-2d.cpp 2011-08-02 14:28:18 +0000 @@ -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 . + * + * Authors: + * Alexandros Frantzis (glmark2) + */ +#include +#include +#include + +#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(x), + static_cast(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 &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::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 &kernel, + unsigned int width) +{ + std::stringstream ss; + ss << std::fixed; + + for (std::vector::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 &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 &matrix, + unsigned int &width, unsigned int &height) +{ + std::vector rows; + unsigned int w = UINT_MAX; + + split(str, ';', rows); + + Log::debug("Parsing kernel matrix:\n"); + + for (std::vector::const_iterator iter = rows.begin(); + iter != rows.end(); + iter++) + { + std::vector 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::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 &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::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::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 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 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 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' --- src/scene-shading.cpp 2011-07-27 15:02:27 +0000 +++ src/scene-shading.cpp 2011-07-28 08:44:31 +0000 @@ -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' --- src/scene-texture.cpp 2011-07-27 15:02:27 +0000 +++ src/scene-texture.cpp 2011-07-28 08:44:31 +0000 @@ -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' --- src/scene.h 2011-07-19 10:09:30 +0000 +++ src/scene.h 2011-07-28 08:44:31 +0000 @@ -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' --- src/shader-source.cpp 2011-07-27 14:43:40 +0000 +++ src/shader-source.cpp 2011-07-28 08:44:31 +0000 @@ -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 &array, + const std::string &function) +{ + std::stringstream ss; + + ss << "const float " << name << "[" << array.size() << "] = {" << std::fixed; + for(std::vector::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 &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::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' --- src/shader-source.h 2011-07-27 14:43:40 +0000 +++ src/shader-source.h 2011-07-28 08:44:31 +0000 @@ -22,7 +22,9 @@ #include #include +#include #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 &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 &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_;