[banshee/stable-vis] Synced up latest Clutter video sink



commit f517f29ca9375df45715e0d57525c88a0832c85c
Author: Aaron Bockover <abockover novell com>
Date:   Fri May 8 20:10:45 2009 -0400

    Synced up latest Clutter video sink
---
 configure.ac                        |    4 +-
 libbanshee/Makefile.am              |    1 +
 libbanshee/clutter-gst-video-sink.c |  980 ++++++++++++++++++++++++++---------
 libbanshee/shaders/I420.h           |   36 ++
 libbanshee/shaders/YV12.h           |   36 ++
 5 files changed, 821 insertions(+), 236 deletions(-)

diff --git a/configure.ac b/configure.ac
index 61a8b9c..726e7b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 dnl Automake setup
-AC_INIT([banshee-1], [1.4.4beta1])
-DISPLAY_VERSION="1.4.4 Beta 1"
+AC_INIT([banshee-1], [1.4.4beta2])
+DISPLAY_VERSION="1.4.4 Beta 2"
 API_VERSION="1.4"
 ASM_VERSION="$API_VERSION.0.0"
 AC_SUBST(API_VERSION)
diff --git a/libbanshee/Makefile.am b/libbanshee/Makefile.am
index 4864c79..bad4ae1 100644
--- a/libbanshee/Makefile.am
+++ b/libbanshee/Makefile.am
@@ -1,5 +1,6 @@
 INCLUDES = \
 	-I$(top_srcdir) \
+	-I$(srcdir)/shaders \
 	-Wall \
 	-ggdb3 \
 	-D_FORTIFY_SOURCE=2 \
diff --git a/libbanshee/clutter-gst-video-sink.c b/libbanshee/clutter-gst-video-sink.c
index 58a80f0..f4e19b1 100644
--- a/libbanshee/clutter-gst-video-sink.c
+++ b/libbanshee/clutter-gst-video-sink.c
@@ -35,10 +35,15 @@
  * data to a #ClutterTexture.
  */
 
+#ifdef HAVE_CONFIG_H
 #include "config.h"
+#endif
 
 #include "clutter-gst-video-sink.h"
 #include "clutter-gst-shaders.h"
+/* include assembly shaders */
+#include "I420.h"
+#include "YV12.h"
 
 #include <gst/gst.h>
 #include <gst/gstvalue.h>
@@ -54,7 +59,6 @@ static gchar *ayuv_to_rgba_shader = \
      "void main () {"
      "  vec4 color = texture2D (tex, vec2(" TEX_COORD "));"
      "  float y = 1.1640625 * (color.g - 0.0625);"
-     "  float u = color.b - 0.5;"
      "  float v = color.a - 0.5;"
      "  color.a = color.r;"
      "  color.r = y + 1.59765625 * v;"
@@ -88,47 +92,16 @@ static gchar *yv12_to_rgba_shader = \
      FRAGMENT_SHADER_END
      "}";
 
-static GstStaticPadTemplate sinktemplate 
- = GST_STATIC_PAD_TEMPLATE ("sink",
-                            GST_PAD_SINK,
-                            GST_PAD_ALWAYS,
-                            GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBx ";"   \
-                                             GST_VIDEO_CAPS_BGRx "; " \
-                                             GST_VIDEO_CAPS_RGB ";"   \
-                                             GST_VIDEO_CAPS_BGR \
-                                             ));
-
-/* Multi-texturing will only be available on GL, so decide on capabilties
- * accordingly.
- */
-
-#ifdef CLUTTER_COGL_HAS_GL
-#define YUV_CAPS GST_VIDEO_CAPS_YUV("AYUV") ";" GST_VIDEO_CAPS_YUV("YV12") ";"
-#else
-#define YUV_CAPS GST_VIDEO_CAPS_YUV("AYUV") ";"
-#endif
-
-/* Don't advertise RGB/BGR as it seems to override yv12, even when it's the
- * better choice. Unfortunately, RGBx/BGRx also override AYUV when it's the
- * better choice too, but that's not quite as bad.
- */
-
-static GstStaticPadTemplate sinktemplate_shaders 
- = GST_STATIC_PAD_TEMPLATE ("sink",
-                            GST_PAD_SINK,
-                            GST_PAD_ALWAYS,
-                            GST_STATIC_CAPS (YUV_CAPS \
-                                             GST_VIDEO_CAPS_RGBx ";" \
-                                             GST_VIDEO_CAPS_BGRx));
-
 static GstStaticPadTemplate sinktemplate_all 
  = GST_STATIC_PAD_TEMPLATE ("sink",
                             GST_PAD_SINK,
                             GST_PAD_ALWAYS,
-                            GST_STATIC_CAPS (YUV_CAPS \
-                                             GST_VIDEO_CAPS_RGBx ";" \
-                                             GST_VIDEO_CAPS_BGRx ";" \
-                                             GST_VIDEO_CAPS_RGB ";" \
+                            GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV("AYUV") ";" \
+                                             GST_VIDEO_CAPS_YUV("YV12") ";" \
+                                             GST_VIDEO_CAPS_YUV("I420") ";" \
+                                             GST_VIDEO_CAPS_RGBA        ";" \
+                                             GST_VIDEO_CAPS_BGRA        ";" \
+                                             GST_VIDEO_CAPS_RGB         ";" \
                                              GST_VIDEO_CAPS_BGR));
 
 GST_DEBUG_CATEGORY_STATIC (clutter_gst_video_sink_debug);
@@ -156,10 +129,60 @@ typedef enum
   CLUTTER_GST_RGB24,
   CLUTTER_GST_AYUV,
   CLUTTER_GST_YV12,
+  CLUTTER_GST_I420,
 } ClutterGstVideoFormat;
 
 typedef void (*GLUNIFORM1IPROC)(COGLint location, COGLint value);
+/* GL_ARB_fragment_program */
+typedef void (*GLGENPROGRAMSPROC)(GLsizei n, COGLuint *programs);
+typedef void (*GLBINDPROGRAMPROC)(GLenum target, COGLint program);
+typedef void (*GLPROGRAMSTRINGPROC)(GLenum target, GLenum format, GLsizei len,
+                                    const void *string);
+/* multi-texturing */
 typedef void (*GLACTIVETEXTUREPROC)(GLenum unit);
+typedef void (*GLMULTITEXCOORD2FPROC)(GLenum target, GLfloat s, GLfloat t);
+
+typedef struct _ClutterGstSymbols
+{
+  /* GL_ARB_fragment_program */
+  GLGENPROGRAMSPROC   glGenProgramsARB;
+  GLBINDPROGRAMPROC   glBindProgramARB;
+  GLPROGRAMSTRINGPROC glProgramStringARB;
+
+  /* multi-texturing */
+  GLACTIVETEXTUREPROC glActiveTextureARB;
+  GLMULTITEXCOORD2FPROC glMultiTexCoord2fARB;
+} ClutterGstSymbols;
+
+/*
+ * features: what does the underlaying video card supports ?
+ */
+typedef enum _ClutterGstFeatures
+{
+  CLUTTER_GST_FP             = 0x1, /* fragment programs (ARB fp1.0) */
+  CLUTTER_GST_GLSL           = 0x2, /* GLSL */
+  CLUTTER_GST_MULTI_TEXTURE  = 0x4, /* multi-texturing */
+} ClutterGstFeatures;
+
+/*
+ * renderer: abstracts a backend to render a frame.
+ */
+typedef struct _ClutterGstRenderer
+{
+ const char            *name;     /* user friendly name */
+ ClutterGstVideoFormat  format;   /* the format handled by this renderer */
+ int                    flags;    /* ClutterGstFeatures ORed flags */
+ GstStaticCaps          caps;     /* caps handled by the renderer */
+
+ void (*init)       (ClutterActor        *actor,
+                     ClutterGstVideoSink *sink);
+ void (*upload)     (ClutterGstVideoSink *sink,
+                     GstBuffer           *buffer);
+ void (*paint)      (ClutterActor        *actor,
+                     ClutterGstVideoSink *sink);
+ void (*post_paint) (ClutterActor        *actor,
+                     ClutterGstVideoSink *sink);
+} ClutterGstRenderer;
 
 struct _ClutterGstVideoSinkPrivate
 {
@@ -168,7 +191,12 @@ struct _ClutterGstVideoSinkPrivate
   CoglHandle             v_tex;
   CoglHandle             program;
   CoglHandle             shader;
-  GAsyncQueue           *async_queue;
+  COGLuint               fp;
+
+  GMutex                *buffer_lock;   /* mutex for the buffer and idle_id */
+  GstBuffer             *buffer;
+  guint                  idle_id;
+
   ClutterGstVideoFormat  format;
   gboolean               bgr;
   int                    width;
@@ -178,8 +206,12 @@ struct _ClutterGstVideoSinkPrivate
   gboolean               use_shaders;
   gboolean               shaders_init;
   
+  ClutterGstSymbols      syms;          /* extra OpenGL functions */
   GLUNIFORM1IPROC        glUniform1iARB;
-  GLACTIVETEXTUREPROC    glActiveTexture;
+
+  GSList                *renderers;
+  GstCaps               *caps;
+  ClutterGstRenderer    *renderer;
 };
 
 
@@ -195,92 +227,70 @@ GST_BOILERPLATE_FULL (ClutterGstVideoSink,
                       GST_TYPE_BASE_SINK,
                       _do_init);
 
+/*
+ * Small helpers
+ */
+
 static void
-clutter_gst_video_sink_base_init (gpointer g_class)
+_string_array_to_char_array (char	*dst,
+                             const char *src[])
 {
-  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+  int i, n;
 
-  gst_element_class_add_pad_template 
-                     (element_class,
-                      gst_static_pad_template_get (&sinktemplate_all));
-
-  gst_element_class_set_details (element_class, 
-                                 &clutter_gst_video_sink_details);
+  for (i = 0; src[i]; i++) {
+      n = strlen (src[i]);
+      memcpy (dst, src[i], n);
+      dst += n;
+  }
+  *dst = '\0';
 }
 
 static void
-clutter_gst_video_sink_init (ClutterGstVideoSink      *sink,
-                             ClutterGstVideoSinkClass *klass)
+clutter_gst_video_sink_fp_paint (ClutterActor        *actor,
+                                ClutterGstVideoSink *sink)
 {
-  ClutterGstVideoSinkPrivate *priv;
-
-  sink->priv = priv =
-    G_TYPE_INSTANCE_GET_PRIVATE (sink, CLUTTER_GST_TYPE_VIDEO_SINK,
-                                 ClutterGstVideoSinkPrivate);
-
-  priv->async_queue = g_async_queue_new ();
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
 
-#ifdef CLUTTER_COGL_HAS_GL
-  priv->glUniform1iARB = (GLUNIFORM1IPROC)
-    cogl_get_proc_address ("glUniform1iARB");
+  glEnable (GL_FRAGMENT_PROGRAM_ARB);
+  priv->syms.glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, priv->fp);
 
-  priv->glActiveTexture = (GLACTIVETEXTUREPROC)
-    cogl_get_proc_address ("glActiveTexture");
-#endif
 }
 
 static void
-clutter_gst_video_sink_paint (ClutterActor        *actor,
-                              ClutterGstVideoSink *sink)
+clutter_gst_video_sink_set_fp_shader (ClutterGstVideoSink *sink,
+                                      const gchar         *shader_src,
+                                      const int            size)
 {
   ClutterGstVideoSinkPrivate *priv = sink->priv;
-  if (priv->program)
-    cogl_program_use (priv->program);
-}
 
-static void
-clutter_gst_yv12_paint (ClutterActor        *actor,
-                        ClutterGstVideoSink *sink)
-{
-#ifdef CLUTTER_COGL_HAS_GL
-  ClutterGstVideoSinkPrivate *priv = sink->priv;
-  GLuint texture;
-  
-  /* Bind the U and V textures in texture units 1 and 2 */
-  if (priv->u_tex)
-    {
-      cogl_texture_get_gl_texture (priv->u_tex, &texture, NULL);
-      priv->glActiveTexture (GL_TEXTURE1);
-      glEnable (GL_TEXTURE_2D);
-      glBindTexture (GL_TEXTURE_2D, texture);
-    }
+  priv->shaders_init = FALSE;
+
+  glEnable (GL_FRAGMENT_PROGRAM_ARB);
+  priv->syms.glGenProgramsARB (1, &priv->fp);
+  priv->syms.glBindProgramARB (GL_FRAGMENT_PROGRAM_ARB, priv->fp);
+  priv->syms.glProgramStringARB (GL_FRAGMENT_PROGRAM_ARB,
+                                  GL_PROGRAM_FORMAT_ASCII_ARB,
+                                  size,
+                                  (const GLbyte *)shader_src);
+
+  glDisable(GL_FRAGMENT_PROGRAM_ARB);
+
+  /* Hook onto the pre-paint signal to bind the shader. */
+  g_signal_connect (priv->texture,
+                    "paint",
+                    G_CALLBACK (clutter_gst_video_sink_fp_paint),
+                    sink);
+  priv->shaders_init = TRUE;
 
-  if (priv->v_tex)
-    {
-      cogl_texture_get_gl_texture (priv->v_tex, &texture, NULL);
-      priv->glActiveTexture (GL_TEXTURE2);
-      glEnable (GL_TEXTURE_2D);
-      glBindTexture (GL_TEXTURE_2D, texture);
-    }
-  
-  priv->glActiveTexture (GL_TEXTURE0_ARB);
-#endif
 }
 
 static void
-clutter_gst_yv12_post_paint (ClutterActor        *actor,
-                             ClutterGstVideoSink *sink)
+clutter_gst_video_sink_paint (ClutterActor        *actor,
+                              ClutterGstVideoSink *sink)
 {
-#ifdef CLUTTER_COGL_HAS_GL
   ClutterGstVideoSinkPrivate *priv = sink->priv;
-
-  /* Disable the extra texture units */
-  priv->glActiveTexture (GL_TEXTURE1);
-  glDisable (GL_TEXTURE_2D);
-  priv->glActiveTexture (GL_TEXTURE2);
-  glDisable (GL_TEXTURE_2D);
-  priv->glActiveTexture (GL_TEXTURE0);
-#endif
+  if (priv->program)
+    cogl_program_use (priv->program);
 }
 
 static void
@@ -338,6 +348,508 @@ clutter_gst_video_sink_set_shader (ClutterGstVideoSink *sink,
     }
 }
 
+/* some renderers don't need all the ClutterGstRenderer vtable */
+static void
+clutter_gst_dummy_init (ClutterActor        *actor,
+                        ClutterGstVideoSink *sink)
+{
+}
+
+/*
+ * RGB 24 / BGR 24
+ *
+ * 3 bytes per pixel, stride % 4 = 0.
+ */
+
+static void
+clutter_gst_rgb24_upload (ClutterGstVideoSink *sink,
+                          GstBuffer           *buffer)
+{
+  ClutterGstVideoSinkPrivate *priv= sink->priv;
+
+  clutter_texture_set_from_rgb_data (priv->texture,
+                                     GST_BUFFER_DATA (buffer),
+                                     FALSE,
+                                     priv->width,
+                                     priv->height,
+                                     GST_ROUND_UP_4 (3 * priv->width),
+                                     3,
+                                     priv->bgr ?
+                                     CLUTTER_TEXTURE_RGB_FLAG_BGR : 0,
+                                     NULL);
+}
+
+static ClutterGstRenderer rgb24_renderer =
+{
+  "RGB 24",
+  CLUTTER_GST_RGB24,
+  0,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR),
+  clutter_gst_dummy_init,
+  clutter_gst_rgb24_upload,
+  NULL,
+  NULL,
+};
+
+/*
+ * RGBA / BGRA 8888
+ */
+
+static void
+clutter_gst_rgb32_upload (ClutterGstVideoSink *sink,
+                          GstBuffer           *buffer)
+{
+  ClutterGstVideoSinkPrivate *priv= sink->priv;
+
+  clutter_texture_set_from_rgb_data (priv->texture,
+                                     GST_BUFFER_DATA (buffer),
+                                     TRUE,
+                                     priv->width,
+                                     priv->height,
+                                     GST_ROUND_UP_4 (4 * priv->width),
+                                     4,
+                                     priv->bgr ?
+                                     CLUTTER_TEXTURE_RGB_FLAG_BGR : 0,
+                                     NULL);
+}
+
+static ClutterGstRenderer rgb32_renderer =
+{
+  "RGB 32",
+  CLUTTER_GST_RGB32,
+  0,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_BGRA),
+  clutter_gst_dummy_init,
+  clutter_gst_rgb32_upload,
+  NULL,
+  NULL,
+};
+
+#ifdef CLUTTER_COGL_HAS_GL
+
+/*
+ * YV12
+ *
+ * 8 bit Y plane followed by 8 bit 2x2 subsampled V and U planes.
+ */
+
+static void
+clutter_gst_yv12_glsl_init (ClutterActor        *actor,
+                            ClutterGstVideoSink *sink)
+{
+  ClutterGstVideoSinkPrivate *priv= sink->priv;
+  COGLint location;
+
+  clutter_gst_video_sink_set_shader (sink,
+                                     yv12_to_rgba_shader);
+
+  cogl_program_use (priv->program);
+  location = cogl_program_get_uniform_location (priv->program, "ytex");
+  priv->glUniform1iARB (location, 0);
+  location = cogl_program_get_uniform_location (priv->program, "utex");
+  priv->glUniform1iARB (location, 1);
+  location = cogl_program_get_uniform_location (priv->program, "vtex");
+  priv->glUniform1iARB (location, 2);
+
+  cogl_program_use (COGL_INVALID_HANDLE);
+}
+
+static void
+clutter_gst_yv12_upload (ClutterGstVideoSink *sink,
+                         GstBuffer           *buffer)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+
+  CoglHandle y_tex = cogl_texture_new_from_data (priv->width,
+                                                 priv->height,
+                                                 -1,
+                                                 COGL_TEXTURE_NONE,
+                                                 COGL_PIXEL_FORMAT_G_8,
+                                                 COGL_PIXEL_FORMAT_G_8,
+                                                 priv->width,
+                                                 GST_BUFFER_DATA (buffer));
+  cogl_texture_set_filters (y_tex, CGL_LINEAR, CGL_LINEAR);
+  clutter_texture_set_cogl_texture (priv->texture, y_tex);
+  cogl_texture_unref (y_tex);
+
+  if (priv->u_tex)
+    cogl_texture_unref (priv->u_tex);
+
+  if (priv->v_tex)
+    cogl_texture_unref (priv->v_tex);
+
+  priv->v_tex = cogl_texture_new_from_data (priv->width/2,
+                                            priv->height/2,
+                                            -1,
+                                            COGL_TEXTURE_NONE,
+                                            COGL_PIXEL_FORMAT_G_8,
+                                            COGL_PIXEL_FORMAT_G_8,
+                                            priv->width/2,
+                                            GST_BUFFER_DATA (buffer) +
+                                            (priv->width * priv->height));
+  cogl_texture_set_filters (priv->v_tex, CGL_LINEAR, CGL_LINEAR);
+
+  priv->u_tex =
+    cogl_texture_new_from_data (priv->width/2,
+                                priv->height/2,
+                                -1,
+                                COGL_TEXTURE_NONE,
+                                COGL_PIXEL_FORMAT_G_8,
+                                COGL_PIXEL_FORMAT_G_8,
+                                priv->width/2,
+                                GST_BUFFER_DATA (buffer) +
+                                (priv->width * priv->height) +
+                                (priv->width/2 * priv->height/2));
+  cogl_texture_set_filters (priv->u_tex, CGL_LINEAR, CGL_LINEAR);
+}
+
+static void
+clutter_gst_yv12_paint (ClutterActor        *actor,
+                        ClutterGstVideoSink *sink)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+  GLuint texture;
+
+  /* Bind the U and V textures in texture units 1 and 2 */
+  if (priv->u_tex)
+    {
+      cogl_texture_get_gl_texture (priv->u_tex, &texture, NULL);
+      priv->syms.glActiveTextureARB (GL_TEXTURE1);
+      glEnable (GL_TEXTURE_2D);
+      glBindTexture (GL_TEXTURE_2D, texture);
+    }
+
+  if (priv->v_tex)
+    {
+      cogl_texture_get_gl_texture (priv->v_tex, &texture, NULL);
+      priv->syms.glActiveTextureARB (GL_TEXTURE2);
+      glEnable (GL_TEXTURE_2D);
+      glBindTexture (GL_TEXTURE_2D, texture);
+    }
+
+  priv->syms.glActiveTextureARB (GL_TEXTURE0_ARB);
+}
+
+static void
+clutter_gst_yv12_glsl_post_paint (ClutterActor        *actor,
+                                  ClutterGstVideoSink *sink)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+
+  /* Disable the extra texture units */
+  priv->syms.glActiveTextureARB (GL_TEXTURE1);
+  glDisable (GL_TEXTURE_2D);
+  priv->syms.glActiveTextureARB (GL_TEXTURE2);
+  glDisable (GL_TEXTURE_2D);
+  priv->syms.glActiveTextureARB (GL_TEXTURE0);
+}
+
+static ClutterGstRenderer yv12_glsl_renderer =
+{
+  "YV12 glsl",
+  CLUTTER_GST_YV12,
+  CLUTTER_GST_GLSL | CLUTTER_GST_MULTI_TEXTURE,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YV12")),
+  clutter_gst_yv12_glsl_init,
+  clutter_gst_yv12_upload,
+  clutter_gst_yv12_paint,
+  clutter_gst_yv12_glsl_post_paint,
+};
+
+/*
+ * YV12 (fragment program version)
+ *
+ * 8 bit Y plane followed by 8 bit 2x2 subsampled V and U planes.
+ */
+
+static void
+clutter_gst_yv12_fp_init (ClutterActor        *actor,
+                          ClutterGstVideoSink *sink)
+{
+  gchar *shader;
+
+  shader = g_malloc(YV12_FP_SZ + 1);
+  _string_array_to_char_array (shader, YV12_fp);
+
+  /* the size given to glProgramStringARB is without the trailing '\0', which
+   * is precisely YV12_FP_SZ */
+  clutter_gst_video_sink_set_fp_shader (sink, shader, YV12_FP_SZ);
+  g_free(shader);
+}
+
+static void
+clutter_gst_yv12_fp_post_paint (ClutterActor        *actor,
+                                ClutterGstVideoSink *sink)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+
+  /* Disable the extra texture units */
+  priv->syms.glActiveTextureARB (GL_TEXTURE1);
+  glDisable (GL_TEXTURE_2D);
+  priv->syms.glActiveTextureARB (GL_TEXTURE2);
+  glDisable (GL_TEXTURE_2D);
+  priv->syms.glActiveTextureARB (GL_TEXTURE0);
+
+  /* Disable the shader */
+  glDisable (GL_FRAGMENT_PROGRAM_ARB);
+}
+
+static ClutterGstRenderer yv12_fp_renderer =
+{
+  "YV12 fp",
+  CLUTTER_GST_YV12,
+  CLUTTER_GST_FP | CLUTTER_GST_MULTI_TEXTURE,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("YV12")),
+  clutter_gst_yv12_fp_init,
+  clutter_gst_yv12_upload,
+  clutter_gst_yv12_paint,
+  clutter_gst_yv12_fp_post_paint,
+};
+
+/*
+ * I420
+ *
+ * 8 bit Y plane followed by 8 bit 2x2 subsampled U and V planes.
+ * Basically the same as YV12, but with the 2 chroma planes switched.
+ */
+
+static void
+clutter_gst_i420_glsl_init (ClutterActor        *actor,
+                            ClutterGstVideoSink *sink)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+  COGLint location;
+
+  clutter_gst_video_sink_set_shader (sink,
+                                     yv12_to_rgba_shader);
+
+  cogl_program_use (priv->program);
+  location = cogl_program_get_uniform_location (priv->program, "ytex");
+  priv->glUniform1iARB (location, 0);
+  location = cogl_program_get_uniform_location (priv->program, "vtex");
+  priv->glUniform1iARB (location, 1);
+  location = cogl_program_get_uniform_location (priv->program, "utex");
+  priv->glUniform1iARB (location, 2);
+  cogl_program_use (COGL_INVALID_HANDLE);
+}
+
+static ClutterGstRenderer i420_glsl_renderer =
+{
+  "I420 glsl",
+  CLUTTER_GST_I420,
+  CLUTTER_GST_GLSL | CLUTTER_GST_MULTI_TEXTURE,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420")),
+  clutter_gst_i420_glsl_init,
+  clutter_gst_yv12_upload,
+  clutter_gst_yv12_paint,
+  clutter_gst_yv12_glsl_post_paint,
+};
+
+/*
+ * I420 (fragment program version)
+ *
+ * 8 bit Y plane followed by 8 bit 2x2 subsampled U and V planes.
+ * Basically the same as YV12, but with the 2 chroma planes switched.
+ */
+
+static void
+clutter_gst_i420_fp_init (ClutterActor        *actor,
+                          ClutterGstVideoSink *sink)
+{
+  gchar *shader;
+
+  shader = g_malloc(I420_FP_SZ + 1);
+  _string_array_to_char_array (shader, I420_fp);
+
+  /* the size given to glProgramStringARB is without the trailing '\0', which
+   * is precisely I420_FP_SZ */
+  clutter_gst_video_sink_set_fp_shader (sink, shader, I420_FP_SZ);
+  g_free(shader);
+}
+
+static ClutterGstRenderer i420_fp_renderer =
+{
+  "I420 fp",
+  CLUTTER_GST_I420,
+  CLUTTER_GST_FP | CLUTTER_GST_MULTI_TEXTURE,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420")),
+  clutter_gst_i420_fp_init,
+  clutter_gst_yv12_upload,
+  clutter_gst_yv12_paint,
+  clutter_gst_yv12_fp_post_paint,
+};
+
+#endif /* CLUTTER_COGL_HAS_GL */
+
+/*
+ * AYUV
+ *
+ * This is a 4:4:4 YUV format with 8 bit samples for each component along
+ * with an 8 bit alpha blend value per pixel. Component ordering is A Y U V
+ * (as the name suggests).
+ */
+
+static void
+clutter_gst_ayuv_glsl_init(ClutterActor        *actor,
+                           ClutterGstVideoSink *sink)
+{
+  clutter_gst_video_sink_set_shader (sink, ayuv_to_rgba_shader);
+}
+
+static void
+clutter_gst_ayuv_upload (ClutterGstVideoSink *sink,
+                         GstBuffer           *buffer)
+{
+  ClutterGstVideoSinkPrivate *priv= sink->priv;
+
+  clutter_texture_set_from_rgb_data (priv->texture,
+                                     GST_BUFFER_DATA (buffer),
+                                     TRUE,
+                                     priv->width,
+                                     priv->height,
+                                     GST_ROUND_UP_4 (4 * priv->width),
+                                     4,
+                                     0,
+                                     NULL);
+}
+
+static ClutterGstRenderer ayuv_glsl_renderer =
+{
+  "AYUV glsl",
+  CLUTTER_GST_AYUV,
+  CLUTTER_GST_GLSL,
+  GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")),
+  clutter_gst_ayuv_glsl_init,
+  clutter_gst_ayuv_upload,
+  NULL,
+  NULL,
+};
+
+static GSList *
+clutter_gst_build_renderers_list (ClutterGstSymbols *syms)
+{
+  GSList             *list = NULL;
+  const gchar        *gl_extensions;
+  GLint               nb_texture_units;
+  gint                features = 0, i;
+  /* The order of the list of renderers is important. They will be prepended
+   * to a GSList and we'll iterate over that list to choose the first matching
+   * renderer. Thus if you want to use the fp renderer over the glsl one, the
+   * fp renderer has to be put after the glsl one in this array */
+  ClutterGstRenderer *renderers[] =
+    {
+      &rgb24_renderer,
+      &rgb32_renderer,
+      &yv12_glsl_renderer,
+      &yv12_fp_renderer,
+      &i420_glsl_renderer,
+      &i420_fp_renderer,
+      &ayuv_glsl_renderer,
+      NULL
+    };
+
+  /* get the features */
+  gl_extensions = (const gchar*) glGetString (GL_EXTENSIONS);
+  if (cogl_check_extension ("GL_ARB_multitexture", gl_extensions))
+    {
+      /* we need 3 texture units for planar YUV */
+      glGetIntegerv (GL_MAX_TEXTURE_UNITS_ARB, &nb_texture_units);
+
+      syms->glActiveTextureARB = (GLACTIVETEXTUREPROC)
+        cogl_get_proc_address ("glActiveTextureARB");
+      syms->glMultiTexCoord2fARB = (GLMULTITEXCOORD2FPROC)
+        cogl_get_proc_address ("glMultiTexCoord2fARB");
+
+      if (nb_texture_units >= 3 &&
+          syms->glActiveTextureARB &&
+          syms->glMultiTexCoord2fARB)
+        {
+          features |= CLUTTER_GST_MULTI_TEXTURE;
+        }
+    }
+
+  if (cogl_check_extension ("GL_ARB_fragment_program", gl_extensions))
+    {
+      /* the shaders we'll feed to the GPU are simple enough, we don't need
+       * to check GL limits for GL_FRAGMENT_PROGRAM_ARB */
+
+      syms->glGenProgramsARB = (GLGENPROGRAMSPROC)
+        cogl_get_proc_address ("glGenProgramsARB");
+      syms->glBindProgramARB = (GLBINDPROGRAMPROC)
+        cogl_get_proc_address ("glBindProgramARB");
+      syms->glProgramStringARB = (GLPROGRAMSTRINGPROC)
+        cogl_get_proc_address ("glProgramStringARB");
+
+      if (syms->glGenProgramsARB &&
+          syms->glBindProgramARB &&
+          syms->glProgramStringARB)
+        {
+          features |= CLUTTER_GST_FP;
+        }
+    }
+
+  if (cogl_features_available (COGL_FEATURE_SHADERS_GLSL))
+    features |= CLUTTER_GST_GLSL;
+
+  GST_INFO ("GL features: 0x%08x", features);
+
+  for (i = 0; renderers[i]; i++)
+    {
+      gint needed = renderers[i]->flags;
+
+      if ((needed & features) == needed)
+        list = g_slist_prepend (list, renderers[i]);
+    }
+
+  return list;
+}
+
+static void
+append_cap (gpointer data, gpointer user_data)
+{
+  ClutterGstRenderer *renderer = (ClutterGstRenderer *)data;
+  GstCaps *caps = (GstCaps *)user_data;
+  GstCaps *writable_caps;
+
+  writable_caps =
+    gst_caps_make_writable (gst_static_caps_get (&renderer->caps));
+  gst_caps_append (caps, writable_caps);
+}
+
+static GstCaps *
+clutter_gst_build_caps (GSList *renderers)
+{
+  GstCaps *caps;
+
+  caps = gst_caps_new_empty ();
+
+  g_slist_foreach (renderers, append_cap, caps);
+
+  return caps;
+}
+
+ClutterGstRenderer *
+clutter_gst_find_renderer_by_format (ClutterGstVideoSink  *sink,
+                                     ClutterGstVideoFormat format)
+{
+  ClutterGstVideoSinkPrivate *priv = sink->priv;
+  ClutterGstRenderer *renderer = NULL;
+  GSList *element;
+
+  for (element = priv->renderers; element; element = g_slist_next(element))
+    {
+      ClutterGstRenderer *candidate = (ClutterGstRenderer *)element->data;
+
+      if (candidate->format == format)
+        {
+          renderer = candidate;
+          break;
+        }
+    }
+
+  return renderer;
+}
+
 static gboolean
 clutter_gst_video_sink_idle_func (gpointer data)
 {
@@ -348,116 +860,58 @@ clutter_gst_video_sink_idle_func (gpointer data)
   sink = data;
   priv = sink->priv;
 
-  buffer = g_async_queue_try_pop (priv->async_queue);
-  if (buffer == NULL || G_UNLIKELY (!GST_IS_BUFFER (buffer)))
+  g_mutex_lock (priv->buffer_lock);
+  if (!priv->buffer)
+    {
+      priv->idle_id = 0;
+      g_mutex_unlock (priv->buffer_lock);
+      return FALSE;
+    }
+
+  buffer = priv->buffer;
+  priv->buffer = NULL;
+
+  if (G_UNLIKELY (!GST_IS_BUFFER (buffer)))
     {
+      priv->idle_id = 0;
+      g_mutex_unlock (priv->buffer_lock);
       return FALSE;
     }
 
+  priv->idle_id = 0;
+  g_mutex_unlock (priv->buffer_lock);
+
+
   if ((priv->format == CLUTTER_GST_RGB32) || (priv->format == CLUTTER_GST_AYUV))
     {
-      clutter_texture_set_from_rgb_data (priv->texture,
-                                         GST_BUFFER_DATA (buffer),
-                                         TRUE,
-                                         priv->width,
-                                         priv->height,
-                                         GST_ROUND_UP_4 (4 * priv->width),
-                                         4,
-                                         priv->bgr ?
-                                         CLUTTER_TEXTURE_RGB_FLAG_BGR : 0,
-                                         NULL);
+      priv->renderer->upload (sink, buffer);
 
       /* Initialise AYUV shader */
       if ((priv->format == CLUTTER_GST_AYUV) && !priv->shaders_init)
-        clutter_gst_video_sink_set_shader (sink,
-                                           ayuv_to_rgba_shader);
+        priv->renderer->upload (sink, buffer);
     }
   else if (priv->format == CLUTTER_GST_RGB24)
     {
-      clutter_texture_set_from_rgb_data (priv->texture,
-                                         GST_BUFFER_DATA (buffer),
-                                         FALSE,
-                                         priv->width,
-                                         priv->height,
-                                         GST_ROUND_UP_4 (3 * priv->width),
-                                         3,
-                                         priv->bgr ?
-                                         CLUTTER_TEXTURE_RGB_FLAG_BGR : 0,
-                                         NULL);
+      priv->renderer->upload (sink, buffer);
     }
-  else if (priv->format == CLUTTER_GST_YV12)
+  else if (priv->format == CLUTTER_GST_YV12 || priv->format == CLUTTER_GST_I420)
     {
-      CoglHandle y_tex =
-        cogl_texture_new_from_data (priv->width,
-                                    priv->height,
-                                    -1,
-                                    FALSE,
-                                    COGL_PIXEL_FORMAT_G_8,
-                                    COGL_PIXEL_FORMAT_G_8,
-                                    priv->width,
-                                    GST_BUFFER_DATA (buffer));
-      cogl_texture_set_filters (y_tex, CGL_LINEAR, CGL_LINEAR);
-      clutter_texture_set_cogl_texture (priv->texture, y_tex);
-      cogl_texture_unref (y_tex);
-      
-      if (priv->u_tex)
-        cogl_texture_unref (priv->u_tex);
-      
-      if (priv->v_tex)
-        cogl_texture_unref (priv->v_tex);
-      
-      priv->v_tex =
-        cogl_texture_new_from_data (priv->width/2,
-                                    priv->height/2,
-                                    -1,
-                                    FALSE,
-                                    COGL_PIXEL_FORMAT_G_8,
-                                    COGL_PIXEL_FORMAT_G_8,
-                                    priv->width/2,
-                                    GST_BUFFER_DATA (buffer) +
-                                      (priv->width * priv->height));
-      cogl_texture_set_filters (priv->v_tex, CGL_LINEAR, CGL_LINEAR);
-
-      priv->u_tex =
-        cogl_texture_new_from_data (priv->width/2,
-                                    priv->height/2,
-                                    -1,
-                                    FALSE,
-                                    COGL_PIXEL_FORMAT_G_8,
-                                    COGL_PIXEL_FORMAT_G_8,
-                                    priv->width/2,
-                                    GST_BUFFER_DATA (buffer) +
-                                      (priv->width * priv->height) +
-                                      (priv->width/2 * priv->height/2));
-      cogl_texture_set_filters (priv->u_tex, CGL_LINEAR, CGL_LINEAR);
       
-      /* Initialise YV12 shader */
+      priv->renderer->upload (sink, buffer);
+
+      /* Initialize renderer */
       if (!priv->shaders_init)
         {
-#ifdef CLUTTER_COGL_HAS_GL
-          COGLint location;
-          clutter_gst_video_sink_set_shader (sink,
-                                             yv12_to_rgba_shader);
-          
-          cogl_program_use (priv->program);
-          location = cogl_program_get_uniform_location (priv->program, "ytex");
-          priv->glUniform1iARB (location, 0);
-          location = cogl_program_get_uniform_location (priv->program, "utex");
-          priv->glUniform1iARB (location, 1);
-          location = cogl_program_get_uniform_location (priv->program, "vtex");
-          priv->glUniform1iARB (location, 2);
-          cogl_program_use (COGL_INVALID_HANDLE);
-          
+          priv->renderer->init (CLUTTER_ACTOR (priv->texture), sink);
           g_signal_connect (priv->texture,
                             "paint",
-                            G_CALLBACK (clutter_gst_yv12_paint),
+                            G_CALLBACK (priv->renderer->paint),
                             sink);
           g_signal_connect_after (priv->texture,
                                   "paint",
-                                  G_CALLBACK (clutter_gst_yv12_post_paint),
+                                  G_CALLBACK (priv->renderer->post_paint),
                                   sink);
-#endif
-        }
+        }
     }
 
   gst_buffer_unref (buffer);
@@ -465,6 +919,40 @@ clutter_gst_video_sink_idle_func (gpointer data)
   return FALSE;
 }
 
+static void
+clutter_gst_video_sink_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template
+                     (element_class,
+                      gst_static_pad_template_get (&sinktemplate_all));
+
+  gst_element_class_set_details (element_class,
+                                 &clutter_gst_video_sink_details);
+}
+
+static void
+clutter_gst_video_sink_init (ClutterGstVideoSink      *sink,
+                             ClutterGstVideoSinkClass *klass)
+{
+  ClutterGstVideoSinkPrivate *priv;
+
+  sink->priv = priv =
+    G_TYPE_INSTANCE_GET_PRIVATE (sink, CLUTTER_GST_TYPE_VIDEO_SINK,
+                                 ClutterGstVideoSinkPrivate);
+
+  priv->buffer_lock = g_mutex_new ();
+  priv->use_shaders = TRUE;
+  priv->renderers = clutter_gst_build_renderers_list (&priv->syms);
+  priv->caps = clutter_gst_build_caps (priv->renderers);
+
+#ifdef CLUTTER_COGL_HAS_GL
+  priv->glUniform1iARB = (GLUNIFORM1IPROC)
+    cogl_get_proc_address ("glUniform1iARB");
+#endif
+}
+
 static GstFlowReturn
 clutter_gst_video_sink_render (GstBaseSink *bsink,
                                GstBuffer   *buffer)
@@ -475,25 +963,36 @@ clutter_gst_video_sink_render (GstBaseSink *bsink,
   sink = CLUTTER_GST_VIDEO_SINK (bsink);
   priv = sink->priv;
 
-  g_async_queue_push (priv->async_queue, gst_buffer_ref (buffer));
 
-  clutter_threads_add_idle_full (G_PRIORITY_HIGH_IDLE,
-                                 clutter_gst_video_sink_idle_func,
-                                 sink,
-                                 NULL);
+  g_mutex_lock (priv->buffer_lock);
+  if (priv->buffer)
+    { 
+      gst_buffer_unref (priv->buffer);
+    }
+  priv->buffer = gst_buffer_ref (buffer);
+
+  if (priv->idle_id == 0)
+    {
+      priv->idle_id = clutter_threads_add_idle_full (G_PRIORITY_HIGH_IDLE,
+                                     clutter_gst_video_sink_idle_func,
+                                     sink,
+                                     NULL);
+      /* the lock must be held when adding this idle, if it is not the idle
+       * callback would be invoked before priv->idle_id had been assigned
+       */
+    }
+  g_mutex_unlock (priv->buffer_lock);
 
   return GST_FLOW_OK;
 }
 
 static GstCaps *
-clutter_gst_video_sink_get_caps (GstBaseSink *sink)
+clutter_gst_video_sink_get_caps (GstBaseSink *bsink)
 {
-  ClutterGstVideoSinkPrivate *priv = CLUTTER_GST_VIDEO_SINK (sink)->priv;
-  
-  if (priv->use_shaders && cogl_features_available (COGL_FEATURE_SHADERS_GLSL))
-    return gst_static_pad_template_get_caps (&sinktemplate_shaders);
-  else
-    return gst_static_pad_template_get_caps (&sinktemplate);
+  ClutterGstVideoSink *sink;
+
+  sink = CLUTTER_GST_VIDEO_SINK (bsink);
+  return gst_caps_copy (sink->priv->caps);
 }
 
 static gboolean
@@ -516,17 +1015,7 @@ clutter_gst_video_sink_set_caps (GstBaseSink *bsink,
 
   clutter_gst_video_sink_set_shader (sink, NULL);
 
-  if (priv->use_shaders && cogl_features_available (COGL_FEATURE_SHADERS_GLSL))
-    intersection 
-      = gst_caps_intersect 
-            (gst_static_pad_template_get_caps (&sinktemplate_shaders), 
-             caps);
-  else
-    intersection 
-      = gst_caps_intersect 
-            (gst_static_pad_template_get_caps (&sinktemplate), 
-             caps);
-
+  intersection = gst_caps_intersect (priv->caps, caps);
   if (gst_caps_is_empty (intersection)) 
     return FALSE;
 
@@ -559,17 +1048,23 @@ clutter_gst_video_sink_set_caps (GstBaseSink *bsink,
   else 
     priv->par_n = priv->par_d = 1;
 
+#if CLUTTER_COGL_HAS_GL
   ret = gst_structure_get_fourcc (structure, "format", &fourcc);
-  if (ret && (fourcc == GST_RIFF_YV12))
+  if (ret && (fourcc == GST_MAKE_FOURCC ('Y', 'V', '1', '2')))
     {
       priv->format = CLUTTER_GST_YV12;
     }
+  else if (ret && (fourcc == GST_MAKE_FOURCC ('I', '4', '2', '0')))
+    {
+      priv->format = CLUTTER_GST_I420;
+    }
   else if (ret && (fourcc == GST_MAKE_FOURCC ('A', 'Y', 'U', 'V')))
     {
       priv->format = CLUTTER_GST_AYUV;
       priv->bgr = FALSE;
     }
   else
+#endif
     {
       guint32 width;
       gst_structure_get_int (structure, "red_mask", &red_mask);
@@ -588,6 +1083,15 @@ clutter_gst_video_sink_set_caps (GstBaseSink *bsink,
         }
     }
 
+  priv->renderer = clutter_gst_find_renderer_by_format (sink, priv->format);
+  if (G_UNLIKELY (priv->renderer == NULL))
+    {
+      GST_ERROR_OBJECT (sink, "could not find a suitable renderer");
+      return FALSE;
+    }
+
+  GST_INFO_OBJECT (sink, "using the %s renderer", priv->renderer->name);
+
   return TRUE;
 }
 
@@ -602,16 +1106,28 @@ clutter_gst_video_sink_dispose (GObject *object)
 
   clutter_gst_video_sink_set_shader (self, NULL);
 
+  if (priv->idle_id > 0)
+    {
+      g_source_remove (priv->idle_id);
+      priv->idle_id = 0;
+    }
+
   if (priv->texture)
     {
       g_object_unref (priv->texture);
       priv->texture = NULL;
     }
 
-  if (priv->async_queue)
+  if (priv->buffer_lock)
     {
-      g_async_queue_unref (priv->async_queue);
-      priv->async_queue = NULL;
+      g_mutex_free (priv->buffer_lock);
+      priv->buffer_lock = NULL;
+    }
+
+  if (priv->caps)
+    {
+      gst_caps_unref (priv->caps);
+      priv->caps = NULL;
     }
 
   G_OBJECT_CLASS (parent_class)->dispose (object);
@@ -626,6 +1142,8 @@ clutter_gst_video_sink_finalize (GObject *object)
   self = CLUTTER_GST_VIDEO_SINK (object);
   priv = self->priv;
 
+  g_slist_free (priv->renderers);
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -655,7 +1173,7 @@ clutter_gst_video_sink_set_property (GObject *object,
       if (priv->use_shaders != use_shaders)
         {
           priv->use_shaders = use_shaders;
-          g_object_notify (object, "use_shaders");
+          g_object_notify (object, "use-shaders");
         }
       break;
     default:
@@ -692,21 +1210,15 @@ static gboolean
 clutter_gst_video_sink_stop (GstBaseSink *base_sink)
 {
   ClutterGstVideoSinkPrivate *priv;
-  GstBuffer *buffer;
 
   priv = CLUTTER_GST_VIDEO_SINK (base_sink)->priv;
 
-  g_async_queue_lock (priv->async_queue);
+  g_mutex_lock (priv->buffer_lock);
+  if (priv->buffer)
+    gst_buffer_unref (priv->buffer);
+  priv->buffer = NULL;
+  g_mutex_unlock (priv->buffer_lock);
 
-  /* Remove all remaining objects from the queue */
-  do
-    {
-      buffer = g_async_queue_try_pop_unlocked (priv->async_queue);
-      if (buffer)
-        gst_buffer_unref (buffer);
-    } while (buffer != NULL);
-
-  g_async_queue_unlock (priv->async_queue);
 
   return TRUE;
 }
@@ -741,11 +1253,11 @@ clutter_gst_video_sink_class_init (ClutterGstVideoSinkClass *klass)
 
   g_object_class_install_property 
               (gobject_class, PROP_USE_SHADERS,
-               g_param_spec_boolean ("use_shaders",
+               g_param_spec_boolean ("use-shaders",
                                      "Use shaders",
                                      "Use a fragment shader to accelerate "
                                      "colour-space conversion.",
-                                     FALSE,
+                                     TRUE,
                                      G_PARAM_READWRITE));
 }
 
@@ -784,4 +1296,4 @@ GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR,
                           VERSION,
                           "LGPL", /* license */
                           PACKAGE,
-                          "");
+                          "http://www.clutter-project.org";);
diff --git a/libbanshee/shaders/I420.h b/libbanshee/shaders/I420.h
new file mode 100644
index 0000000..f81a1d1
--- /dev/null
+++ b/libbanshee/shaders/I420.h
@@ -0,0 +1,36 @@
+/*
+ * This file was generated by pso2h.
+ */
+
+#ifndef __I420_H__
+#define __I420_H__
+
+/*
+ * This define is the size of the shader in bytes.  More precisely it's the
+ * sum of strlen() of every string in the array.
+ */
+#define I420_FP_SZ    526
+
+static const char *I420_fp[] =
+{
+  "!!ARBfp1.0\n",
+  "PARAM c[2] = { { 1, 1.1640625, 0.0625, 2.015625 },\n",
+  "{ 0.5, 0.390625, 0.8125, 1.5976562 } };\n",
+  "TEMP R0;\n",
+  "TEX R0.y, fragment.texcoord[0], texture[0], 2D;\n",
+  "ADD R0.x, R0.y, -c[0].z;\n",
+  "TEX R0.y, fragment.texcoord[0], texture[2], 2D;\n",
+  "ADD R0.z, R0.y, -c[1].x;\n",
+  "MUL R0.x, R0, c[0].y;\n",
+  "MAD result.color.z, R0, c[0].w, R0.x;\n",
+  "TEX R0.y, fragment.texcoord[0], texture[1], 2D;\n",
+  "ADD R0.y, R0, -c[1].x;\n",
+  "MAD R0.z, -R0, c[1].y, R0.x;\n",
+  "MAD result.color.y, -R0, c[1].z, R0.z;\n",
+  "MAD result.color.x, R0.y, c[1].w, R0;\n",
+  "MOV result.color.w, c[0].x;\n",
+  "END\n",
+  NULL
+};
+
+#endif
diff --git a/libbanshee/shaders/YV12.h b/libbanshee/shaders/YV12.h
new file mode 100644
index 0000000..b36348a
--- /dev/null
+++ b/libbanshee/shaders/YV12.h
@@ -0,0 +1,36 @@
+/*
+ * This file was generated by pso2h.
+ */
+
+#ifndef __YV12_H__
+#define __YV12_H__
+
+/*
+ * This define is the size of the shader in bytes.  More precisely it's the
+ * sum of strlen() of every string in the array.
+ */
+#define YV12_FP_SZ    526
+
+static const char *YV12_fp[] =
+{
+  "!!ARBfp1.0\n",
+  "PARAM c[2] = { { 1, 1.1640625, 0.0625, 2.015625 },\n",
+  "{ 0.5, 0.390625, 0.8125, 1.5976562 } };\n",
+  "TEMP R0;\n",
+  "TEX R0.y, fragment.texcoord[0], texture[0], 2D;\n",
+  "ADD R0.x, R0.y, -c[0].z;\n",
+  "TEX R0.y, fragment.texcoord[0], texture[1], 2D;\n",
+  "ADD R0.z, R0.y, -c[1].x;\n",
+  "MUL R0.x, R0, c[0].y;\n",
+  "MAD result.color.z, R0, c[0].w, R0.x;\n",
+  "TEX R0.y, fragment.texcoord[0], texture[2], 2D;\n",
+  "ADD R0.y, R0, -c[1].x;\n",
+  "MAD R0.z, -R0, c[1].y, R0.x;\n",
+  "MAD result.color.y, -R0, c[1].z, R0.z;\n",
+  "MAD result.color.x, R0.y, c[1].w, R0;\n",
+  "MOV result.color.w, c[0].x;\n",
+  "END\n",
+  NULL
+};
+
+#endif



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