From patchwork Thu Jan 26 16:10:18 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jesse Barker X-Patchwork-Id: 6411 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 95F7823ECA for ; Thu, 26 Jan 2012 16:10:20 +0000 (UTC) Received: from mail-bk0-f52.google.com (mail-bk0-f52.google.com [209.85.214.52]) by fiordland.canonical.com (Postfix) with ESMTP id 67376A183D4 for ; Thu, 26 Jan 2012 16:10:20 +0000 (UTC) Received: by bkar19 with SMTP id r19so756044bka.11 for ; Thu, 26 Jan 2012 08:10:20 -0800 (PST) Received: by 10.204.10.82 with SMTP id o18mr1357623bko.20.1327594220083; Thu, 26 Jan 2012 08:10:20 -0800 (PST) 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.204.130.220 with SMTP id u28cs45280bks; Thu, 26 Jan 2012 08:10:19 -0800 (PST) Received: by 10.180.93.105 with SMTP id ct9mr677350wib.17.1327594218741; Thu, 26 Jan 2012 08:10:18 -0800 (PST) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id f50si3805515wed.144.2012.01.26.08.10.18 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 26 Jan 2012 08:10:18 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) client-ip=91.189.90.7; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) smtp.mail=bounces@canonical.com Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1RqRty-00065r-5x for ; Thu, 26 Jan 2012 16:10:18 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id 11513E01D0 for ; Thu, 26 Jan 2012 16:10:18 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: libmatrix X-Launchpad-Branch: ~jesse-barker/libmatrix/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 30 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~jesse-barker/libmatrix/trunk] Rev 30: Merge lp:~jesse-barker/libmatrix/util into trunk. Message-Id: <20120126161018.19837.76399.launchpad@ackee.canonical.com> Date: Thu, 26 Jan 2012 16:10:18 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="14719"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: f77b70f9c07c76b3c7ad9fd121ebe81c1f86c23a 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 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 === 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 +#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 +// Jesse Barker +// +#include +#include +#include +#include +#include +#include "log.h" + +#ifdef ANDROID +#include +#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 +// Jesse Barker +// +#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 #include #include -#include +#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 +// Jesse Barker +// +#include +#include + +#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::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 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 &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 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 &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); +} + +/** + * 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: + * ",,," + * + * 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 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 +// Jesse Barker +// +#include +#include +#include +#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 &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 &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 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::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 +#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 +// Jesse Barker +// +#include +#include +#include +#ifdef ANDROID +#include +#else +#include +#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 &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(tv.tv_sec) * 1000000 + + static_cast(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(ifs); +} + +void +Util::list_files(const std::string& dirName, std::vector& 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(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(ss); +} + +void +Util::list_files(const std::string& dirName, std::vector& 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 +// Jesse Barker +// +#ifndef UTIL_H_ +#define UTIL_H_ + +#include +#include +#include +#include +#include + +#ifdef ANDROID +#include +#endif + +struct Util { + static void split(const std::string &s, char delim, std::vector &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& fileVec); + template static void dispose_pointer_vector(std::vector &vec) + { + for (typename std::vector::const_iterator iter = vec.begin(); + iter != vec.end(); + iter++) + { + delete *iter; + } + + vec.clear(); + } + template + static T + fromString(const std::string& asString) + { + std::stringstream ss(asString); + T retVal; + ss >> retVal; + return retVal; + } + + template + 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 */