[Branch,~jesse-barker/libmatrix/trunk] Rev 30: Merge lp:~jesse-barker/libmatrix/util into trunk.

Message ID 20120126161018.19837.76399.launchpad@ackee.canonical.com
State Accepted
Headers show

Commit Message

Jesse Barker Jan. 26, 2012, 4:10 p.m.
Merge authors:
  Jesse Barker (jesse-barker)
Related merge proposals:
  https://code.launchpad.net/~jesse-barker/libmatrix/util/+merge/88434
  proposed by: Jesse Barker (jesse-barker)
------------------------------------------------------------
revno: 30 [merge]
committer: Jesse Barker <jesse.barker@linaro.org>
branch nick: trunk
timestamp: Thu 2012-01-26 08:07:15 -0800
message:
  Merge lp:~jesse-barker/libmatrix/util into trunk.
  
  Adds common utility objects like logging and shader source management previously duplicated across other projects.
added:
  gl-if.h
  log.cc
  log.h
  shader-source.cc
  shader-source.h
  test/basic-global-const.vert
  test/basic.frag
  test/basic.vert
  test/shader_source_test.cc
  test/shader_source_test.h
  util.cc
  util.h
modified:
  Makefile
  program.cc
  program.h
  test/libmatrix_test.cc


--
lp:libmatrix
https://code.launchpad.net/~jesse-barker/libmatrix/trunk

You are subscribed to branch lp:libmatrix.
To unsubscribe from this branch go to https://code.launchpad.net/~jesse-barker/libmatrix/trunk/+edit-subscription

Patch

=== modified file 'Makefile'
--- Makefile	2011-09-13 16:33:49 +0000
+++ Makefile	2012-01-23 19:18:34 +0000
@@ -1,6 +1,6 @@ 
 CXXFLAGS = -Wall -Werror -pedantic -O3
 LIBMATRIX = libmatrix.a
-LIBSRCS = mat.cc program.cc
+LIBSRCS = mat.cc program.cc log.cc util.cc shader-source.cc
 LIBOBJS = $(LIBSRCS:.cc=.o)
 TESTDIR = test
 LIBMATRIX_TESTS = $(TESTDIR)/libmatrix_test
@@ -8,6 +8,7 @@ 
            $(TESTDIR)/const_vec_test.cc \
            $(TESTDIR)/inverse_test.cc \
            $(TESTDIR)/transpose_test.cc \
+           $(TESTDIR)/shader_source_test.cc \
            $(TESTDIR)/libmatrix_test.cc
 TESTOBJS = $(TESTSRCS:.cc=.o)
 
@@ -18,7 +19,10 @@ 
 # Main library targets here.
 mat.o : mat.cc mat.h vec.h
 program.o: program.cc program.h mat.h vec.h
-libmatrix.a : mat.o mat.h stack.h vec.h program.o program.h
+log.o: log.cc log.h
+util.o: util.cc util.h
+shader-source.o: shader-source.cc shader-source.h mat.h vec.h
+libmatrix.a : mat.o stack.h program.o log.o util.o shader-source.o
 	$(AR) -r $@  $(LIBOBJS)
 
 # Tests and execution targets here.
@@ -27,6 +31,7 @@ 
 $(TESTDIR)/const_vec_test.o: $(TESTDIR)/const_vec_test.cc $(TESTDIR)/const_vec_test.h $(TESTDIR)/libmatrix_test.h vec.h
 $(TESTDIR)/inverse_test.o: $(TESTDIR)/inverse_test.cc $(TESTDIR)/inverse_test.h $(TESTDIR)/libmatrix_test.h mat.h
 $(TESTDIR)/transpose_test.o: $(TESTDIR)/transpose_test.cc $(TESTDIR)/transpose_test.h $(TESTDIR)/libmatrix_test.h mat.h
+$(TESTDIR)/shader_source_test.o: $(TESTDIR)/shader_source_test.cc $(TESTDIR)/shader_source_test.h $(TESTDIR)/libmatrix_test.h shader-source.h
 $(TESTDIR)/libmatrix_test: $(TESTOBJS) libmatrix.a
 	$(CXX) -o $@ $^
 run_tests: $(LIBMATRIX_TESTS)

=== added file 'gl-if.h'
--- gl-if.h	1970-01-01 00:00:00 +0000
+++ gl-if.h	2012-01-26 15:58:52 +0000
@@ -0,0 +1,18 @@ 
+//
+// Copyright (c) 2012 Linaro Limited
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License which accompanies
+// this distribution, and is available at
+// http://www.opensource.org/licenses/mit-license.php
+//
+// Contributors:
+//     Jesse Barker - original implementation.
+//
+#ifndef GL_IF_H_
+#define GL_IF_H_
+// Inclusion abstraction to provide project specific interface headers for
+// whatever flavor of OpenGL(|ES) is appropriate.  For core libmatrix, this
+// is GLEW.
+#include <GL/glew.h>
+#endif // GL_IF_H_

=== added file 'log.cc'
--- log.cc	1970-01-01 00:00:00 +0000
+++ log.cc	2012-01-13 19:35:52 +0000
@@ -0,0 +1,176 @@ 
+//
+// Copyright (c) 2010-2011 Linaro Limited
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License which accompanies
+// this distribution, and is available at
+// http://www.opensource.org/licenses/mit-license.php
+//
+// Contributors:
+//     Alexandros Frantzis <alexandros.frantzis@linaro.org>
+//     Jesse Barker <jesse.barker@linaro.org>
+//
+#include <cstdio>
+#include <cstdarg>
+#include <string>
+#include <sstream>
+#include <iostream>
+#include "log.h"
+
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+
+using std::string;
+
+const string Log::continuation_prefix("\x10");
+string Log::appname;
+bool Log::do_debug_(false);
+
+#ifndef ANDROID
+
+static const string terminal_color_normal("\033[0m");
+static const string terminal_color_red("\033[1;31m");
+static const string terminal_color_cyan("\033[36m");
+static const string terminal_color_yellow("\033[33m");
+static const string empty;
+
+static void
+print_prefixed_message(std::ostream& stream, const string& color, const string& prefix,
+                       const string& fmt, va_list ap)
+{
+    va_list aq;
+
+    /* Estimate message size */
+    va_copy(aq, ap);
+    int msg_size = vsnprintf(NULL, 0, fmt.c_str(), aq);
+    va_end(aq);
+
+    /* Create the buffer to hold the message */
+    char *buf = new char[msg_size + 1];
+
+    /* Store the message in the buffer */
+    va_copy(aq, ap);
+    vsnprintf(buf, msg_size + 1, fmt.c_str(), aq);
+    va_end(aq);
+
+    /*
+     * Print the message lines prefixed with the supplied prefix.
+     * If the target stream is a terminal make the prefix colored.
+     */
+    string start_color;
+    string end_color;
+    static const string colon(": ");
+    if (!color.empty())
+    {
+        start_color = color;
+        if (color[0] != 0)
+        {
+            end_color = terminal_color_normal;
+        }
+    }
+
+    std::string line;
+    std::stringstream ss(buf);
+
+    while(std::getline(ss, line)) {
+        /*
+         * If this line is a continuation of a previous log message
+         * just print the line plainly.
+         */
+        if (line[0] == Log::continuation_prefix[0]) {
+            stream << line.c_str() + 1;
+        }
+        else {
+            /* Normal line, emit the prefix. */
+            stream << start_color << prefix << end_color << colon << line;
+        }
+
+        /* Only emit a newline if the original message has it. */
+        if (!(ss.rdstate() & std::stringstream::eofbit))
+            stream << std::endl;
+    }
+
+    delete[] buf;
+}
+
+void
+Log::info(const char *fmt, ...)
+{
+    static const string infoprefix("Info");
+    static const string& infocolor(isatty(fileno(stdout)) ? terminal_color_cyan : empty);
+    va_list ap;
+    va_start(ap, fmt);
+    if (do_debug_)
+        print_prefixed_message(std::cout, infocolor, infoprefix, fmt, ap);
+    else
+        print_prefixed_message(std::cout, empty, empty, fmt, ap);
+    va_end(ap);
+}
+
+void
+Log::debug(const char *fmt, ...)
+{
+    static const string dbgprefix("Debug");
+    static const string& dbgcolor(isatty(fileno(stdout)) ? terminal_color_yellow : empty);
+    if (!do_debug_)
+        return;
+    va_list ap;
+    va_start(ap, fmt);
+    print_prefixed_message(std::cout, dbgcolor, dbgprefix, fmt, ap);
+    va_end(ap);
+}
+
+void
+Log::error(const char *fmt, ...)
+{
+    static const string errprefix("Error");
+    static const string& errcolor(isatty(fileno(stderr)) ? terminal_color_red : empty);
+    va_list ap;
+    va_start(ap, fmt);
+    print_prefixed_message(std::cerr, errcolor, errprefix, fmt, ap);
+    va_end(ap);
+}
+
+void
+Log::flush()
+{
+    std::cout.flush();
+    std::cerr.flush();
+}
+#else
+void
+Log::info(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    __android_log_vprint(ANDROID_LOG_INFO, appname.c_str(), fmt, ap);
+    va_end(ap);
+}q
+
+void
+Log::debug(const char *fmt, ...)
+{
+    if (!Options::show_debug)
+        return;
+    va_list ap;
+    va_start(ap, fmt);
+    __android_log_vprint(ANDROID_LOG_DEBUG, appname.c_str(), fmt, ap);
+    va_end(ap);
+}
+
+void
+Log::error(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    __android_log_vprint(ANDROID_LOG_ERROR, appname.c_str(), fmt, ap);
+    va_end(ap);
+}
+
+void
+Log::flush()
+{
+}
+
+#endif

=== added file 'log.h'
--- log.h	1970-01-01 00:00:00 +0000
+++ log.h	2011-12-08 22:08:56 +0000
@@ -0,0 +1,39 @@ 
+//
+// Copyright (c) 2010-2011 Linaro Limited
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License which accompanies
+// this distribution, and is available at
+// http://www.opensource.org/licenses/mit-license.php
+//
+// Contributors:
+//     Alexandros Frantzis <alexandros.frantzis@linaro.org>
+//     Jesse Barker <jesse.barker@linaro.org>
+//
+#ifndef LOG_H_
+#define LOG_H_
+
+class Log
+{
+public:
+    static void init(bool do_debug = false) { do_debug_ = do_debug; }
+    // Emit an informational message
+    static void info(const char *fmt, ...);
+    // Emit a debugging message
+    static void debug(const char *fmt, ...);
+    // Emit an error message
+    static void error(const char *fmt, ...);
+    // Explicit flush of the log buffer
+    static void flush();
+    // A prefix constant that informs the logging infrastructure that the log
+    // message is a continuation of a previous log message to be put on the
+    // same line.
+    static const std::string continuation_prefix;
+    // A constant for identifying the log messages as originating from a
+    // particular application.
+    static std::string appname;
+private:
+    static bool do_debug_;
+};
+
+#endif /* LOG_H_ */

=== modified file 'program.cc'
--- program.cc	2011-08-31 21:00:19 +0000
+++ program.cc	2012-01-26 15:55:55 +0000
@@ -14,7 +14,7 @@ 
 #include <sstream>
 #include <fstream>
 #include <iostream>
-#include <GL/glew.h>
+#include "gl-if.h"
 #include "program.h"
 
 using std::string;
@@ -23,27 +23,6 @@ 
 using LibMatrix::vec3;
 using LibMatrix::vec4;
 
-bool
-gotSource(const string& filename, string& source)
-{
-    using std::ifstream;
-    ifstream inputFile(filename.c_str());
-    if (!inputFile)
-    {
-        std::cerr << "Failed to open \"" << filename << "\"" << std::endl;
-        return false;
-    }
-
-    string curLine;
-    while (getline(inputFile, curLine))
-    {
-        source += curLine;
-        source += '\n';
-    }
-
-    return true;
-}
-
 Shader::Shader(unsigned int type, const string& source) :
     handle_(0),
     type_(type),

=== modified file 'program.h'
--- program.h	2011-08-31 21:00:19 +0000
+++ program.h	2012-01-25 20:12:21 +0000
@@ -161,7 +161,4 @@ 
     bool valid_;
 };
 
-// Handy utility for extracting shader source from a named file
-bool gotSource(const std::string& filename, std::string& sourceOut);
-
 #endif // PROGRAM_H_

=== added file 'shader-source.cc'
--- shader-source.cc	1970-01-01 00:00:00 +0000
+++ shader-source.cc	2011-12-12 16:26:45 +0000
@@ -0,0 +1,615 @@ 
+//
+// Copyright (c) 2010-2011 Linaro Limited
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License which accompanies
+// this distribution, and is available at
+// http://www.opensource.org/licenses/mit-license.php
+//
+// Contributors:
+//     Alexandros Frantzis <alexandros.frantzis@linaro.org>
+//     Jesse Barker <jesse.barker@linaro.org>
+//
+#include <istream>
+#include <memory>
+
+#include "shader-source.h"
+#include "log.h"
+#include "vec.h"
+#include "util.h"
+
+/**
+ * Holds default precision values for all shader types
+ * (even the unknown type, which is hardwired to default precision values)
+ */
+std::vector<ShaderSource::Precision>
+ShaderSource::default_precision_(ShaderSource::ShaderTypeUnknown + 1);
+
+/**
+ * Loads the contents of a file into a string.
+ *
+ * @param filename the name of the file
+ * @param str the string to put the contents of the file into
+ */
+bool
+ShaderSource::load_file(const std::string& filename, std::string& str)
+{
+    std::auto_ptr<std::istream> is_ptr(Util::get_resource(filename));
+    std::istream& inputFile(*is_ptr);
+
+    if (!inputFile)
+    {
+        Log::error("Failed to open \"%s\"\n", filename.c_str());
+        return false;
+    }
+
+    std::string curLine;
+    while (getline(inputFile, curLine))
+    {
+        str += curLine;
+        str += '\n';
+    }
+
+    return true;
+}
+
+
+/**
+ * Appends a string to the shader source.
+ *
+ * @param str the string to append
+ */
+void
+ShaderSource::append(const std::string &str)
+{
+    source_ << str;
+}
+
+/**
+ * Appends the contents of a file to the shader source.
+ *
+ * @param filename the name of the file to append
+ */
+void
+ShaderSource::append_file(const std::string &filename)
+{
+    std::string source;
+    if (load_file(filename, source))
+        source_ << source;
+}
+
+/**
+ * Replaces a string in the source with another string.
+ *
+ * @param remove the string to replace
+ * @param insert the string to replace with
+ */
+void
+ShaderSource::replace(const std::string &remove, const std::string &insert)
+{
+    std::string::size_type pos = 0;
+    std::string str(source_.str());
+
+    while ((pos = str.find(remove, pos)) != std::string::npos) {
+        str.replace(pos, remove.size(), insert);
+        pos++;
+    }
+
+    source_.clear();
+    source_.str(str);
+}
+
+/**
+ * Replaces a string in the source with the contents of a file.
+ *
+ * @param remove the string to replace
+ * @param filename the name of the file to read from
+ */
+void
+ShaderSource::replace_with_file(const std::string &remove, const std::string &filename)
+{
+    std::string source;
+    if (load_file(filename, source))
+        replace(remove, source);
+}
+
+/**
+ * Adds a string (usually containing a constant definition) at
+ * global (per shader) scope.
+ *
+ * The string is placed after any default precision qualifiers.
+ *
+ * @param str the string to add
+ */
+void
+ShaderSource::add_global(const std::string &str)
+{
+    std::string::size_type pos = 0;
+    std::string source(source_.str());
+
+    /* Find the last precision qualifier */
+    pos = source.rfind("precision");
+
+    if (pos != std::string::npos) {
+        /*
+         * Find the next #endif line of a preprocessor block that contains
+         * the precision qualifier.
+         */
+        std::string::size_type pos_if = source.find("#if", pos);
+        std::string::size_type pos_endif = source.find("#endif", pos);
+
+        if (pos_endif != std::string::npos && pos_endif < pos_if)
+            pos = pos_endif;
+
+        /* Go to the next line */
+        pos = source.find("\n", pos);
+        if (pos != std::string::npos)
+            pos++;
+    }
+    else
+        pos = 0;
+
+    source.insert(pos, str);
+
+    source_.clear();
+    source_.str(source);
+}
+
+/**
+ * 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 vec2 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::vec2 &v,
+                        const std::string &function)
+{
+    std::stringstream ss;
+
+    ss << "const vec2 " << name << " = vec2(" << std::fixed;
+    ss << v.x() << ", " << v.y() << ");" << 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(ss.str(), function);
+}
+
+/**
+ * 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_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(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);
+}
+
+/**
+ * Gets the ShaderType for this ShaderSource.
+ *
+ * If the ShaderType is unknown, an attempt is made to infer
+ * the type from the shader source contents.
+ *
+ * @return the ShaderType
+ */
+ShaderSource::ShaderType
+ShaderSource::type()
+{
+    /* Try to infer the type from the source contents */
+    if (type_ == ShaderSource::ShaderTypeUnknown) {
+        std::string source(source_.str());
+
+        if (source.find("gl_FragColor") != std::string::npos)
+            type_ = ShaderSource::ShaderTypeFragment;
+        else if (source.find("gl_Position") != std::string::npos)
+            type_ = ShaderSource::ShaderTypeVertex;
+        else
+            Log::debug("Cannot infer shader type from contents. Leaving it Unknown.\n");
+    }
+
+    return type_;
+}
+
+/**
+ * Helper function that emits a precision statement.
+ *
+ * @param ss the stringstream to add the statement to
+ * @param val the precision value
+ * @param type_str the variable type to apply the precision value to
+ */
+void
+ShaderSource::emit_precision(std::stringstream& ss, ShaderSource::PrecisionValue val,
+                             const std::string& type_str)
+{
+    static const char *precision_map[] = {
+        "lowp", "mediump", "highp", NULL
+    };
+
+    if (val == ShaderSource::PrecisionValueHigh) {
+        if (type_ == ShaderSource::ShaderTypeFragment)
+            ss << "#ifdef GL_FRAGMENT_PRECISION_HIGH" << std::endl;
+
+        ss << "precision highp " << type_str << ";" << std::endl;
+
+        if (type_ == ShaderSource::ShaderTypeFragment) {
+            ss << "#else" << std::endl;
+            ss << "precision mediump " << type_str << ";" << std::endl;
+            ss << "#endif" << std::endl;
+        }
+    }
+    else if (val >= 0 && val < ShaderSource::PrecisionValueDefault) {
+        ss << "precision " << precision_map[val] << " ";
+        ss << type_str << ";" << std::endl;
+    }
+
+    /* There is no default precision in the fragment shader, so set it to mediump */
+    if (val == ShaderSource::PrecisionValueDefault
+        && type_str == "float" && type_ == ShaderSource::ShaderTypeFragment)
+    {
+        ss << "precision mediump float;" << std::endl;
+    }
+}
+
+/**
+ * Gets a string containing the complete shader source.
+ *
+ * Precision statements are applied at this point.
+ *
+ * @return the shader source
+ */
+std::string
+ShaderSource::str()
+{
+    /* Decide which precision values to use */
+    ShaderSource::Precision precision;
+
+    /* Ensure we have tried to infer the type from the contents */
+    type();
+
+    if (precision_has_been_set_)
+        precision = precision_;
+    else
+        precision = default_precision(type_);
+
+    /* Create the precision statements */
+    std::stringstream ss;
+
+    emit_precision(ss, precision.int_precision, "int");
+    emit_precision(ss, precision.float_precision, "float");
+    emit_precision(ss, precision.sampler2d_precision, "sampler2D");
+    emit_precision(ss, precision.samplercube_precision, "samplerCube");
+
+    std::string precision_str(ss.str());
+    if (!precision_str.empty()) {
+        precision_str.insert(0, "#ifdef GL_ES\n");
+        precision_str.insert(precision_str.size(), "#endif\n");
+    }
+
+    return precision_str + source_.str();
+}
+
+/**
+ * Sets the precision that will be used for this shader.
+ *
+ * This overrides any default values set with ShaderSource::default_*_precision().
+ *
+ * @param precision the precision to set
+ */
+void
+ShaderSource::precision(const ShaderSource::Precision& precision)
+{
+    precision_ = precision;
+    precision_has_been_set_ = true;
+}
+
+/**
+ * Gets the precision that will be used for this shader.
+ *
+ * @return the precision
+ */
+const ShaderSource::Precision&
+ShaderSource::precision()
+{
+    return precision_;
+}
+
+/**
+ * Sets the default precision that will be used for a shaders type.
+ *
+ * If type is ShaderTypeUnknown the supplied precision is used for all
+ * shader types.
+ *
+ * This can be overriden per ShaderSource object by using ::precision().
+ *
+ * @param precision the default precision to set
+ * @param type the ShaderType to use the precision for
+ */
+void
+ShaderSource::default_precision(const ShaderSource::Precision& precision,
+                                ShaderSource::ShaderType type)
+{
+    if (type < 0 || type > ShaderSource::ShaderTypeUnknown)
+        type = ShaderSource::ShaderTypeUnknown;
+
+    if (type == ShaderSource::ShaderTypeUnknown) {
+        for (size_t i = 0; i < ShaderSource::ShaderTypeUnknown; i++)
+            default_precision_[i] = precision;
+    }
+    else {
+        default_precision_[type] = precision;
+    }
+}
+
+/**
+ * Gets the default precision that will be used for a shader type.
+ *
+ * It is valid to use a type of ShaderTypeUnknown. This will always
+ * return a Precision with default values.
+ *
+ * @param type the ShaderType to get the precision of
+ *
+ * @return the precision
+ */
+const ShaderSource::Precision&
+ShaderSource::default_precision(ShaderSource::ShaderType type)
+{
+    if (type < 0 || type > ShaderSource::ShaderTypeUnknown)
+        type = ShaderSource::ShaderTypeUnknown;
+
+    return default_precision_[type];
+}
+
+/****************************************
+ * ShaderSource::Precision constructors *
+ ****************************************/
+
+/**
+ * Creates a ShaderSource::Precision with default precision values.
+ */
+ShaderSource::Precision::Precision() :
+    int_precision(ShaderSource::PrecisionValueDefault),
+    float_precision(ShaderSource::PrecisionValueDefault),
+    sampler2d_precision(ShaderSource::PrecisionValueDefault),
+    samplercube_precision(ShaderSource::PrecisionValueDefault)
+{
+}
+
+/**
+ * Creates a ShaderSource::Precision using the supplied precision values.
+ */
+ShaderSource::Precision::Precision(ShaderSource::PrecisionValue int_p,
+                                   ShaderSource::PrecisionValue float_p,
+                                   ShaderSource::PrecisionValue sampler2d_p,
+                                   ShaderSource::PrecisionValue samplercube_p) :
+    int_precision(int_p), float_precision(float_p),
+    sampler2d_precision(sampler2d_p), samplercube_precision(samplercube_p)
+{
+}
+
+/**
+ * Creates a ShaderSource::Precision from a string representation of
+ * precision values.
+ *
+ * The string format is:
+ * "<int>,<float>,<sampler2d>,<samplercube>"
+ *
+ * Each precision value is one of "high", "medium", "low" or "default".
+ *
+ * @param precision_values the string representation of the precision values
+ */
+ShaderSource::Precision::Precision(const std::string& precision_values) :
+    int_precision(ShaderSource::PrecisionValueDefault),
+    float_precision(ShaderSource::PrecisionValueDefault),
+    sampler2d_precision(ShaderSource::PrecisionValueDefault),
+    samplercube_precision(ShaderSource::PrecisionValueDefault)
+{
+    std::vector<std::string> elems;
+
+    Util::split(precision_values, ',', elems);
+
+    for (size_t i = 0; i < elems.size() && i < 4; i++) {
+        const std::string& pstr(elems[i]);
+        ShaderSource::PrecisionValue pval;
+
+        if (pstr == "high")
+            pval = ShaderSource::PrecisionValueHigh;
+        else if (pstr == "medium")
+            pval = ShaderSource::PrecisionValueMedium;
+        else if (pstr == "low")
+            pval = ShaderSource::PrecisionValueLow;
+        else
+            pval = ShaderSource::PrecisionValueDefault;
+
+        switch(i) {
+            case 0: int_precision = pval; break;
+            case 1: float_precision = pval; break;
+            case 2: sampler2d_precision = pval; break;
+            case 3: samplercube_precision = pval; break;
+            default: break;
+        }
+    }
+}

=== added file 'shader-source.h'
--- shader-source.h	1970-01-01 00:00:00 +0000
+++ shader-source.h	2011-12-09 19:30:50 +0000
@@ -0,0 +1,103 @@ 
+//
+// Copyright (c) 2010-2011 Linaro Limited
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License which accompanies
+// this distribution, and is available at
+// http://www.opensource.org/licenses/mit-license.php
+//
+// Contributors:
+//     Alexandros Frantzis <alexandros.frantzis@linaro.org>
+//     Jesse Barker <jesse.barker@linaro.org>
+//
+#include <string>
+#include <sstream>
+#include <vector>
+#include "vec.h"
+#include "mat.h"
+
+/**
+ * Helper class for loading and manipulating shader sources.
+ */
+class ShaderSource
+{
+public:
+    enum ShaderType {
+        ShaderTypeVertex,
+        ShaderTypeFragment,
+        ShaderTypeUnknown
+    };
+
+    ShaderSource(ShaderType type = ShaderTypeUnknown) :
+        precision_has_been_set_(false), type_(type) {}
+    ShaderSource(const std::string &filename, ShaderType type = ShaderTypeUnknown) :
+        precision_has_been_set_(false), type_(type) { append_file(filename); }
+
+    void append(const std::string &str);
+    void append_file(const std::string &filename);
+
+    void replace(const std::string &remove, const std::string &insert);
+    void replace_with_file(const std::string &remove, const std::string &filename);
+
+    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::vec2 &v,
+                   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 = "");
+
+    ShaderType type();
+    std::string str();
+
+    enum PrecisionValue {
+        PrecisionValueLow,
+        PrecisionValueMedium,
+        PrecisionValueHigh,
+        PrecisionValueDefault
+    };
+
+    struct Precision {
+        Precision();
+        Precision(PrecisionValue int_p, PrecisionValue float_p,
+                  PrecisionValue sampler2d_p, PrecisionValue samplercube_p);
+        Precision(const std::string& list);
+
+        PrecisionValue int_precision;
+        PrecisionValue float_precision;
+        PrecisionValue sampler2d_precision;
+        PrecisionValue samplercube_precision;
+    };
+
+    void precision(const Precision& precision);
+    const Precision& precision();
+
+    static void default_precision(const Precision& precision,
+                                  ShaderType type = ShaderTypeUnknown);
+    static const Precision& default_precision(ShaderType type);
+
+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);
+    void emit_precision(std::stringstream& ss, ShaderSource::PrecisionValue val,
+                        const std::string& type_str);
+
+    std::stringstream source_;
+    Precision precision_;
+    bool precision_has_been_set_;
+    ShaderType type_;
+
+    static std::vector<Precision> default_precision_;
+};

=== added file 'test/basic-global-const.vert'
--- test/basic-global-const.vert	1970-01-01 00:00:00 +0000
+++ test/basic-global-const.vert	2012-01-23 23:37:01 +0000
@@ -0,0 +1,15 @@ 
+const vec4 ConstantColor = vec4(1.000000, 1.000000, 1.000000, 1.000000);
+attribute vec3 position;
+
+uniform mat4 modelview;
+uniform mat4 projection;
+
+varying vec4 color;
+
+void
+main(void)
+{
+    vec4 curVertex = vec4(position, 1.0);
+    gl_Position = projection * modelview * curVertex;
+    color = ConstantColor;
+}

=== added file 'test/basic.frag'
--- test/basic.frag	1970-01-01 00:00:00 +0000
+++ test/basic.frag	2012-01-23 19:18:34 +0000
@@ -0,0 +1,7 @@ 
+varying vec4 color;
+
+void
+main(void)
+{
+    gl_FragColor = color;
+}

=== added file 'test/basic.vert'
--- test/basic.vert	1970-01-01 00:00:00 +0000
+++ test/basic.vert	2012-01-23 23:37:01 +0000
@@ -0,0 +1,14 @@ 
+attribute vec3 position;
+
+uniform mat4 modelview;
+uniform mat4 projection;
+
+varying vec4 color;
+
+void
+main(void)
+{
+    vec4 curVertex = vec4(position, 1.0);
+    gl_Position = projection * modelview * curVertex;
+    color = ConstantColor;
+}

=== modified file 'test/libmatrix_test.cc'
--- test/libmatrix_test.cc	2011-09-13 16:33:49 +0000
+++ test/libmatrix_test.cc	2012-01-23 19:18:34 +0000
@@ -16,6 +16,7 @@ 
 #include "inverse_test.h"
 #include "transpose_test.h"
 #include "const_vec_test.h"
+#include "shader_source_test.h"
 
 using std::cerr;
 using std::cout;
@@ -40,6 +41,7 @@ 
     testVec.push_back(new MatrixTest2x2Transpose());
     testVec.push_back(new MatrixTest3x3Transpose());
     testVec.push_back(new MatrixTest4x4Transpose());
+    testVec.push_back(new ShaderSourceBasic());
 
     for (vector<MatrixTest*>::iterator testIt = testVec.begin();
          testIt != testVec.end();

=== added file 'test/shader_source_test.cc'
--- test/shader_source_test.cc	1970-01-01 00:00:00 +0000
+++ test/shader_source_test.cc	2012-01-23 23:37:01 +0000
@@ -0,0 +1,38 @@ 
+#include <string>
+#include "libmatrix_test.h"
+#include "shader_source_test.h"
+#include "../shader-source.h"
+#include "../vec.h"
+
+using std::string;
+using LibMatrix::vec4;
+
+void
+ShaderSourceBasic::run(const Options& options)
+{
+    static const string vtx_shader_filename("test/basic.vert");
+
+    ShaderSource vtx_source(vtx_shader_filename);
+    ShaderSource vtx_source2(vtx_shader_filename);
+    
+    pass_ = (vtx_source.str() == vtx_source2.str());
+}
+
+void
+ShaderSourceAddConstGlobal::run(const Options& options)
+{
+    // Load the original shader source.
+    static const string src_shader_filename("test/basic.vert");
+    ShaderSource src_shader(src_shader_filename);
+
+    // Add constant at global scope
+    static const vec4 constantColor(1.0, 1.0, 1.0, 1.0);
+    src_shader.add_const("ConstantColor", constantColor);
+
+    // Load the pre-modified shader
+    static const string result_shader_filename("test/basic-global-const.vert");
+    ShaderSource result_shader(result_shader_filename);
+
+    // Compare the output strings to confirm the results.
+    pass_ = (src_shader.str() == result_shader.str());
+}

=== added file 'test/shader_source_test.h'
--- test/shader_source_test.h	1970-01-01 00:00:00 +0000
+++ test/shader_source_test.h	2012-01-23 23:37:01 +0000
@@ -0,0 +1,32 @@ 
+//
+// Copyright (c) 2012 Linaro Limited
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License which accompanies
+// this distribution, and is available at
+// http://www.opensource.org/licenses/mit-license.php
+//
+// Contributors:
+//     Jesse Barker - original implementation.
+//
+#ifndef SHADER_SOURCE_TEST_H_
+#define SHADER_SOURCE_TEST_H_
+
+class MatrixTest;
+class Options;
+
+class ShaderSourceBasic : public MatrixTest
+{
+public:
+    ShaderSourceBasic() : MatrixTest("ShaderSource::basic") {}
+    virtual void run(const Options& options);
+};
+
+class ShaderSourceAddConstGlobal : public MatrixTest
+{
+public:
+    ShaderSourceAddConstGlobal() : MatrixTest("ShaderSource::AddConstGlobal") {}
+    virtual void run(const Options& options);
+};
+
+#endif // SHADER_SOURCE_TEST_H

=== added file 'util.cc'
--- util.cc	1970-01-01 00:00:00 +0000
+++ util.cc	2011-12-09 00:59:12 +0000
@@ -0,0 +1,153 @@ 
+//
+// Copyright (c) 2010-2011 Linaro Limited
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License which accompanies
+// this distribution, and is available at
+// http://www.opensource.org/licenses/mit-license.php
+//
+// Contributors:
+//     Alexandros Frantzis <alexandros.frantzis@linaro.org>
+//     Jesse Barker <jesse.barker@linaro.org>
+//
+#include <sstream>
+#include <fstream>
+#include <sys/time.h>
+#ifdef ANDROID
+#include <android/asset_manager.h>
+#else
+#include <dirent.h>
+#endif
+
+#include "log.h"
+#include "util.h"
+
+/**
+ * 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
+ */
+void
+Util::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);
+}
+
+uint64_t
+Util::get_timestamp_us()
+{
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    uint64_t now = static_cast<uint64_t>(tv.tv_sec) * 1000000 +
+                   static_cast<double>(tv.tv_usec);
+    return now;
+}
+
+#ifndef ANDROID
+
+std::istream *
+Util::get_resource(const std::string &path)
+{
+    std::ifstream *ifs = new std::ifstream(path.c_str());
+
+    return static_cast<std::istream *>(ifs);
+}
+
+void
+Util::list_files(const std::string& dirName, std::vector<std::string>& fileVec)
+{
+    DIR* dir = opendir(dirName.c_str());
+    if (!dir)
+    {
+        Log::error("Failed to open models directory '%s'\n", dirName.c_str());
+        return;
+    }
+
+    struct dirent* entry = readdir(dir);
+    while (entry)
+    {
+        std::string pathname(dirName + "/");
+        pathname += std::string(entry->d_name);
+        // Skip '.' and '..'
+        if (entry->d_name[0] != '.')
+        {
+            fileVec.push_back(pathname);
+        }
+        entry = readdir(dir);
+    }
+    closedir(dir);
+}
+
+#else
+
+AAssetManager *Util::android_asset_manager = 0;
+
+void
+Util::android_set_asset_manager(AAssetManager *asset_manager)
+{
+    Util::android_asset_manager = asset_manager;
+}
+
+AAssetManager *
+Util::android_get_asset_manager()
+{
+    return Util::android_asset_manager;
+}
+
+std::istream *
+Util::get_resource(const std::string &path)
+{
+    std::string path2(path);
+    /* Remove leading '/' from path name, it confuses the AssetManager */
+    if (path2.size() > 0 && path2[0] == '/')
+        path2.erase(0, 1);
+
+    std::stringstream *ss = new std::stringstream;
+    AAsset *asset = AAssetManager_open(Util::android_asset_manager,
+                                       path2.c_str(), AASSET_MODE_RANDOM);
+    if (asset) {
+        ss->write(reinterpret_cast<const char *>(AAsset_getBuffer(asset)),
+                  AAsset_getLength(asset));
+        Log::debug("Load asset %s\n", path2.c_str());
+        AAsset_close(asset);
+    }
+    else {
+        Log::error("Couldn't load asset %s\n", path2.c_str());
+    }
+
+    return static_cast<std::istream *>(ss);
+}
+
+void
+Util::list_files(const std::string& dirName, std::vector<std::string>& fileVec)
+{
+    AAssetManager *mgr(Util::android_get_asset_manager());
+    std::string dir_name(dirName);
+
+    /* Remove leading '/' from path, it confuses the AssetManager */
+    if (dir_name.size() > 0 && dir_name[0] == '/')
+        dir_name.erase(0, 1);
+
+    AAssetDir* dir = AAssetManager_openDir(mgr, dir_name.c_str());
+    if (!dir)
+    {
+        Log::error("Failed to open models directory '%s'\n", dir_name.c_str());
+        return;
+    }
+
+    const char *filename(0);
+    while ((filename = AAssetDir_getNextFileName(dir)) != 0)
+    {
+        std::string pathname(dir_name + "/");
+        pathname += std::string(filename);
+        fileVec.push_back(pathname);
+    }
+    AAssetDir_close(dir);
+}
+#endif

=== added file 'util.h'
--- util.h	1970-01-01 00:00:00 +0000
+++ util.h	2011-12-09 00:59:12 +0000
@@ -0,0 +1,70 @@ 
+//
+// Copyright (c) 2010-2011 Linaro Limited
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License which accompanies
+// this distribution, and is available at
+// http://www.opensource.org/licenses/mit-license.php
+//
+// Contributors:
+//     Alexandros Frantzis <alexandros.frantzis@linaro.org>
+//     Jesse Barker <jesse.barker@linaro.org>
+//
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <string>
+#include <vector>
+#include <istream>
+#include <sstream>
+#include <stdint.h>
+
+#ifdef ANDROID
+#include <android/asset_manager_jni.h>
+#endif
+
+struct Util {
+    static void split(const std::string &s, char delim, std::vector<std::string> &elems);
+    static uint64_t get_timestamp_us();
+    static std::istream *get_resource(const std::string &path);
+    static void list_files(const std::string& dirName, std::vector<std::string>& fileVec);
+    template <class T> static void dispose_pointer_vector(std::vector<T*> &vec)
+    {
+        for (typename std::vector<T*>::const_iterator iter = vec.begin();
+             iter != vec.end();
+             iter++)
+        {
+            delete *iter;
+        }
+
+        vec.clear();
+    }
+    template<typename T>
+    static T
+    fromString(const std::string& asString)
+    {
+        std::stringstream ss(asString);
+        T retVal;
+        ss >> retVal;
+        return retVal;
+    }
+
+    template<typename T>
+    static std::string
+    toString(const T t)
+    {
+        std::stringstream ss;
+        ss << t;
+        return ss.str();
+    }
+
+
+#ifdef ANDROID
+    static void android_set_asset_manager(AAssetManager *asset_manager);
+    static AAssetManager *android_get_asset_manager(void);
+private:
+    static AAssetManager *android_asset_manager;
+#endif
+};
+
+#endif /* UTIL_H */