=== added file 'src/benchmark.cc'
--- src/benchmark.cc	1970-01-01 00:00:00 +0000
+++ src/benchmark.cc	2011-06-22 11:35:50 +0000
@@ -0,0 +1,142 @@
+/*
+ * Copyright © 2011 Linaro Limited
+ *
+ * This file is part of glcompbench.
+ *
+ * glcompbench 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.
+ *
+ * glcompbench 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 glcompbench.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ *  Jesse Barker <jesse.barker@linaro.org>
+ */
+
+#include "benchmark.h"
+#include "log.h"
+#include <sstream>
+
+using std::string;
+using std::vector;
+using std::map;
+
+std::map<string, CompositeTest *> Benchmark::test_map_;
+
+static void
+split(const string &s, char delim, vector<string> &elems)
+{
+    std::stringstream ss(s);
+
+    string item;
+    while(std::getline(ss, item, delim))
+        elems.push_back(item);
+}
+
+static CompositeTest &
+get_test_from_description(const string &s)
+{
+    vector<string> elems;
+
+    split(s, ':', elems);
+
+    const string &name = !elems.empty() ? elems[0] : ""; 
+
+    return Benchmark::get_test_by_name(name);
+}
+
+static vector<Benchmark::OptionPair>
+get_options_from_description(const string &s)
+{
+    vector<Benchmark::OptionPair> options;
+    vector<string> elems;
+
+    split(s, ':', elems);
+
+    for (vector<string>::const_iterator iter = ++elems.begin();
+         iter != elems.end();
+         iter++)
+    {
+        vector<string> opt;
+
+        split(*iter, '=', opt);
+        if (opt.size() == 2)
+            options.push_back(Benchmark::OptionPair(opt[0], opt[1]));
+        else
+            Log::info("Warning: ignoring invalid option string '%s' "
+                      "in benchmark description\n",
+                      iter->c_str());
+    }
+
+    return options;
+}
+
+void
+Benchmark::register_test(CompositeTest &test)
+{
+    test_map_[test.name()] = &test;
+}
+
+CompositeTest &
+Benchmark::get_test_by_name(const string &name)
+{
+    map<string, CompositeTest *>::const_iterator iter;
+
+    if ((iter = test_map_.find(name)) != test_map_.end())
+        return *(iter->second);
+    else
+        return CompositeTest::dummy();
+}
+
+Benchmark::Benchmark(CompositeTest &test, const vector<OptionPair> &options) :
+    test_(test), options_(options)
+{
+}
+
+Benchmark::Benchmark(const string &name, const vector<OptionPair> &options) :
+    test_(Benchmark::get_test_by_name(name)), options_(options)
+{
+}
+
+Benchmark::Benchmark(const string &s) :
+    test_(get_test_from_description(s)),
+    options_(get_options_from_description(s))
+{
+}
+
+CompositeTest &
+Benchmark::setup_test()
+{
+    test_.reset_options();
+    load_options();
+
+    test_.prepare_for_run();
+
+    return test_;
+}
+
+void
+Benchmark::teardown_test()
+{
+    test_.cleanup();
+}
+
+void
+Benchmark::load_options()
+{
+    for (vector<OptionPair>::iterator iter = options_.begin();
+         iter != options_.end();
+         iter++)
+    {
+        test_.set_option(iter->first, iter->second);
+    }
+}
+

=== added file 'src/benchmark.h'
--- src/benchmark.h	1970-01-01 00:00:00 +0000
+++ src/benchmark.h	2011-06-22 11:35:50 +0000
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2011 Linaro Limited
+ *
+ * This file is part of glcompbench.
+ *
+ * glcompbench 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.
+ *
+ * glcompbench 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 glcompbench.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ *  Jesse Barker <jesse.barker@linaro.org>
+ */
+#ifndef BENCHMARK_H_
+#define BENCHMARK_H_
+
+#include <vector>
+#include <string>
+#include <map>
+
+#include "composite-test.h"
+
+class Benchmark
+{
+public:
+    typedef std::pair<std::string, std::string> OptionPair;
+
+    Benchmark(CompositeTest &test, const std::vector<OptionPair> &options);
+    Benchmark(const std::string &name, const std::vector<OptionPair> &options);
+    // Create a benchmark from a description string of the form:
+    // test[:opt1=val1:opt2=val2...]
+    Benchmark(const std::string &s);
+
+    CompositeTest &setup_test();
+    void teardown_test();
+
+    static void register_test(CompositeTest &test);
+    static CompositeTest &get_test_by_name(const std::string &name);
+    static const std::map<std::string, CompositeTest *> &tests() { return test_map_; }
+
+private:
+    CompositeTest &test_;
+    std::vector<OptionPair> options_;
+
+    void load_options();
+
+    static std::map<std::string, CompositeTest *> test_map_;
+};
+
+#endif

=== modified file 'src/composite-canvas.cc'
--- src/composite-canvas.cc	2011-04-27 18:10:23 +0000
+++ src/composite-canvas.cc	2011-06-23 08:54:28 +0000
@@ -34,6 +34,7 @@
 
 #include "composite-canvas.h"
 #include "composite-window.h"
+#include "benchmark.h"
 #include "options.h"
 #include "log.h"
 #include "profiler.h"
@@ -443,9 +444,6 @@
 
     iteration_complete = false;
 
-    if (!Options::benchmark)
-        return;
-
     Profiler &profiler = Profiler::instance();
 
     profiler_cycle_pair_.sample_end();
@@ -476,29 +474,13 @@
     }
 }
 
-void
-CompositeCanvas::next_test(const std::list<CompositeTest*>& test_list, bool& back_to_beginning)
+static void
+next_benchmark(std::list<Benchmark*>& benchmarks,
+               std::list<Benchmark*>::iterator &benchIt)
 {
-    back_to_beginning = false;
-    if (test_list.back() == current_test_) {
-        current_test_ = test_list.front();
-        back_to_beginning = true;
-        return;
-    }
-    bool foundIt(false);
-    for (std::list<CompositeTest*>::const_iterator testIt = test_list.begin();
-         testIt != test_list.end();
-         testIt++)
-    {
-        CompositeTest* curTest = *testIt;
-        if (foundIt) {
-            current_test_ = curTest;
-            break;
-        }
-        if (curTest == current_test_) {
-            foundIt = true;
-        }
-    }
+    benchIt++;
+    if (benchIt == benchmarks.end() && Options::run_forever)
+        benchIt = benchmarks.begin();
 }
 
 /******************
@@ -546,12 +528,12 @@
 /**
  * Runs a list of tests.
  *
- * @param test_list the list of tests to run
+ * @param benchmarks the list of benchmarks to run
  */
 void
-CompositeCanvas::run_tests(std::list<CompositeTest*> &test_list)
+CompositeCanvas::run_tests(std::list<Benchmark*> &benchmarks)
 {
-    if (test_list.empty()) {
+    if (benchmarks.empty()) {
         Log::error("Test list is empty!\n");
         return;
     }
@@ -567,11 +549,12 @@
 
     // Initialize all tests so that the benchmarking is not biased by
     // setup time (loading and building shaders, etc.)
-    for (std::list<CompositeTest*>::iterator testIt = test_list.begin();
-         testIt != test_list.end();
+    for (std::map<std::string, CompositeTest*>::const_iterator testIt =
+            Benchmark::tests().begin();
+         testIt != Benchmark::tests().end();
          testIt++)
     {
-        CompositeTest* curTest = *testIt;
+        CompositeTest* curTest = testIt->second;
         if (!curTest) {
             Log::error("Found a NULL test pointer!\n");
             return;
@@ -579,19 +562,23 @@
         curTest->init();
     }
 
-    current_test_ = test_list.front();
-    current_test_->prepare_for_run();
-    Log::info("Running test: '%s' %susing vertex buffer objects\n", 
-              current_test_->name().c_str(), 
-              (Options::use_vbo) ? "" : "NOT ");
-    reshape(width_, height_);
-
     benchmark_init();
 
     bool next_iteration(false);
-    while (true) {
-        if (handle_xevent()) {
-            if (current_test_) {
+
+    for (std::list<Benchmark *>::iterator benchIt = benchmarks.begin();
+         benchIt != benchmarks.end();
+         next_benchmark(benchmarks, benchIt))
+    {
+        Benchmark *benchmark = *benchIt;
+        current_test_ = &benchmark->setup_test();
+        reshape(width_, height_);
+        Log::info("Running test: '%s' %susing vertex buffer objects\n", 
+                  current_test_->name().c_str(),
+                  (Options::use_vbo) ? "" : "NOT ");
+
+        while (true) {
+            if (handle_xevent()) {
                 if (Options::force_tex_update)
                     update_all_textures();
 
@@ -606,20 +593,13 @@
 
                 benchmark_update(next_iteration);
                 if (next_iteration) {
-                    current_test_->cleanup();
-                    bool back_to_beginning(false);
-                    next_test(test_list, back_to_beginning);
-                    if (!Options::run_forever && back_to_beginning) {
-                        break;
-                    }
-                    Log::info("Running test: '%s' %susing vertex buffer objects\n", 
-                              current_test_->name().c_str(),
-                              (Options::use_vbo) ? "" : "NOT ");
-                    current_test_->prepare_for_run();
-                    current_test_->reshape(width_, height_);
+                    break;
                 }
+
             }
         }
+
+        benchmark->teardown_test();
     }
 }
 

=== modified file 'src/composite-canvas.h'
--- src/composite-canvas.h	2011-04-04 12:56:06 +0000
+++ src/composite-canvas.h	2011-06-22 11:36:50 +0000
@@ -34,6 +34,7 @@
 
 #include "composite-window.h"
 #include "composite-test.h"
+#include "benchmark.h"
 #include "profiler.h"
 
 class CompositeCanvas
@@ -49,7 +50,7 @@
     {}
 
     bool init();
-    void run_tests(std::list<CompositeTest*> &test_list);
+    void run_tests(std::list<Benchmark*> &benchmarks);
 
 protected:
     virtual XVisualInfo *get_canvas_xvisualinfo() = 0;
@@ -82,7 +83,6 @@
     void benchmark_init();
     void benchmark_update(bool& iteration_complete);
     void update_all_textures();
-    void next_test(const std::list<CompositeTest*>& test_list, bool& back_to_beginning);
     void sample_point(int i);
     unsigned int iterations_;
     unsigned int width_;

=== modified file 'src/composite-test.h'
--- src/composite-test.h	2011-06-22 09:12:20 +0000
+++ src/composite-test.h	2011-06-22 11:34:41 +0000
@@ -60,6 +60,12 @@
     void reset_options();
     const std::map<std::string, Option> &options() { return options_; }
 
+    static CompositeTest &dummy()
+    {
+        static CompositeTest dummy_test("");
+        return dummy_test;
+    }
+
 protected:
     CompositeTest();
     //

=== modified file 'src/gl-composite-benchmark'
--- src/gl-composite-benchmark	2011-01-17 18:15:21 +0000
+++ src/gl-composite-benchmark	2011-06-23 08:54:28 +0000
@@ -113,6 +113,6 @@
 
 start_xterms $NWINDOWS
 
-$GLCOMPBENCH_EXEC --benchmark --ids $wids $GLCOMPBENCH_OPTS
+$GLCOMPBENCH_EXEC --ids $wids $GLCOMPBENCH_OPTS
 
 stop_xterms

=== modified file 'src/glcompbench.cc'
--- src/glcompbench.cc	2011-06-22 09:26:51 +0000
+++ src/glcompbench.cc	2011-06-23 08:54:28 +0000
@@ -30,7 +30,63 @@
 #endif
 
 #include "composite-test.h"
+#include "benchmark.h"
 #include "options.h"
+#include "log.h"
+
+static const char *default_benchmarks[] = {
+    "default",
+    "brick",
+    NULL
+};
+
+static void
+list_tests()
+{
+    const map<string, CompositeTest *> &tests = Benchmark::tests();
+
+    for (map<string, CompositeTest *>::const_iterator test_iter = tests.begin();
+         test_iter != tests.end();
+         test_iter++)
+    {
+        CompositeTest *test = test_iter->second;
+        Log::info("[Test] %s\n", test->name().c_str());
+
+        const map<string, CompositeTest::Option> &options = test->options();
+
+        for (map<string, CompositeTest::Option>::const_iterator opt_iter = options.begin();
+             opt_iter != options.end();
+             opt_iter++)
+        {
+            const CompositeTest::Option &opt = opt_iter->second;
+            Log::info("  [Option] %s\n"
+                      "    Description  : %s\n"
+                      "    Default Value: %s\n",
+                      opt.name.c_str(), 
+                      opt.description.c_str(),
+                      opt.default_value.c_str());
+        }
+    }
+}
+
+static void
+add_default_benchmarks(std::list<Benchmark*> &benchmarks)
+{
+    for (const char **s = default_benchmarks; *s != NULL; s++)
+        benchmarks.push_back(new Benchmark(*s));
+}
+
+static void
+add_custom_benchmarks(std::list<Benchmark *> &benchmarks)
+{
+    for (std::list<string>::const_iterator iter = Options::benchmarks.begin();
+         iter != Options::benchmarks.end();
+         iter++)
+    {
+        Log::info("Adding benchmark %s\n", iter->c_str());
+        benchmarks.push_back(new Benchmark(*iter));
+    }
+}
 
 int
 main(int argc, char **argv)
@@ -41,7 +97,7 @@
     CompositeCanvasGLX canvas;
 #endif
 
-    std::list<CompositeTest*> tests;
+    std::list<Benchmark*> benchmarks;
 
     if (!Options::parse_args(argc, argv))
         return 1;
@@ -51,11 +107,22 @@
         return 0;
     }
 
-    tests.push_back(new CompositeTestDefault());
-    tests.push_back(new CompositeTestBrick());
+    Benchmark::register_test(*new CompositeTestDefault());
+    Benchmark::register_test(*new CompositeTestBrick());
+
+    if (Options::list_tests) {
+        list_tests();
+        return 0;
+    }
+
+    // Add the benchmarks to run
+    if (Options::benchmarks.empty())
+        add_default_benchmarks(benchmarks);
+    else
+        add_custom_benchmarks(benchmarks);
 
     canvas.init();
-    canvas.run_tests(tests);
+    canvas.run_tests(benchmarks);
 
     return 0;
 }

=== modified file 'src/options.cc'
--- src/options.cc	2011-06-22 09:26:51 +0000
+++ src/options.cc	2011-06-23 08:54:28 +0000
@@ -34,7 +34,7 @@
 std::list<Window> Options::window_ids;
 bool Options::run_forever = false;
 bool Options::use_accel_tfp = true;
-bool Options::benchmark = false;
+std::list<std::string> Options::benchmarks;
 bool Options::draw = true;
 bool Options::idle_redraw = false;
 bool Options::force_tex_update = false;
@@ -42,19 +42,21 @@
 bool Options::show_debug = false;
 bool Options::show_help = false;
 bool Options::use_vbo = true;
+bool Options::list_tests = false;
 
 static struct option long_options[] = {
     {"ids", 1, 0, 0},
     {"size", 1, 0, 0},
     {"no-accel-tfp", 0, 0, 0},
     {"no-vbo", 0, 0, 0},
-    {"benchmark", 0, 0, 0},
+    {"benchmark", 1, 0, 0},
     {"no-draw", 0, 0, 0},
     {"idle-redraw", 0, 0, 0},
     {"force-tex-update", 0, 0, 0},
     {"manual-redirect", 0, 0, 0},
     {"run-forever", 0, 0, 0},
     {"num-iters", 1, 0, 0},
+    {"list-tests", 0, 0, 0},
     {"debug", 0, 0, 0},
     {"help", 0, 0, 0},
     {0, 0, 0, 0}
@@ -86,7 +88,8 @@
            "                     [default: 512]\n"
            "  --no-accel-tfp     Don't use accelerated TFP (use glTexImage2D)\n"
            "  --no-vbo           Don't use vertex buffer objects for drawing (use client vertex arrays)\n"
-           "  --benchmark        Display benchmarking statistics\n"
+           "  --benchmark BENCH  A benchmark to run: 'test(:opt1=val1)*'\n"
+           "                     (the option can be used multiple times)\n"
            "  --no-draw          Process the windows but don't draw anything on screen\n"
            "  --idle-redraw      Redraw when idle, even when nothing has changed\n"
            "  --force-tex-update Force texture updates even when they are unchanged\n"
@@ -94,6 +97,7 @@
            "  --run-forever      Run indefinitely, looping from the last test back to the first\n"
            "  --num-iters <NUM>  The number of iterations run for each test\n"
            "                     [default: 5]\n"
+           "  --list-tests       List the avalaible tests and their options\n"
            "  --debug            Display debug messages\n"
            "  --help             Display help\n");
 }
@@ -125,7 +129,7 @@
        else if (!strcmp(optname, "no-vbo"))
            Options::use_vbo = false;
        else if (!strcmp(optname, "benchmark"))
-           Options::benchmark = true;
+           Options::benchmarks.push_back(optarg);
        else if (!strcmp(optname, "no-draw"))
            Options::draw = false;
        else if (!strcmp(optname, "idle-redraw"))
@@ -136,6 +140,8 @@
            Options::run_forever = true;
        else if (!strcmp(optname, "num-iters"))
            Options::num_iterations = strtol(optarg, NULL, 10);
+       else if (!strcmp(optname, "list-tests"))
+           Options::list_tests = true;
        else if (!strcmp(optname, "manual-redirect"))
            Options::manual_redirect = true;
        else if (!strcmp(optname, "debug"))

=== modified file 'src/options.h'
--- src/options.h	2011-06-22 09:26:51 +0000
+++ src/options.h	2011-06-23 08:54:28 +0000
@@ -26,6 +26,7 @@
 
 #include <X11/Xlib.h>
 #include <list>
+#include <string>
 
 struct Options {
     static bool parse_args(int argc, char **argv);
@@ -36,7 +37,7 @@
     static unsigned int num_iterations;
     static bool run_forever;
     static bool use_accel_tfp;
-    static bool benchmark;
+    static std::list<std::string> benchmarks;
     static bool draw;
     static bool idle_redraw;
     static bool force_tex_update;
@@ -44,6 +45,7 @@
     static bool show_debug;
     static bool show_help;
     static bool use_vbo;
+    static bool list_tests;
 };
 
 #endif /* OPTIONS_H_ */

