diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am
index 7a5b6f971..442800d63 100644
--- a/tests/conform/Makefile.am
+++ b/tests/conform/Makefile.am
@@ -35,6 +35,7 @@ test_conformance_SOURCES = 		\
 	test-materials.c		\
 	test-group.c			\
 	test-actor-size.c		\
+	test-texture-fbo.c		\
         $(NULL)
 
 # For convenience, this provides a way to easily run individual unit tests:
diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c
index 2b2624434..3379fbabf 100644
--- a/tests/conform/test-conform-main.c
+++ b/tests/conform/test-conform-main.c
@@ -147,6 +147,7 @@ main (int argc, char **argv)
   TEST_CONFORM_SIMPLE ("/texture", test_backface_culling);
   TEST_CONFORM_SIMPLE ("/texture", test_npot_texture);
   TEST_CONFORM_SIMPLE ("/texture", test_premult);
+  TEST_CONFORM_SIMPLE ("/texture", test_texture_fbo);
 
   TEST_CONFORM_SIMPLE ("/path", test_path);
 
diff --git a/tests/conform/test-texture-fbo.c b/tests/conform/test-texture-fbo.c
new file mode 100644
index 000000000..b11bde080
--- /dev/null
+++ b/tests/conform/test-texture-fbo.c
@@ -0,0 +1,255 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+#define SOURCE_SIZE        32
+#define SOURCE_DIVISIONS_X 2
+#define SOURCE_DIVISIONS_Y 2
+#define DIVISION_WIDTH     (SOURCE_SIZE / SOURCE_DIVISIONS_X)
+#define DIVISION_HEIGHT    (SOURCE_SIZE / SOURCE_DIVISIONS_Y)
+
+static const ClutterColor
+corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] =
+  {
+    { 0xff, 0x00, 0x00, 0xff },
+    { 0x00, 0xff, 0x00, 0xff },
+    { 0x00, 0x00, 0xff, 0xff },
+    { 0xff, 0x00, 0xff, 0xff }
+  };
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+typedef struct _TestState
+{
+  ClutterActor *stage;
+  guint frame;
+} TestState;
+
+static ClutterActor *
+create_source (void)
+{
+  int x, y;
+  ClutterActor *group = clutter_group_new ();
+
+  /* Create a group with a different coloured rectangle at each
+     corner */
+  for (y = 0; y < SOURCE_DIVISIONS_Y; y++)
+    for (x = 0; x < SOURCE_DIVISIONS_X; x++)
+      {
+        ClutterActor *rect = clutter_rectangle_new ();
+        clutter_actor_set_size (rect, DIVISION_WIDTH, DIVISION_HEIGHT);
+        clutter_actor_set_position (rect,
+                                    DIVISION_WIDTH * x,
+                                    DIVISION_HEIGHT * y);
+        clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect),
+                                     corner_colors +
+                                     (y * SOURCE_DIVISIONS_X + x));
+        clutter_container_add (CLUTTER_CONTAINER (group), rect, NULL);
+      }
+
+  return group;
+}
+
+static void
+pre_paint_clip_cb (void)
+{
+  /* Generate a clip path that clips out the top left division */
+  cogl_path_move_to (DIVISION_WIDTH, 0);
+  cogl_path_line_to (SOURCE_SIZE, 0);
+  cogl_path_line_to (SOURCE_SIZE, SOURCE_SIZE);
+  cogl_path_line_to (0, SOURCE_SIZE);
+  cogl_path_line_to (0, DIVISION_HEIGHT);
+  cogl_path_line_to (DIVISION_WIDTH, DIVISION_HEIGHT);
+  cogl_path_close ();
+  cogl_clip_push_from_path ();
+}
+
+static void
+post_paint_clip_cb (void)
+{
+  cogl_clip_pop ();
+}
+
+static gboolean
+validate_part (TestState *state,
+               int xpos, int ypos,
+               int clip_flags)
+{
+  int x, y;
+  gboolean pass = TRUE;
+
+  /* Check whether the center of each division is the right color */
+  for (y = 0; y < SOURCE_DIVISIONS_Y; y++)
+    for (x = 0; x < SOURCE_DIVISIONS_X; x++)
+      {
+        guchar *pixels;
+        const ClutterColor *correct_color;
+
+        /* Read the center pixels of this division */
+        pixels = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage),
+                                            x * DIVISION_WIDTH +
+                                            DIVISION_WIDTH / 2 + xpos,
+                                            y * DIVISION_HEIGHT +
+                                            DIVISION_HEIGHT / 2 + ypos,
+                                            1, 1);
+
+        /* If this division is clipped then it should be the stage
+           color */
+        if ((clip_flags & (1 << ((y * SOURCE_DIVISIONS_X) + x))))
+          correct_color = &stage_color;
+        else
+          /* Otherwise it should be the color for this division */
+          correct_color = corner_colors + (y * SOURCE_DIVISIONS_X) + x;
+
+        if (pixels == NULL ||
+            pixels[0] != correct_color->red ||
+            pixels[1] != correct_color->green ||
+            pixels[2] != correct_color->blue)
+          pass = FALSE;
+
+        g_free (pixels);
+      }
+
+  return pass;
+}
+
+static void
+validate_result (TestState *state)
+{
+  int ypos = 0;
+
+  if (g_test_verbose ())
+    g_print ("Testing onscreen clone...\n");
+  g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0));
+  ypos++;
+
+#if 0 /* this doesn't work */
+  if (g_test_verbose ())
+    g_print ("Testing offscreen clone...\n");
+  g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0));
+#endif
+  ypos++;
+
+  if (g_test_verbose ())
+    g_print ("Testing onscreen clone with rectangular clip...\n");
+  g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, ~1));
+  ypos++;
+
+  if (g_test_verbose ())
+    g_print ("Testing onscreen clone with path clip...\n");
+  g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 1));
+  ypos++;
+
+  /* Comment this out if you want visual feedback of what this test
+   * paints.
+   */
+  clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  int frame_num;
+
+  /* XXX: Experiments have shown that for some buggy drivers, when using
+   * glReadPixels there is some kind of race, so we delay our test for a
+   * few frames and a few seconds:
+   */
+  /* Need to increment frame first because clutter_stage_read_pixels
+     fires a redraw */
+  frame_num = state->frame++;
+  if (frame_num == 2)
+    validate_result (state);
+  else if (frame_num < 2)
+    g_usleep (G_USEC_PER_SEC);
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_texture_fbo (TestConformSimpleFixture *fixture,
+                  gconstpointer data)
+{
+  TestState state;
+  guint idle_source;
+  ClutterActor *actor;
+  int ypos = 0;
+
+  state.frame = 0;
+
+  state.stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color);
+
+  /* Onscreen source with clone next to it */
+  actor = create_source ();
+  clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
+  clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE);
+  actor = clutter_texture_new_from_actor (actor);
+  clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE);
+  clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
+  ypos++;
+
+  /* Offscreen source with clone */
+#if 0 /* this doesn't work */
+  actor = create_source ();
+  actor = clutter_texture_new_from_actor (actor);
+  clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE);
+  clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
+#endif
+  ypos++;
+
+  /* Source clipped to the top left division */
+  actor = create_source ();
+  clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
+  clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE);
+  clutter_actor_set_clip (actor, 0, 0, DIVISION_WIDTH, DIVISION_HEIGHT);
+  actor = clutter_texture_new_from_actor (actor);
+  clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE);
+  clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
+  ypos++;
+
+  /* Source clipped to everything but top left division using a
+     path */
+  actor = create_source ();
+  clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
+  clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE);
+  g_signal_connect (actor, "paint",
+                    G_CALLBACK (pre_paint_clip_cb), NULL);
+  g_signal_connect_after (actor, "paint",
+                          G_CALLBACK (post_paint_clip_cb), NULL);
+  actor = clutter_texture_new_from_actor (actor);
+  clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE);
+  clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL);
+  ypos++;
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, state.stage);
+
+  g_signal_connect_after (state.stage, "paint", G_CALLBACK (on_paint), &state);
+
+  clutter_actor_show_all (state.stage);
+
+  clutter_main ();
+
+  g_source_remove (idle_source);
+
+  /* Remove all of the actors from the stage */
+  clutter_container_foreach (CLUTTER_CONTAINER (state.stage),
+                             (ClutterCallback) clutter_actor_destroy,
+                             NULL);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+