[clutter] tests: Add performance tracking framework



commit aa05b66a018b7b7bc9349aed30d24e7cf012b34b
Author: Ãyvind KolÃs <pippin linux intel com>
Date:   Wed Jan 19 11:38:25 2011 +0000

    tests: Add performance tracking framework
    
    This adds a performance tracking framework that can run a set of tests over
    specified git revisions. The ruby script for generating the reports comes from
    similar performance tracking in GEGL. The framework permits evaluating new
    tests against older version of clutter.
    
    The tests themselves go through a few hoops for disabling framerate limiting in
    both mesa and clutter.
    
    When running make check the tests will be run and lines of the form:
    
    @ test-state: 40.51 fps
    
    will be left in the output, a script can scrape these lines out of a build log
    on a buildbot to in other ways track performance.

 Makefile.am                                |    5 +-
 configure.ac                               |    1 +
 tests/Makefile.am                          |    4 +-
 tests/README                               |   18 ++--
 tests/performance/Makefile-retrospect      |   66 ++++++++++
 tests/performance/Makefile-tests           |   15 ++
 tests/performance/Makefile.am              |   42 ++++++
 tests/performance/create-report.rb         |  179 +++++++++++++++++++++++++
 tests/performance/joblist                  |   27 ++++
 tests/performance/makejobs.rb              |   24 ++++
 tests/performance/test-common.h            |  130 +++++++++++++++++++
 tests/performance/test-picking.c           |  133 +++++++++++++++++++
 tests/performance/test-state-hidden.c      |  149 +++++++++++++++++++++
 tests/performance/test-state-interactive.c |  194 ++++++++++++++++++++++++++++
 tests/performance/test-state-mini.c        |  152 ++++++++++++++++++++++
 tests/performance/test-state-pick.c        |  158 ++++++++++++++++++++++
 tests/performance/test-state.c             |  157 ++++++++++++++++++++++
 tests/performance/test-text-perf.c         |  164 +++++++++++++++++++++++
 18 files changed, 1606 insertions(+), 12 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 7246923..4962c3a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,4 +32,7 @@ gcov:
 test-report full-report:
 	$(MAKE) -C tests/conform $(@)
 
-.PHONY: gcov test-report full-report
+perf-report:
+	$(MAKE) -C tests/performance $(@)
+
+.PHONY: gcov test-report full-report perf-report
diff --git a/configure.ac b/configure.ac
index 4903250..402016a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -949,6 +949,7 @@ AC_CONFIG_FILES([
 	tests/interactive/Makefile
 	tests/interactive/wrapper.sh
 	tests/micro-bench/Makefile
+	tests/performance/Makefile
 
 	doc/Makefile
 	doc/reference/Makefile
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 03df5c7..9eeba79 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,10 +1,10 @@
-SUBDIRS = accessibility data interactive micro-bench
+SUBDIRS = accessibility data interactive micro-bench performance
 
 if BUILD_TESTS
 SUBDIRS += conform
 endif
 
-DIST_SUBDIRS = accessibility data conform interactive micro-bench
+DIST_SUBDIRS = accessibility data conform interactive micro-bench performance
 
 EXTRA_DIST = README
 
diff --git a/tests/README b/tests/README
index 48fd2f3..563fa4d 100644
--- a/tests/README
+++ b/tests/README
@@ -4,15 +4,15 @@ The conform/ tests should be non-interactive unit-tests that verify a single
 feature is behaving as documented. See conform/ADDING_NEW_TESTS for more
 details.
 
-The micro-bench/ tests should be focused perfomance test, ideally testing a
-single metric. Please never forget that these tests are synthetec and if you
-are using them then you understand what metric is being tested. They probably
-don't reflect any real world application loads and the intention is that you
-use these tests once you have already determined the crux of your problem and
-need focused feedback that your changes are indeed improving matters. There is
-no exit status requirements for these tests, but they should give clear
-feedback as to their performance. If the framerate is the feedback metric, then
-the test should forcibly enable FPS debugging.
+The performance/ tests are performance tests, both focused tests testing single
+metrics and larger tests. These tests are used to report one or more
+performance markers for the build of Clutter. Each performance marker is picked
+up from the standard output of running the tests from strings having the form
+"\n@ marker-name: 42.23" where 'marker-name' and '42.23' are the key/value pairs
+of a single metric. Each test can provide multiple key/value pairs. Note that
+if framerate is the feedback metric the test should forcibly enable FPS
+debugging itself. The file test-common.h contains utility function helping to
+do fps reporting.
 
 The interactive/ tests are any tests whose status can not be determined without
 a user looking at some visual output, or providing some manual input etc. This
diff --git a/tests/performance/Makefile-retrospect b/tests/performance/Makefile-retrospect
new file mode 100644
index 0000000..f696d53
--- /dev/null
+++ b/tests/performance/Makefile-retrospect
@@ -0,0 +1,66 @@
+# A makefile based framework for testing performance commits in retrospect,
+# based on work done by pippin gimp org done for GEGL, original code placed in the public domain.
+
+SELF = Makefile-retrospect
+
+MAKE_FLAGS = -j3 -k
+CC = "ccache gcc"   # if you do not have ccache replace with just gcc
+
+PROJECT_PATH = ../../
+
+# mute makes echoing of commands
+.SILENT:
+
+# replace sequential with random to build a random subset
+all: reset sequential
+#all: reset random
+
+retry:
+	rm -rf reports/`cat jobs | tail -n1`*
+	make -f $(SELF)
+
+prepare:
+	# uncomment these to make sure cpu is in high performance mode
+	#sudo sh -c 'echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor || true'
+	#sudo sh -c 'echo performance > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor || true'
+
+reset:
+	rm -rf jobs jobs
+	# remove checkout dir to have a full reset on each invokation
+	rm -rf checkout
+	# create clone
+	git clone -s $(PROJECT_PATH) checkout
+	mkdir reports > /dev/null 2>&1 || true
+	make -f $(SELF) jobs
+	make -f $(SELF) prepare
+
+jobs: joblist
+	./makejobs.rb joblist > jobs
+
+sequential:
+	for a in `cat jobs`;do make -f $(SELF) reports/$$a;done
+
+random:
+	for a in `cat jobs|sort`;do make -f $(SELF) reports/$$a;done
+
+reports/%:
+	# check out revision
+	(cd checkout; git checkout `echo $@|sed s:reports/::`)
+	# write header for report
+	git log -1 `echo $@|sed s:reports/::` > $@  || true 
+	# clean previous build
+	rm -rf install; mkdir install
+	# build revision
+	(cd checkout; if [ ! -f Makefile ]; then CC=$(CC) ./autogen.sh --disable-introspection --prefix=`pwd`/../install; fi ; \
+	 make $(MAKE_FLAGS) ; make -k install ) > $  log 2>&1 || true
+	# testing
+	make -f Makefile-tests clean;\
+	make -f Makefile-tests; sync;\
+	make -f Makefile-tests check >> $@ || true
+	# update report.pdf / report.png
+	./create-report.rb
+	echo
+
+clean:
+	rm -rf reports jobs report.pdf report.png checkout install
+	make -f Makefile-tests clean
diff --git a/tests/performance/Makefile-tests b/tests/performance/Makefile-tests
new file mode 100644
index 0000000..3381a32
--- /dev/null
+++ b/tests/performance/Makefile-tests
@@ -0,0 +1,15 @@
+CFILES = $(wildcard *.c)
+bins   = $(subst ,,$(CFILES:.c=))
+
+all: $(bins)
+
+%: %.c
+	PKG_CONFIG_PATH=install/lib/pkgconfig:$(PKG_CONFIG_PATH) $(CC) -DTESTS_DATA_DIR=\"../data/\" `pkg-config clutter-1.0 --cflags --libs` -Wall -O2  -o $@ $<
+
+check: $(bins)
+	for a in $(bins); do \
+	  LD_LIBRARY_PATH=install/lib:$(LD_LIBRARY_PATH) ./$$a;\
+	done
+
+clean:
+	rm -f $(bins)
diff --git a/tests/performance/Makefile.am b/tests/performance/Makefile.am
new file mode 100644
index 0000000..259f777
--- /dev/null
+++ b/tests/performance/Makefile.am
@@ -0,0 +1,42 @@
+include $(top_srcdir)/build/autotools/Makefile.am.silent
+
+noinst_PROGRAMS = \
+	test-picking \
+	test-text-perf \
+	test-state \
+	test-state-interactive \
+	test-state-hidden \
+	test-state-mini \
+	test-state-pick
+
+INCLUDES = \
+	-I$(top_srcdir)/ \
+	-I$(top_srcdir)/clutter \
+	-I$(top_srcdir)/clutter/cogl \
+	-I$(top_builddir)/clutter \
+	-I$(top_builddir)/clutter/cogl
+LDADD = $(top_builddir)/clutter/libclutter- CLUTTER_SONAME_INFIX@- CLUTTER_API_VERSION@.la
+AM_CFLAGS = \
+	$(CLUTTER_CFLAGS) \
+	$(MAINTAINER_CFLAGS) \
+	-DG_DISABLE_SINGLE_INCLUDES \
+	-DTESTS_DATA_DIR=\""$(top_srcdir)/tests/data/"\"
+
+perf-report: check
+
+check:
+	for a in $(noinst_PROGRAMS);do ./$$a;done;true
+
+AM_LDFLAGS = $(CLUTTER_LIBS)
+
+test_picking_SOURCES = test-picking.c
+test_text_perf_SOURCES = test-text-perf.c
+test_state_SOURCES = test-state.c
+test_state_hidden_SOURCES = test-state-hidden.c
+test_state_pick_SOURCES = test-state-pick.c
+test_state_interactive_SOURCES = test-state-interactive.c
+test_state_mini_SOURCES = test-state-mini.c
+
+EXTRA_DIST = Makefile-retrospect Makefile-tests create-report.rb test-common.h
+
+-include $(top_srcdir)/build/autotools/Makefile.am.gitignore
diff --git a/tests/performance/create-report.rb b/tests/performance/create-report.rb
new file mode 100755
index 0000000..43defc4
--- /dev/null
+++ b/tests/performance/create-report.rb
@@ -0,0 +1,179 @@
+#!/usr/bin/env ruby
+#
+# ruby program to generate a performance report in PDF/png over git revisions, based on work
+# originally done for gegl by pippin gimp org, the original program is in the public domain.
+
+require 'cairo'
+
+def cairo_surface(w,h)
+    surface = Cairo::PDFSurface.new("report.pdf", w,h)
+    cr = Cairo::Context.new(surface)
+    yield(cr)
+end 
+
+class Database
+
+    def initialize()
+        @vals = Hash.new
+        @runs = Array.new
+        @colors = [
+          [0,1,0, 0.8],
+          [0,1,1, 0.8],
+          [1,0,0, 0.8],
+          [1,0,1, 0.8],
+          [1,1,0, 0.8],
+          #[0.5,0.5,0.5,0.8],
+          # gray doesnt have sufficient contrast against background
+          [0.5,0.5,1, 0.8],
+          [0.5,1,0.5, 0.8],
+          [0.5,1,1, 0.8],
+          [1,0.5,0.5, 0.8],
+          [1,0.5,1, 0.8],
+          [1,1,0.5, 0.8],
+          [1,1,1, 0.8],
+        ]
+        @width  = 1800
+        @height = 500
+
+        @marginlx = 10
+        @marginrx = 180
+        @rgap = 40
+        @marginy = 10
+    end
+    def val_max(key)
+       max=0
+       @runs.each { |run|
+         val = @vals[key][run]
+         if val and val > max
+           max = val
+         end
+       }
+       max
+    end
+    def val_min(key)
+       min=9999990
+       @runs.each { |run|
+         val = @vals[key][run]
+         min = val  if val and val < min
+       }
+       #min
+       0   # this shows the relative noise in measurements better
+    end
+    def add_run(run)
+        @runs = @runs + [run]
+    end
+    def add_entry(run, name, val)
+        if ! vals[name]
+            @vals[name]=Hash.new
+        end
+        # check if there is an existing value,
+        # and perhaps have different behaviors
+        # associated with 
+        @vals[name][run] = val.to_f
+    end
+
+    def drawbg cr
+      cr.set_source_rgba(0.2, 0.2, 0.2, 1)
+      cr.paint 
+
+      i=0
+        @runs.each { |run|
+         if i % 2 == 1
+           cr.move_to 1.0 * i / @runs.length * (@width - @marginlx- marginrx) + @marginlx, 0 * (@height - @marginy*2) + @marginy
+           cr.line_to 1.0 * i / @runs.length * (@width - @marginlx- marginrx) + @marginlx, 1.0 * (@height - @marginy*2) + @marginy
+           cr.rel_line_to(1.0 / @runs.length * (@width - @marginlx- marginrx), 0)
+           cr.rel_line_to(0, -(@height - @marginy*2))
+
+           cr.set_source_rgba([0.25,0.25,0.25,1])
+           cr.fill
+         end
+         i+=1
+        }
+    end
+
+    def drawtext cr
+      i = 0
+      @runs.each { |run|
+        y = i * 10 + 20
+        while y > @height - @marginy
+          y = y - @height + @marginy + 10
+        end
+        cr.move_to 1.0 * i / @runs.length * (@width - @marginlx- marginrx) + @marginlx, y
+
+        cr.set_source_rgba(0.6,0.6,0.6,1)
+        cr.show_text(run[0..6])
+        i+=1
+      }
+    end
+
+    def draw_limits cr, key
+      cr.move_to @width - @marginrx + @rgap, 20
+      cr.set_source_rgba(1.0, 1.0, 1.0, 1.0)
+      cr.show_text(" #{val_max(key)} ")
+      cr.move_to @width - @marginrx + @rgap, @height - @marginy
+      cr.show_text(" #{val_min(key)} ")
+    end
+
+    def draw_val cr, key, valno
+      min = val_min(key)
+      max = val_max(key)
+
+      cr.set_source_rgba(@colors[valno])
+      cr.move_to(@width - @marginrx + @rgap, valno * 14 + @marginy + 20)
+      cr.show_text(key)
+
+      cr.line_width = 2
+      cr.new_path
+
+      i = 0
+      @runs.each { |run|
+        val = @vals[key][run]
+        if val 
+          cr.line_to 1.0 * (i+0.5) / @runs.length * (@width - @marginlx- marginrx) + @marginlx,
+                     (1.0 - ((val-min) * 1.0 / (max - min))) * (@height - @marginy*2) + @marginy
+        end
+        i = i + 1
+      }
+      cr.stroke
+    end
+
+    def create_report
+      cairo_surface(@width, @height) { |cr|
+        drawbg cr
+        valno = 0
+        @vals.each { |key, value|
+           draw_val cr, key, valno
+           valno += 1
+        }
+        drawtext cr
+        cr.target.write_to_png("report.png")
+
+        valno = 0
+        @vals.each { |key, value|
+           cr.show_page
+           drawbg cr
+           draw_val cr, key, valno
+           drawtext cr
+           draw_limits cr, key
+           valno += 1
+        }
+      }
+    end
+end
+
+generator = Database.new
+
+items = File.open('jobs').each { |rev|
+  rev.strip!
+  generator.add_run(rev)
+  filename = "reports/" + rev;
+  if File.exist?(filename)
+      File.open(filename).each { |line| 
+         if line =~ /^@ (.*):(.*)/
+            generator.add_entry(rev, $1, $2)
+         end
+      }
+  end
+}
+
+generator.create_report
diff --git a/tests/performance/joblist b/tests/performance/joblist
new file mode 100644
index 0000000..ce046cc
--- /dev/null
+++ b/tests/performance/joblist
@@ -0,0 +1,27 @@
+# This file lists the commits that we want to do retrospective testing
+# of, aborting the retrospective testing and adjusting this file will
+# add the commit range to be tested without having to redo the initial one
+# thus allowing a sparse distributed range to first be tested, with
+# detailed ranges added in later.
+
+master~500..master % 32   # every 32th commit, to provide context
+
+# various spans of commits around which "interesting things happen"
+
+#eeac7~1..985a4
+#6c6e93d~1..3142b15
+#0486c56..88b026
+#732eecf..8cfb158
+#b499696..b77d9a6
+#ba09e9c..7bdbbe6
+#b424bd7..c1878 
+#bc58de4..d03c3a6
+#5640a6..a29623e
+#6fd2663..6ec9c32
+# 012e4ab..1a8d577   #  
+#120d759~4..2235e70
+
+# ef8be9e25ebe77fc63055191cc48af53d731c108 - actor: Use paint volumes to always queue clipped redraws
+
+# 5d16000 going up, and 3b78949 going down,. was a case of clipped redraws being broken
+
diff --git a/tests/performance/makejobs.rb b/tests/performance/makejobs.rb
new file mode 100755
index 0000000..79499df
--- /dev/null
+++ b/tests/performance/makejobs.rb
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+
+# this ruby script generates a chronologically sorted 
+
+res = ""
+input = File.read(ARGV[0])
+input = input.gsub(/#.*/, "")
+input.split("\n").each {|a|
+  if a =~ /([^ ]*)\.\.([^ ]*) %(.*)/
+    res += `git log #{$1}..#{$2} | grep '^commit' | sed 's/commit //' | sed -n '0~#{$3}p'`
+  elsif a =~ /([^ ]*)\.\.([^ ]*)/
+    res += `git log #{$1}..#{$2} | grep '^commit' | sed 's/commit //'`
+  else
+    res += `echo #{a}`
+  end
+}
+
+all = `git log | grep '^commit' | sed 's/commit//' `
+all.split("\n").reverse.each {|a|
+  if res.match(a.strip) != nil
+    puts "#{a.strip}"
+  end
+}
+
diff --git a/tests/performance/test-common.h b/tests/performance/test-common.h
new file mode 100644
index 0000000..caabdaf
--- /dev/null
+++ b/tests/performance/test-common.h
@@ -0,0 +1,130 @@
+#include <stdlib.h>
+#include <glib.h>
+#include <clutter/clutter.h>
+
+static GTimer *testtimer = NULL;
+static gint testframes = 0;
+static float testmaxtime = 1.0;
+
+/* initialize environment to be suitable for fps testing */
+void clutter_perf_fps_init (void)
+{
+  /* Force not syncing to vblank, we want free-running maximum FPS */
+  g_setenv ("vblank_mode", "0", FALSE);
+  g_setenv ("CLUTTER_VBLANK", "none", FALSE);
+
+  /* also overrride internal default FPS */
+  g_setenv ("CLUTTER_DEFAULT_FPS", "1000", FALSE);
+
+  if (g_getenv ("CLUTTER_PERFORMANCE_TEST_DURATION"))
+    testmaxtime = atof(g_getenv("CLUTTER_PERFORMANCE_TEST_DURATION"));
+  else
+    testmaxtime = 10.0;
+
+  g_random_set_seed (12345678);
+}
+
+static void perf_stage_paint_cb (ClutterStage *stage, gpointer *data);
+static gboolean perf_fake_mouse_cb (gpointer stage);
+
+void clutter_perf_fps_start (ClutterStage *stage)
+{
+  g_signal_connect (stage, "paint", G_CALLBACK (perf_stage_paint_cb), NULL);
+}
+
+void clutter_perf_fake_mouse (ClutterStage *stage)
+{
+  g_timeout_add (1000/60, perf_fake_mouse_cb, stage);
+}
+
+void clutter_perf_fps_report (const gchar *id)
+{
+  g_print ("\n@ %s: %.2f fps \n",
+       id, testframes / g_timer_elapsed (testtimer, NULL));
+}
+
+static void perf_stage_paint_cb (ClutterStage *stage, gpointer *data)
+{
+  if (!testtimer)
+    testtimer = g_timer_new ();
+  testframes ++;
+  if (g_timer_elapsed (testtimer, NULL) > testmaxtime)
+    {
+      clutter_main_quit ();
+    }
+}
+
+static void wrap (gfloat *value, gfloat min, gfloat max)
+{
+  if (*value > max)
+    *value = min;
+  else if (*value < min)
+    *value = max;
+}
+
+static gboolean perf_fake_mouse_cb (gpointer stage)
+{
+  ClutterEvent *event = clutter_event_new (CLUTTER_MOTION);
+  static ClutterInputDevice *device = NULL;
+  int i;
+  static float x = 0.0;
+  static float y = 0.0;
+  static float xd = 0.0;
+  static float yd = 0.0;
+  static gboolean inited = FALSE;
+
+  gfloat w, h;
+
+  if (!inited) /* XXX:
+                  force clutter to do handle our motion events,
+                  by forcibly updating the input device's state
+                  this shoudl be possible to do in a better
+                  manner in the future, a versioning check
+                  will have to be added when this is possible
+                  without a hack... and the means to do the
+                  hack is deprecated
+                */
+    {
+      ClutterEvent *event2 = clutter_event_new (CLUTTER_ENTER);
+      device = clutter_device_manager_get_core_device (clutter_device_manager_get_default (), CLUTTER_POINTER_DEVICE);
+
+      event2->crossing.stage = stage;
+      event2->crossing.source = stage;
+      event2->crossing.x = 10;
+      event2->crossing.y = 10;
+      event2->crossing.device = device;
+      event2->crossing.related = NULL;
+
+      clutter_input_device_update_from_event (device, event2, TRUE);
+
+      clutter_event_put (event2);
+      clutter_event_free (event2);
+      inited = TRUE;
+    }
+
+  clutter_actor_get_size (stage, &w, &h);
+  event->motion.stage = stage;
+  event->motion.device = device;
+
+  /* called about every 60fps, and do 10 picks per stage */
+  for (i = 0; i < 10; i++)
+    {
+      event->motion.x = x;
+      event->motion.y = y;
+
+      clutter_event_put (event);
+
+      x += xd;
+      y += yd;
+      xd += g_random_double_range (-0.1, 0.1);
+      yd += g_random_double_range (-0.1, 0.1);
+
+      wrap (&x, 0, w);
+      wrap (&y, 0, h);
+
+      xd = CLAMP(xd, -1.3, 1.3);
+      yd = CLAMP(yd, -1.3, 1.3);
+    }
+  clutter_event_free (event);
+  return TRUE;
+}
diff --git a/tests/performance/test-picking.c b/tests/performance/test-picking.c
new file mode 100644
index 0000000..9362d35
--- /dev/null
+++ b/tests/performance/test-picking.c
@@ -0,0 +1,133 @@
+
+#include <math.h>
+#include <stdlib.h>
+#include <clutter/clutter.h>
+#include "test-common.h"
+
+#define N_ACTORS 100
+#define N_EVENTS 5
+
+static gint n_actors = N_ACTORS;
+static gint n_events = N_EVENTS;
+
+static GOptionEntry entries[] = {
+  {
+    "num-actors", 'a',
+    0,
+    G_OPTION_ARG_INT, &n_actors,
+    "Number of actors", "ACTORS"
+  },
+  {
+    "num-events", 'e',
+    0,
+    G_OPTION_ARG_INT, &n_events,
+    "Number of events", "EVENTS"
+  },
+  { NULL }
+};
+
+static gboolean
+motion_event_cb (ClutterActor *actor, ClutterEvent *event, gpointer user_data)
+{
+  return FALSE;
+}
+
+static void
+do_events (ClutterActor *stage)
+{
+  glong i;
+  static gdouble angle = 0;
+
+  for (i = 0; i < n_events; i++)
+    {
+      angle += (2.0 * M_PI) / (gdouble)n_actors;
+      while (angle > M_PI * 2.0)
+        angle -= M_PI * 2.0;
+
+      /* If we synthesized events, they would be motion compressed;
+       * calling get_actor_at_position() doesn't have that problem
+       */
+      clutter_stage_get_actor_at_pos (CLUTTER_STAGE (stage),
+				      CLUTTER_PICK_REACTIVE,
+				      256.0 + 206.0 * cos (angle),
+				      256.0 + 206.0 * sin (angle));
+    }
+}
+
+static gboolean queue_redraw (gpointer data)
+{
+  ClutterActor *stage = CLUTTER_ACTOR (data);
+  clutter_actor_queue_redraw (stage);
+  do_events (stage);
+  return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+  glong i;
+  gdouble angle;
+  const ClutterColor black = { 0x00, 0x00, 0x00, 0xff };
+  ClutterColor color = { 0x00, 0x00, 0x00, 0xff };
+  ClutterActor *stage, *rect;
+
+  clutter_perf_fps_init ();
+
+  if (CLUTTER_INIT_SUCCESS !=
+        clutter_init_with_args (&argc, &argv,
+                                NULL,
+                                entries,
+                                NULL,
+                                NULL))
+    {
+      g_warning ("Failed to initialize clutter");
+      return -1;
+    }
+
+  stage = clutter_stage_get_default ();
+  clutter_actor_set_size (stage, 512, 512);
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &black);
+
+  printf ("Picking performance test with "
+          "%d actors and %d events per frame\n",
+          n_actors,
+          n_events);
+
+  for (i = n_actors - 1; i >= 0; i--)
+    {
+      angle = ((2.0 * M_PI) / (gdouble) n_actors) * i;
+
+      color.red = (1.0 - ABS ((MAX (0, MIN (n_actors/2.0 + 0, i))) /
+                  (gdouble)(n_actors/4.0) - 1.0)) * 255.0;
+      color.green = (1.0 - ABS ((MAX (0, MIN (n_actors/2.0 + 0,
+                    fmod (i + (n_actors/3.0)*2, n_actors)))) /
+                    (gdouble)(n_actors/4) - 1.0)) * 255.0;
+      color.blue = (1.0 - ABS ((MAX (0, MIN (n_actors/2.0 + 0,
+                   fmod ((i + (n_actors/3.0)), n_actors)))) /
+                   (gdouble)(n_actors/4.0) - 1.0)) * 255.0;
+
+      rect = clutter_rectangle_new_with_color (&color);
+      clutter_actor_set_size (rect, 100, 100);
+      clutter_actor_set_anchor_point_from_gravity (rect,
+                                                   CLUTTER_GRAVITY_CENTER);
+      clutter_actor_set_position (rect,
+                                  256 + 206 * cos (angle),
+                                  256 + 206 * sin (angle));
+      clutter_actor_set_reactive (rect, TRUE);
+      g_signal_connect (rect, "motion-event",
+                        G_CALLBACK (motion_event_cb), NULL);
+
+      clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
+    }
+
+  clutter_actor_show (stage);
+
+  clutter_perf_fps_start (CLUTTER_STAGE (stage));
+  g_idle_add  (queue_redraw, (gpointer)stage);
+  clutter_main ();
+  clutter_perf_fps_report ("test-picking");
+
+  return 0;
+}
+
+
diff --git a/tests/performance/test-state-hidden.c b/tests/performance/test-state-hidden.c
new file mode 100644
index 0000000..699beff
--- /dev/null
+++ b/tests/performance/test-state-hidden.c
@@ -0,0 +1,149 @@
+#include <stdlib.h>
+#include <math.h>
+#include <gmodule.h>
+#include <clutter/clutter.h>
+#include "test-common.h"
+
+#define STAGE_WIDTH    160
+#define STAGE_HEIGHT   120
+
+#define ACTOR_WIDTH    8
+#define ACTOR_HEIGHT   8
+
+#define COLS  (STAGE_WIDTH/ACTOR_WIDTH)
+#define ROWS  (STAGE_HEIGHT/ACTOR_HEIGHT)
+#define TOTAL (ROWS*COLS)
+
+
+static void completed (ClutterState *state,
+                       gpointer      data)
+{
+  if (g_str_equal (clutter_state_get_state (state), "right"))
+    {
+      /* skip straight to left state when reaching right */
+      clutter_state_warp_to_state (state, "left");
+    }
+  else if (g_str_equal (clutter_state_get_state (state), "active"))
+      clutter_state_set_state (state, "right");
+  else
+    {
+      clutter_state_set_state (state, "active");
+    }
+}
+
+static ClutterActor *new_rect (gint r,
+                               gint g,
+                               gint b,
+                               gint a)
+{
+  ClutterColor *color = clutter_color_new (r, g, b, a);
+  ClutterActor *group = clutter_group_new ();
+  ClutterActor *rectangle = clutter_rectangle_new_with_color (color);
+
+  gchar *file = g_build_filename (TESTS_DATA_DIR, "redhand.png", NULL);
+
+  g_free (file);
+  clutter_actor_set_size (rectangle, ACTOR_WIDTH,ACTOR_HEIGHT);
+  clutter_color_free (color);
+  clutter_container_add (CLUTTER_CONTAINER (group), rectangle, NULL);
+  return group;
+}
+
+gint
+main (gint    argc,
+      gchar **argv)
+{
+  ClutterColor  black={0,0,0,0xff};
+  ClutterActor *stage;
+  ClutterActor *group;
+  ClutterState *layout_state;
+  gint i;
+
+  clutter_perf_fps_init ();
+  if (CLUTTER_INIT_SUCCESS != clutter_init (&argc, &argv))
+    g_error ("Failed to initialize Clutter");
+
+  stage = clutter_stage_get_default ();
+  group = clutter_group_new ();
+  layout_state = clutter_state_new ();
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &black);
+  clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT);
+
+  for (i=0; i<TOTAL; i++)
+    {
+      ClutterActor *actor;
+      ClutterState *a_state;
+
+      int row = i/COLS;
+      int col = i%COLS;
+
+      actor = new_rect (255 * ( 1.0*col/COLS), 50,
+                        255 * ( 1.0*row/ROWS), 255);
+      clutter_container_add_actor (CLUTTER_CONTAINER (group), actor);
+      clutter_actor_set_position (actor, 320.0, 240.0);
+      clutter_actor_set_reactive (actor, TRUE);
+
+
+      clutter_state_set (layout_state, NULL, "active",
+            actor, "delayed::x", CLUTTER_LINEAR,
+                                         ACTOR_WIDTH * 1.0 * ((TOTAL-1-i) % COLS), 
+                                        ((row*1.0/ROWS))/2, (1.0-(row*1.0/ROWS))/2,
+            actor, "delayed::y", CLUTTER_LINEAR,
+                                         ACTOR_HEIGHT * 1.0 * ((TOTAL-1-i) / COLS),
+                                        ((row*1.0/ROWS))/2, 0.0,
+            actor, "rotation-angle-x", CLUTTER_LINEAR, 0.0,
+            actor, "rotation-angle-y", CLUTTER_LINEAR, 0.0,
+            NULL);
+
+      clutter_state_set (layout_state, NULL, "right",
+            actor, "delayed::x", CLUTTER_LINEAR, STAGE_WIDTH * 1.0,
+                                    ((row*1.0/ROWS))/2,
+                                    (1.0-(row*1.0/ROWS))/2,
+            actor, "delayed::y", CLUTTER_LINEAR, STAGE_HEIGHT * 1.0,
+                                    ((row*1.0/ROWS))/2,
+                                    0.0,
+            NULL);
+
+      clutter_state_set (layout_state, NULL, "left",
+            actor, "rotation-angle-x", CLUTTER_LINEAR, 45.0,
+            actor, "rotation-angle-y", CLUTTER_LINEAR, 5.0,
+            actor, "x", CLUTTER_LINEAR, 0-64.0,
+            actor, "y", CLUTTER_LINEAR, 0-64.0,
+                         NULL);
+
+      a_state = clutter_state_new ();
+      g_object_set_data_full (G_OBJECT (actor), "hover-state-machine",
+                              a_state, g_object_unref);
+
+      clutter_state_set (a_state, NULL, "normal",
+                         actor, "opacity", CLUTTER_LINEAR, 0x77,
+                         actor, "rotation-angle-z", CLUTTER_LINEAR, 0.0,
+                         NULL);
+      clutter_state_set (a_state, NULL, "hover",
+                         actor, "opacity", CLUTTER_LINEAR, 0xff,
+                         actor, "rotation-angle-z", CLUTTER_LINEAR, 10.0,
+                         NULL);
+      clutter_actor_set_opacity (actor, 0x77);
+
+      clutter_state_set_duration (a_state, NULL, NULL, 500);
+    }
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+  clutter_actor_set_opacity (group, 0);
+
+  clutter_state_set_duration (layout_state, NULL, NULL, 1000);
+  clutter_state_set_duration (layout_state, "active", "left", 1400);
+
+  g_signal_connect (layout_state, "completed", G_CALLBACK (completed), NULL);
+
+  clutter_actor_show (stage);
+
+  clutter_state_warp_to_state (layout_state, "left");
+  clutter_state_set_state (layout_state, "active");
+
+  clutter_perf_fps_start (CLUTTER_STAGE (stage));
+  clutter_main ();
+  clutter_perf_fps_report ("test-state-hidden");
+  g_object_unref (layout_state);
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/performance/test-state-interactive.c b/tests/performance/test-state-interactive.c
new file mode 100644
index 0000000..b26b67d
--- /dev/null
+++ b/tests/performance/test-state-interactive.c
@@ -0,0 +1,194 @@
+#include <stdlib.h>
+#include <math.h>
+#include <gmodule.h>
+#include <clutter/clutter.h>
+#include "test-common.h"
+
+#define STAGE_WIDTH    800
+#define STAGE_HEIGHT   600
+
+#define ACTOR_WIDTH    64
+#define ACTOR_HEIGHT   64
+
+#define COLS  (STAGE_WIDTH/ACTOR_WIDTH)
+#define ROWS  (STAGE_HEIGHT/ACTOR_HEIGHT)
+#define TOTAL (ROWS*COLS)
+
+
+static gboolean press_event (ClutterActor *actor,
+                             ClutterEvent *event,
+                             gpointer      user_data)
+{
+  ClutterState *state = CLUTTER_STATE (user_data);
+  clutter_state_set_state (state, "right");
+  return TRUE;
+}
+
+static gboolean release_event (ClutterActor *actor,
+                               ClutterEvent *event,
+                               gpointer      user_data)
+{
+  ClutterState *state = CLUTTER_STATE (user_data);
+  clutter_state_set_state (state, "active");
+  return TRUE;
+}
+
+static gboolean enter_event (ClutterActor *actor,
+                             ClutterEvent *event,
+                             gpointer      user_data)
+{
+  ClutterState *state = CLUTTER_STATE (user_data);
+  clutter_state_set_state (state, "hover");
+  return TRUE;
+}
+
+static gboolean leave_event (ClutterActor *actor,
+                             ClutterEvent *event,
+                             gpointer      user_data)
+{
+  ClutterState *state = CLUTTER_STATE (user_data);
+  clutter_state_set_state (state, "normal");
+  return TRUE;
+}
+
+static void completed (ClutterState *state,
+                       gpointer      data)
+{
+  g_print ("Completed transitioning to state: %s\n",
+           clutter_state_get_state (state));
+
+  if (g_str_equal (clutter_state_get_state (state), "right"))
+    {
+      /* skip straight to left state when reaching right */
+      clutter_state_warp_to_state (state, "left");
+    }
+}
+
+static ClutterActor *new_rect (gint r,
+                               gint g,
+                               gint b,
+                               gint a)
+{
+  GError *error = NULL;
+  ClutterColor *color = clutter_color_new (r, g, b, a);
+  ClutterActor *group = clutter_group_new ();
+  ClutterActor *rectangle = clutter_rectangle_new_with_color (color);
+  ClutterActor *hand = NULL;
+
+  gchar *file = g_build_filename (TESTS_DATA_DIR, "redhand.png", NULL);
+
+  hand = clutter_texture_new_from_file (file, &error);
+  if (rectangle == NULL)
+    g_error ("image load failed: %s", error->message);
+  g_free (file);
+  clutter_actor_set_size (hand, ACTOR_WIDTH,ACTOR_HEIGHT);
+
+  clutter_actor_set_size (rectangle, ACTOR_WIDTH,ACTOR_HEIGHT);
+  clutter_color_free (color);
+  clutter_container_add (CLUTTER_CONTAINER (group), rectangle, hand, NULL);
+  return group;
+}
+
+gint
+main (gint    argc,
+      gchar **argv)
+{
+  ClutterColor  black={0,0,0,0xff};
+  ClutterActor *stage;
+  ClutterState *layout_state;
+  gint i;
+  clutter_perf_fps_init ();
+  if (CLUTTER_INIT_SUCCESS != clutter_init (&argc, &argv))
+    g_error ("Failed to initialize Clutter");
+
+  stage = clutter_stage_get_default ();
+  layout_state = clutter_state_new ();
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &black);
+  clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT);
+
+  g_signal_connect (stage, "button-press-event",
+                    G_CALLBACK (press_event), layout_state);
+  g_signal_connect (stage, "button-release-event",
+                    G_CALLBACK (release_event), layout_state);
+
+  for (i=0; i<TOTAL; i++)
+    {
+      ClutterActor *actor;
+      ClutterState *a_state;
+
+      int row = i/COLS;
+      int col = i%COLS;
+
+      actor = new_rect (255 * ( 1.0*col/COLS), 50,
+                        255 * ( 1.0*row/ROWS), 255);
+      clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
+      clutter_actor_set_position (actor, 320.0, 240.0);
+      clutter_actor_set_reactive (actor, TRUE);
+
+
+      clutter_state_set (layout_state, NULL, "active",
+            actor, "delayed::x", CLUTTER_LINEAR,
+                                         ACTOR_WIDTH * 1.0 * ((TOTAL-1-i) % COLS), 
+                                        ((row*1.0/ROWS))/2, (1.0-(row*1.0/ROWS))/2,
+            actor, "delayed::y", CLUTTER_LINEAR,
+                                         ACTOR_HEIGHT * 1.0 * ((TOTAL-1-i) / COLS),
+                                        ((row*1.0/ROWS))/2, 0.0,
+            actor, "rotation-angle-x", CLUTTER_LINEAR, 0.0,
+            actor, "rotation-angle-y", CLUTTER_LINEAR, 0.0,
+            NULL);
+
+      clutter_state_set (layout_state, NULL, "right",
+            actor, "delayed::x", CLUTTER_LINEAR, STAGE_WIDTH * 1.0,
+                                    ((row*1.0/ROWS))/2,
+                                    (1.0-(row*1.0/ROWS))/2,
+            actor, "delayed::y", CLUTTER_LINEAR, STAGE_HEIGHT * 1.0,
+                                    ((row*1.0/ROWS))/2,
+                                    0.0,
+            NULL);
+
+      clutter_state_set (layout_state, NULL, "left",
+            actor, "rotation-angle-x", CLUTTER_LINEAR, 45.0,
+            actor, "rotation-angle-y", CLUTTER_LINEAR, 5.0,
+            actor, "x", CLUTTER_LINEAR, 0-64.0,
+            actor, "y", CLUTTER_LINEAR, 0-64.0,
+                         NULL);
+
+      a_state = clutter_state_new ();
+      g_object_set_data_full (G_OBJECT (actor), "hover-state-machine",
+                              a_state, g_object_unref);
+      g_signal_connect (actor, "enter-event",
+                        G_CALLBACK (enter_event), a_state);
+      g_signal_connect (actor, "leave-event",
+                        G_CALLBACK (leave_event), a_state);
+
+      clutter_state_set (a_state, NULL, "normal",
+                         actor, "opacity", CLUTTER_LINEAR, 0x77,
+                         actor, "rotation-angle-z", CLUTTER_LINEAR, 0.0,
+                         NULL);
+      clutter_state_set (a_state, NULL, "hover",
+                         actor, "opacity", CLUTTER_LINEAR, 0xff,
+                         actor, "rotation-angle-z", CLUTTER_LINEAR, 10.0,
+                         NULL);
+      clutter_actor_set_opacity (actor, 0x77);
+
+      clutter_state_set_duration (a_state, NULL, NULL, 500);
+    }
+
+  clutter_state_set_duration (layout_state, NULL, NULL, 1000);
+  clutter_state_set_duration (layout_state, "active", "left", 1400);
+
+  g_signal_connect (layout_state, "completed", G_CALLBACK (completed), NULL);
+
+  clutter_actor_show (stage);
+
+  clutter_state_warp_to_state (layout_state, "left");
+  clutter_state_set_state (layout_state, "active");
+
+  clutter_perf_fake_mouse (CLUTTER_STAGE (stage));
+  clutter_perf_fps_start (CLUTTER_STAGE (stage));
+  clutter_main ();
+  clutter_perf_fps_report ("test-state-interactive");
+  g_object_unref (layout_state);
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/performance/test-state-mini.c b/tests/performance/test-state-mini.c
new file mode 100644
index 0000000..9120036
--- /dev/null
+++ b/tests/performance/test-state-mini.c
@@ -0,0 +1,152 @@
+#include <stdlib.h>
+#include <math.h>
+#include <gmodule.h>
+#include <clutter/clutter.h>
+#include "test-common.h"
+
+#define STAGE_WIDTH    160
+#define STAGE_HEIGHT   120
+
+#define ACTOR_WIDTH    8
+#define ACTOR_HEIGHT   8
+
+#define COLS  (STAGE_WIDTH/ACTOR_WIDTH)
+#define ROWS  (STAGE_HEIGHT/ACTOR_HEIGHT)
+#define TOTAL (ROWS*COLS)
+
+
+static void completed (ClutterState *state,
+                       gpointer      data)
+{
+  if (g_str_equal (clutter_state_get_state (state), "right"))
+    {
+      /* skip straight to left state when reaching right */
+      clutter_state_warp_to_state (state, "left");
+    }
+  else if (g_str_equal (clutter_state_get_state (state), "active"))
+      clutter_state_set_state (state, "right");
+  else
+    {
+      clutter_state_set_state (state, "active");
+    }
+}
+
+static ClutterActor *new_rect (gint r,
+                               gint g,
+                               gint b,
+                               gint a)
+{
+  GError *error = NULL;
+  ClutterColor *color = clutter_color_new (r, g, b, a);
+  ClutterActor *group = clutter_group_new ();
+  ClutterActor *rectangle = clutter_rectangle_new_with_color (color);
+  ClutterActor *hand = NULL;
+
+  gchar *file = g_build_filename (TESTS_DATA_DIR, "redhand.png", NULL);
+
+  hand = clutter_texture_new_from_file (file, &error);
+  if (rectangle == NULL)
+    g_error ("image load failed: %s", error->message);
+  g_free (file);
+  clutter_actor_set_size (hand, ACTOR_WIDTH,ACTOR_HEIGHT);
+
+  clutter_actor_set_size (rectangle, ACTOR_WIDTH,ACTOR_HEIGHT);
+  clutter_color_free (color);
+  clutter_container_add (CLUTTER_CONTAINER (group), rectangle, hand, NULL);
+  return group;
+}
+
+gint
+main (gint    argc,
+      gchar **argv)
+{
+  ClutterColor  black={0,0,0,0xff};
+  ClutterActor *stage;
+  ClutterState *layout_state;
+  gint i;
+
+  clutter_perf_fps_init ();
+  if (CLUTTER_INIT_SUCCESS != clutter_init (&argc, &argv))
+    g_error ("Failed to initialize Clutter");
+
+  stage = clutter_stage_get_default ();
+  layout_state = clutter_state_new ();
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &black);
+  clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT);
+
+  for (i=0; i<TOTAL; i++)
+    {
+      ClutterActor *actor;
+      ClutterState *a_state;
+
+      int row = i/COLS;
+      int col = i%COLS;
+
+      actor = new_rect (255 * ( 1.0*col/COLS), 50,
+                        255 * ( 1.0*row/ROWS), 255);
+      clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
+      clutter_actor_set_position (actor, 320.0, 240.0);
+      clutter_actor_set_reactive (actor, TRUE);
+
+
+      clutter_state_set (layout_state, NULL, "active",
+            actor, "delayed::x", CLUTTER_LINEAR,
+                                         ACTOR_WIDTH * 1.0 * ((TOTAL-1-i) % COLS), 
+                                        ((row*1.0/ROWS))/2, (1.0-(row*1.0/ROWS))/2,
+            actor, "delayed::y", CLUTTER_LINEAR,
+                                         ACTOR_HEIGHT * 1.0 * ((TOTAL-1-i) / COLS),
+                                        ((row*1.0/ROWS))/2, 0.0,
+            actor, "rotation-angle-x", CLUTTER_LINEAR, 0.0,
+            actor, "rotation-angle-y", CLUTTER_LINEAR, 0.0,
+            NULL);
+
+      clutter_state_set (layout_state, NULL, "right",
+            actor, "delayed::x", CLUTTER_LINEAR, STAGE_WIDTH * 1.0,
+                                    ((row*1.0/ROWS))/2,
+                                    (1.0-(row*1.0/ROWS))/2,
+            actor, "delayed::y", CLUTTER_LINEAR, STAGE_HEIGHT * 1.0,
+                                    ((row*1.0/ROWS))/2,
+                                    0.0,
+            NULL);
+
+      clutter_state_set (layout_state, NULL, "left",
+            actor, "rotation-angle-x", CLUTTER_LINEAR, 45.0,
+            actor, "rotation-angle-y", CLUTTER_LINEAR, 5.0,
+            actor, "x", CLUTTER_LINEAR, 0-64.0,
+            actor, "y", CLUTTER_LINEAR, 0-64.0,
+                         NULL);
+
+      a_state = clutter_state_new ();
+      g_object_set_data_full (G_OBJECT (actor), "hover-state-machine",
+                              a_state, g_object_unref);
+
+      clutter_state_set (a_state, NULL, "normal",
+                         actor, "opacity", CLUTTER_LINEAR, 0x77,
+                         actor, "rotation-angle-z", CLUTTER_LINEAR, 0.0,
+                         NULL);
+      clutter_state_set (a_state, NULL, "hover",
+                         actor, "opacity", CLUTTER_LINEAR, 0xff,
+                         actor, "rotation-angle-z", CLUTTER_LINEAR, 10.0,
+                         NULL);
+      clutter_actor_set_opacity (actor, 0x77);
+
+      clutter_state_set_duration (a_state, NULL, NULL, 500);
+    }
+
+  clutter_state_set_duration (layout_state, NULL, NULL, 1000);
+  clutter_state_set_duration (layout_state, "active", "left", 1400);
+
+  g_signal_connect (layout_state, "completed", G_CALLBACK (completed), NULL);
+
+  clutter_actor_show (stage);
+
+  clutter_state_warp_to_state (layout_state, "left");
+  clutter_state_set_state (layout_state, "active");
+
+  clutter_perf_fps_start (CLUTTER_STAGE (stage));
+  clutter_main ();
+  clutter_perf_fps_report ("test-state-mini");
+  g_object_unref (layout_state);
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/performance/test-state-pick.c b/tests/performance/test-state-pick.c
new file mode 100644
index 0000000..c5e0ba6
--- /dev/null
+++ b/tests/performance/test-state-pick.c
@@ -0,0 +1,158 @@
+#include <stdlib.h>
+#include <math.h>
+#include <gmodule.h>
+#include <clutter/clutter.h>
+#include "test-common.h"
+
+static gint times = 16;
+
+#define STAGE_WIDTH    800
+#define STAGE_HEIGHT   600
+
+#define ACTOR_WIDTH    64
+#define ACTOR_HEIGHT   64
+
+#define COLS  (STAGE_WIDTH/ACTOR_WIDTH)
+#define ROWS  (STAGE_HEIGHT/ACTOR_HEIGHT)
+#define TOTAL (ROWS*COLS)
+
+
+static void completed (ClutterState *state,
+                       gpointer      data)
+{
+  if (g_str_equal (clutter_state_get_state (state), "right"))
+    {
+      /* skip straight to left state when reaching right */
+      clutter_state_warp_to_state (state, "left");
+    }
+  else if (g_str_equal (clutter_state_get_state (state), "active"))
+      clutter_state_set_state (state, "right");
+  else
+    {
+      clutter_state_set_state (state, "active");
+    }
+  times --;
+  if (times <=0)
+    clutter_main_quit ();
+}
+
+static ClutterActor *new_rect (gint r,
+                               gint g,
+                               gint b,
+                               gint a)
+{
+  GError *error = NULL;
+  ClutterColor *color = clutter_color_new (r, g, b, a);
+  ClutterActor *group = clutter_group_new ();
+  ClutterActor *rectangle = clutter_rectangle_new_with_color (color);
+  ClutterActor *hand = NULL;
+
+  gchar *file = g_build_filename (TESTS_DATA_DIR, "redhand.png", NULL);
+
+  hand = clutter_texture_new_from_file (file, &error);
+  if (rectangle == NULL)
+    g_error ("image load failed: %s", error->message);
+  g_free (file);
+  clutter_actor_set_size (hand, ACTOR_WIDTH,ACTOR_HEIGHT);
+
+  clutter_actor_set_size (rectangle, ACTOR_WIDTH,ACTOR_HEIGHT);
+  clutter_color_free (color);
+  clutter_container_add (CLUTTER_CONTAINER (group), rectangle, hand, NULL);
+  return group;
+}
+
+gint
+main (gint    argc,
+      gchar **argv)
+{
+  ClutterColor  black={0,0,0,0xff};
+  ClutterActor *stage;
+  ClutterState *layout_state;
+  gint i;
+
+  clutter_perf_fps_init ();
+  if (CLUTTER_INIT_SUCCESS != clutter_init (&argc, &argv))
+    g_error ("Failed to initialize Clutter");
+
+  stage = clutter_stage_get_default ();
+  layout_state = clutter_state_new ();
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &black);
+  clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT);
+
+  for (i=0; i<TOTAL; i++)
+    {
+      ClutterActor *actor;
+      ClutterState *a_state;
+
+      int row = i/COLS;
+      int col = i%COLS;
+
+      actor = new_rect (255 * ( 1.0*col/COLS), 50,
+                        255 * ( 1.0*row/ROWS), 255);
+      clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
+      clutter_actor_set_position (actor, 320.0, 240.0);
+      clutter_actor_set_reactive (actor, TRUE);
+
+
+      clutter_state_set (layout_state, NULL, "active",
+            actor, "delayed::x", CLUTTER_LINEAR,
+                                         ACTOR_WIDTH * 1.0 * ((TOTAL-1-i) % COLS), 
+                                        ((row*1.0/ROWS))/2, (1.0-(row*1.0/ROWS))/2,
+            actor, "delayed::y", CLUTTER_LINEAR,
+                                         ACTOR_HEIGHT * 1.0 * ((TOTAL-1-i) / COLS),
+                                        ((row*1.0/ROWS))/2, 0.0,
+            actor, "rotation-angle-x", CLUTTER_LINEAR, 0.0,
+            actor, "rotation-angle-y", CLUTTER_LINEAR, 0.0,
+            NULL);
+
+      clutter_state_set (layout_state, NULL, "right",
+            actor, "delayed::x", CLUTTER_LINEAR, STAGE_WIDTH * 1.0,
+                                    ((row*1.0/ROWS))/2,
+                                    (1.0-(row*1.0/ROWS))/2,
+            actor, "delayed::y", CLUTTER_LINEAR, STAGE_HEIGHT * 1.0,
+                                    ((row*1.0/ROWS))/2,
+                                    0.0,
+            NULL);
+
+      clutter_state_set (layout_state, NULL, "left",
+            actor, "rotation-angle-x", CLUTTER_LINEAR, 45.0,
+            actor, "rotation-angle-y", CLUTTER_LINEAR, 5.0,
+            actor, "x", CLUTTER_LINEAR, 0-64.0,
+            actor, "y", CLUTTER_LINEAR, 0-64.0,
+                         NULL);
+
+      a_state = clutter_state_new ();
+      g_object_set_data_full (G_OBJECT (actor), "hover-state-machine",
+                              a_state, g_object_unref);
+
+      clutter_state_set (a_state, NULL, "normal",
+                         actor, "opacity", CLUTTER_LINEAR, 0x77,
+                         actor, "rotation-angle-z", CLUTTER_LINEAR, 0.0,
+                         NULL);
+      clutter_state_set (a_state, NULL, "hover",
+                         actor, "opacity", CLUTTER_LINEAR, 0xff,
+                         actor, "rotation-angle-z", CLUTTER_LINEAR, 10.0,
+                         NULL);
+      clutter_actor_set_opacity (actor, 0x77);
+
+      clutter_state_set_duration (a_state, NULL, NULL, 500);
+    }
+
+  clutter_state_set_duration (layout_state, NULL, NULL, 1000);
+  clutter_state_set_duration (layout_state, "active", "left", 1400);
+
+  g_signal_connect (layout_state, "completed", G_CALLBACK (completed), NULL);
+
+  clutter_actor_show (stage);
+
+  clutter_state_warp_to_state (layout_state, "left");
+  clutter_state_set_state (layout_state, "active");
+
+  clutter_perf_fake_mouse (CLUTTER_STAGE (stage));
+  clutter_perf_fps_start (CLUTTER_STAGE (stage));
+  clutter_main ();
+  clutter_perf_fps_report ("test-state-pick");
+  g_object_unref (layout_state);
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/performance/test-state.c b/tests/performance/test-state.c
new file mode 100644
index 0000000..d931a6e
--- /dev/null
+++ b/tests/performance/test-state.c
@@ -0,0 +1,157 @@
+#include <stdlib.h>
+#include <math.h>
+#include <gmodule.h>
+#include <clutter/clutter.h>
+#include "test-common.h"
+
+static gint times = 16;
+
+#define STAGE_WIDTH    800
+#define STAGE_HEIGHT   600
+
+#define ACTOR_WIDTH    64
+#define ACTOR_HEIGHT   64
+
+#define COLS  (STAGE_WIDTH/ACTOR_WIDTH)
+#define ROWS  (STAGE_HEIGHT/ACTOR_HEIGHT)
+#define TOTAL (ROWS*COLS)
+
+
+static void completed (ClutterState *state,
+                       gpointer      data)
+{
+  if (g_str_equal (clutter_state_get_state (state), "right"))
+    {
+      /* skip straight to left state when reaching right */
+      clutter_state_warp_to_state (state, "left");
+    }
+  else if (g_str_equal (clutter_state_get_state (state), "active"))
+      clutter_state_set_state (state, "right");
+  else
+    {
+      clutter_state_set_state (state, "active");
+    }
+  times --;
+  if (times <=0)
+    clutter_main_quit ();
+}
+
+static ClutterActor *new_rect (gint r,
+                               gint g,
+                               gint b,
+                               gint a)
+{
+  GError *error = NULL;
+  ClutterColor *color = clutter_color_new (r, g, b, a);
+  ClutterActor *group = clutter_group_new ();
+  ClutterActor *rectangle = clutter_rectangle_new_with_color (color);
+  ClutterActor *hand = NULL;
+
+  gchar *file = g_build_filename (TESTS_DATA_DIR, "redhand.png", NULL);
+
+  hand = clutter_texture_new_from_file (file, &error);
+  if (rectangle == NULL)
+    g_error ("image load failed: %s", error->message);
+  g_free (file);
+  clutter_actor_set_size (hand, ACTOR_WIDTH,ACTOR_HEIGHT);
+
+  clutter_actor_set_size (rectangle, ACTOR_WIDTH,ACTOR_HEIGHT);
+  clutter_color_free (color);
+  clutter_container_add (CLUTTER_CONTAINER (group), rectangle, hand, NULL);
+  return group;
+}
+
+gint
+main (gint    argc,
+      gchar **argv)
+{
+  ClutterColor  black={0,0,0,0xff};
+  ClutterActor *stage;
+  ClutterState *layout_state;
+  gint i;
+
+  clutter_perf_fps_init ();
+  if (CLUTTER_INIT_SUCCESS != clutter_init (&argc, &argv))
+    g_error ("Failed to initialize Clutter");
+
+  stage = clutter_stage_get_default ();
+  layout_state = clutter_state_new ();
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &black);
+  clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT);
+
+  for (i=0; i<TOTAL; i++)
+    {
+      ClutterActor *actor;
+      ClutterState *a_state;
+
+      int row = i/COLS;
+      int col = i%COLS;
+
+      actor = new_rect (255 * ( 1.0*col/COLS), 50,
+                        255 * ( 1.0*row/ROWS), 255);
+      clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
+      clutter_actor_set_position (actor, 320.0, 240.0);
+      clutter_actor_set_reactive (actor, TRUE);
+
+
+      clutter_state_set (layout_state, NULL, "active",
+            actor, "delayed::x", CLUTTER_LINEAR,
+                                         ACTOR_WIDTH * 1.0 * ((TOTAL-1-i) % COLS), 
+                                        ((row*1.0/ROWS))/2, (1.0-(row*1.0/ROWS))/2,
+            actor, "delayed::y", CLUTTER_LINEAR,
+                                         ACTOR_HEIGHT * 1.0 * ((TOTAL-1-i) / COLS),
+                                        ((row*1.0/ROWS))/2, 0.0,
+            actor, "rotation-angle-x", CLUTTER_LINEAR, 0.0,
+            actor, "rotation-angle-y", CLUTTER_LINEAR, 0.0,
+            NULL);
+
+      clutter_state_set (layout_state, NULL, "right",
+            actor, "delayed::x", CLUTTER_LINEAR, STAGE_WIDTH * 1.0,
+                                    ((row*1.0/ROWS))/2,
+                                    (1.0-(row*1.0/ROWS))/2,
+            actor, "delayed::y", CLUTTER_LINEAR, STAGE_HEIGHT * 1.0,
+                                    ((row*1.0/ROWS))/2,
+                                    0.0,
+            NULL);
+
+      clutter_state_set (layout_state, NULL, "left",
+            actor, "rotation-angle-x", CLUTTER_LINEAR, 45.0,
+            actor, "rotation-angle-y", CLUTTER_LINEAR, 5.0,
+            actor, "x", CLUTTER_LINEAR, 0-64.0,
+            actor, "y", CLUTTER_LINEAR, 0-64.0,
+                         NULL);
+
+      a_state = clutter_state_new ();
+      g_object_set_data_full (G_OBJECT (actor), "hover-state-machine",
+                              a_state, g_object_unref);
+
+      clutter_state_set (a_state, NULL, "normal",
+                         actor, "opacity", CLUTTER_LINEAR, 0x77,
+                         actor, "rotation-angle-z", CLUTTER_LINEAR, 0.0,
+                         NULL);
+      clutter_state_set (a_state, NULL, "hover",
+                         actor, "opacity", CLUTTER_LINEAR, 0xff,
+                         actor, "rotation-angle-z", CLUTTER_LINEAR, 10.0,
+                         NULL);
+      clutter_actor_set_opacity (actor, 0x77);
+
+      clutter_state_set_duration (a_state, NULL, NULL, 500);
+    }
+
+  clutter_state_set_duration (layout_state, NULL, NULL, 1000);
+  clutter_state_set_duration (layout_state, "active", "left", 1400);
+
+  g_signal_connect (layout_state, "completed", G_CALLBACK (completed), NULL);
+
+  clutter_actor_show (stage);
+
+  clutter_state_warp_to_state (layout_state, "left");
+  clutter_state_set_state (layout_state, "active");
+
+  clutter_perf_fps_start (CLUTTER_STAGE (stage));
+  clutter_main ();
+  clutter_perf_fps_report ("test-state");
+  g_object_unref (layout_state);
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/performance/test-text-perf.c b/tests/performance/test-text-perf.c
new file mode 100644
index 0000000..9f9ae80
--- /dev/null
+++ b/tests/performance/test-text-perf.c
@@ -0,0 +1,164 @@
+#include <clutter/clutter.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include "test-common.h"
+
+#define STAGE_WIDTH  800
+#define STAGE_HEIGHT 600
+
+static int font_size;
+static int n_chars;
+static int rows, cols;
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+  return TRUE;
+}
+
+static gunichar
+get_character (int ch)
+{
+  int total_letters = 0;
+  int i;
+
+  static const struct
+  {
+    gunichar first_letter;
+    int n_letters;
+  }
+  ranges[] =
+    {
+      { 'a', 26 }, /* lower case letters */
+      { 'A', 26 }, /* upper case letters */
+      { '0', 10 }, /* digits */
+      { 0x410, 0x40 }, /* cyrillic alphabet */
+      { 0x3b1, 18 } /* greek alphabet */
+    };
+
+  for (i = 0; i < G_N_ELEMENTS (ranges); i++)
+    total_letters += ranges[i].n_letters;
+
+  ch %= total_letters;
+
+  for (i = 0; i < G_N_ELEMENTS (ranges) - 1; i++)
+    if (ch < ranges[i].n_letters)
+      return ch + ranges[i].first_letter;
+    else
+      ch -= ranges[i].n_letters;
+
+  return ch + ranges[i].first_letter;
+}
+
+static ClutterActor *
+create_label (void)
+{
+  ClutterColor label_color = { 0xff, 0xff, 0xff, 0xff };
+  ClutterActor *label;
+  char         *font_name;
+  GString      *str;
+  int           i;
+
+  font_name = g_strdup_printf ("Monospace %dpx", font_size);
+
+  str = g_string_new (NULL);
+  for (i = 0; i < n_chars; i++)
+    g_string_append_unichar (str, get_character (i));
+
+  label = clutter_text_new_with_text (font_name, str->str);
+  clutter_text_set_color (CLUTTER_TEXT (label), &label_color);
+
+  g_free (font_name);
+  g_string_free (str, TRUE);
+
+  return label;
+}
+
+int
+main (int argc, char *argv[])
+{
+  ClutterActor    *stage;
+  ClutterColor     stage_color = { 0x00, 0x00, 0x00, 0xff };
+  ClutterActor    *label;
+  int              w, h;
+  int              row, col;
+  float            scale = 1.0f;
+
+  clutter_perf_fps_init ();
+
+  if (CLUTTER_INIT_SUCCESS != clutter_init (&argc, &argv))
+    g_error ("Failed to initialize Clutter");
+
+  if (argc != 3)
+    {
+      //g_printerr ("Usage test-text-perf FONT_SIZE N_CHARS\n");
+      //exit (1);
+      font_size = 30;
+      n_chars = 400;
+    }
+  else
+    {
+      font_size = atoi (argv[1]);
+      n_chars = atoi (argv[2]);
+    }
+
+  g_print ("Monospace %dpx, string length = %d\n", font_size, n_chars);
+
+  stage = clutter_stage_get_default ();
+  clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT);
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+  label = create_label ();
+  w = clutter_actor_get_width (label);
+  h = clutter_actor_get_height (label);
+
+  /* If the label is too big to fit on the stage then scale it so that
+     it will fit */
+  if (w > STAGE_WIDTH || h > STAGE_HEIGHT)
+    {
+      float x_scale = STAGE_WIDTH / (float) w;
+      float y_scale = STAGE_HEIGHT / (float) h;
+
+      if (x_scale < y_scale)
+        {
+          scale = x_scale;
+          cols = 1;
+          rows = STAGE_HEIGHT / (h * scale);
+        }
+      else
+        {
+          scale = y_scale;
+          cols = STAGE_WIDTH / (w * scale);
+          rows = 1;
+        }
+
+      g_print ("Text scaled by %f to fit on the stage\n", scale);
+    }
+  else
+    {
+      cols = STAGE_WIDTH / w;
+      rows = STAGE_HEIGHT / h;
+    }
+
+  clutter_actor_destroy (label);
+
+  for (row=0; row<rows; row++)
+    for (col=0; col<cols; col++)
+      {
+	label = create_label();
+        clutter_actor_set_scale (label, scale, scale);
+	clutter_actor_set_position (label, w * col * scale, h * row * scale);
+	clutter_container_add_actor (CLUTTER_CONTAINER (stage), label);
+      }
+
+  clutter_actor_show_all (stage);
+
+  clutter_perf_fps_start (CLUTTER_STAGE (stage));
+  g_idle_add (queue_redraw, stage);
+  clutter_main ();
+  clutter_perf_fps_report ("test-text-perf");
+
+  return 0;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]