This add a more flexible way of generating shaders. It generates all valid
combinations of different input, conversion and output pipelines, which
can easily be extended with more if desired.
---
 src/gl-internal.h |  65 +++--
 src/gl-renderer.c |  68 +++---
 src/gl-shaders.c  | 706 +++++++++++++++++++++++++++++++++++++++---------------
 3 files changed, 600 insertions(+), 239 deletions(-)

diff --git a/src/gl-internal.h b/src/gl-internal.h
index 90912df..994c139 100644
--- a/src/gl-internal.h
+++ b/src/gl-internal.h
@@ -39,13 +39,41 @@
 #include <EGL/eglext.h>
 #include "weston-egl-ext.h"
 
+#define MAX_PLANES 3
+
+enum gl_shader_attribute {
+       ATTRIBUTE_INPUT,
+       ATTRIBUTE_OUTPUT,
+       ATTRIBUTE_CONVERSION,
+       ATTRIBUTE_COUNT
+};
+
+enum gl_conversion_attribute {
+       CONVERSION_NONE,
+       CONVERSION_COUNT
+};
+
+enum gl_output_attribute {
+       OUTPUT_BLEND,
+       OUTPUT_COUNT
+};
+
+enum gl_input_attribute {
+       INPUT_RGBX,
+       INPUT_RGBA,
+       INPUT_EGL_EXTERNAL,
+       INPUT_Y_UV,
+       INPUT_Y_U_V,
+       INPUT_Y_XUXV,
+       INPUT_SOLID,
+       INPUT_COUNT
+};
+
 struct gl_shader {
        GLuint program;
-       GLuint vertex_shader, fragment_shader;
-       GLint proj_uniform;
-       GLint tex_uniforms[3];
-       GLint alpha_uniform;
+       GLint projection_uniform;
        GLint color_uniform;
+       GLint alpha_uniform;
 };
 
 struct gl_output_state {
@@ -54,12 +82,12 @@ struct gl_output_state {
 
 struct gl_surface_state {
        GLfloat color[4];
-       struct gl_shader *shader;
+       enum gl_input_attribute input;
 
-       GLuint textures[3];
+       GLuint textures[MAX_PLANES];
        int num_textures;
 
-       EGLImageKHR images[3];
+       EGLImageKHR images[MAX_PLANES];
        GLenum target;
        int num_images;
 };
@@ -91,15 +119,11 @@ struct gl_renderer {
 
        int has_egl_image_external;
 
-       struct gl_shader texture_shader_rgba;
-       struct gl_shader texture_shader_rgbx;
-       struct gl_shader texture_shader_egl_external;
-       struct gl_shader texture_shader_y_uv;
-       struct gl_shader texture_shader_y_u_v;
-       struct gl_shader texture_shader_y_xuxv;
-       struct gl_shader invert_color_shader;
-       struct gl_shader solid_shader;
+       struct gl_shader *solid_shader;
        struct gl_shader *current_shader;
+
+       struct gl_shader **shaders;
+       size_t shader_count;
 };
 
 static inline struct gl_output_state *
@@ -127,11 +151,20 @@ void
 gl_destroy_shaders(struct gl_renderer *gr);
 
 void
+gl_shader_set_output(struct gl_shader *shader,
+                    struct weston_output *output);
+
+void
 gl_use_shader(struct gl_renderer *gr,
                             struct gl_shader *shader);
 
+struct gl_shader *
+gl_select_shader(struct gl_renderer *gr,
+                       enum gl_input_attribute input,
+                       enum gl_output_attribute output);
+
 void
-gl_shader_uniforms(struct gl_shader *shader,
+gl_shader_setup(struct gl_shader *shader,
                       struct weston_surface *surface,
                       struct weston_output *output);
 
diff --git a/src/gl-renderer.c b/src/gl-renderer.c
index a14fe2f..1a9f782 100644
--- a/src/gl-renderer.c
+++ b/src/gl-renderer.c
@@ -520,8 +520,8 @@ triangle_fan_debug(struct weston_surface *surface, int 
first, int count)
                *index++ = first + i;
        }
 
-       glUseProgram(gr->solid_shader.program);
-       glUniform4fv(gr->solid_shader.color_uniform, 1,
+       glUseProgram(gr->solid_shader->program);
+       glUniform4fv(gr->solid_shader->color_uniform, 1,
                        color[color_idx++ % ARRAY_LENGTH(color)]);
        glDrawElements(GL_LINES, nelems, GL_UNSIGNED_SHORT, buffer);
        glUseProgram(gr->current_shader->program);
@@ -602,6 +602,7 @@ draw_surface(struct weston_surface *es, struct 
weston_output *output,
        struct weston_compositor *ec = es->compositor;
        struct gl_renderer *gr = get_renderer(ec);
        struct gl_surface_state *gs = get_surface_state(es);
+       struct gl_shader *shader;
        /* repaint bounding region in global coordinates: */
        pixman_region32_t repaint;
        /* non-opaque region in surface coordinates: */
@@ -624,12 +625,14 @@ draw_surface(struct weston_surface *es, struct 
weston_output *output,
        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
 
        if (ec->fan_debug) {
-               gl_use_shader(gr, &gr->solid_shader);
-               gl_shader_uniforms(&gr->solid_shader, es, output);
+               gl_use_shader(gr, gr->solid_shader);
+               gl_shader_setup(gr->solid_shader, es, output);
        }
 
-       gl_use_shader(gr, gs->shader);
-       gl_shader_uniforms(gs->shader, es, output);
+       shader = gl_select_shader(gr, gs->input, OUTPUT_BLEND);
+
+       gl_use_shader(gr, shader);
+       gl_shader_setup(shader, es, output);
 
        if (es->transform.enabled || output->zoom.active)
                filter = GL_LINEAR;
@@ -649,14 +652,16 @@ draw_surface(struct weston_surface *es, struct 
weston_output *output,
        pixman_region32_subtract(&surface_blend, &surface_blend, &es->opaque);
 
        if (pixman_region32_not_empty(&es->opaque)) {
-               if (gs->shader == &gr->texture_shader_rgba) {
+               if (gs->input == INPUT_RGBA) {
                        /* Special case for RGBA textures with possibly
                         * bad data in alpha channel: use the shader
                         * that forces texture alpha = 1.0.
                         * Xwayland surfaces need this.
                         */
-                       gl_use_shader(gr, &gr->texture_shader_rgbx);
-                       gl_shader_uniforms(&gr->texture_shader_rgbx, es, 
output);
+
+                       struct gl_shader *rgbx_shader = gl_select_shader(gr, 
INPUT_RGBX, OUTPUT_BLEND);
+                       gl_use_shader(gr, rgbx_shader);
+                       gl_shader_setup(rgbx_shader, es, output);
                }
 
                if (es->alpha < 1.0)
@@ -668,7 +673,7 @@ draw_surface(struct weston_surface *es, struct 
weston_output *output,
        }
 
        if (pixman_region32_not_empty(&surface_blend)) {
-               gl_use_shader(gr, gs->shader);
+               gl_use_shader(gr, shader);
                glEnable(GL_BLEND);
                repaint_region(es, &repaint, &surface_blend);
        }
@@ -772,17 +777,17 @@ draw_border(struct weston_output *output)
 {
        struct weston_compositor *ec = output->compositor;
        struct gl_renderer *gr = get_renderer(ec);
-       struct gl_shader *shader = &gr->texture_shader_rgba;
+       struct gl_shader *shader;
        GLfloat *v;
        int n;
 
+       shader = gl_select_shader(gr, INPUT_RGBA, OUTPUT_BLEND);
+
        glDisable(GL_BLEND);
        gl_use_shader(gr, shader);
 
-       glUniformMatrix4fv(shader->proj_uniform,
-                          1, GL_FALSE, output->matrix.d);
+       gl_shader_set_output(shader, output);
 
-       glUniform1i(shader->tex_uniforms[0], 0);
        glUniform1f(shader->alpha_uniform, 1);
 
        n = texture_border(output);
@@ -1002,9 +1007,9 @@ gl_renderer_attach(struct weston_surface *es, struct 
wl_buffer *buffer)
                             es->pitch, buffer->height, 0,
                             GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL);
                if (wl_shm_buffer_get_format(buffer) == WL_SHM_FORMAT_XRGB8888)
-                       gs->shader = &gr->texture_shader_rgbx;
+                       gs->input = INPUT_RGBX;
                else
-                       gs->shader = &gr->texture_shader_rgba;
+                       gs->input = INPUT_RGBA;
        } else if (gr->query_buffer(gr->egl_display, buffer,
                                    EGL_TEXTURE_FORMAT, &format)) {
                for (i = 0; i < gs->num_images; i++)
@@ -1013,30 +1018,35 @@ gl_renderer_attach(struct weston_surface *es, struct 
wl_buffer *buffer)
                gs->target = GL_TEXTURE_2D;
                switch (format) {
                case EGL_TEXTURE_RGB:
-               case EGL_TEXTURE_RGBA:
                default:
                        num_planes = 1;
-                       gs->shader = &gr->texture_shader_rgba;
+                       gs->input = INPUT_RGBX;
+                       break;
+               case EGL_TEXTURE_RGBA:
+                       num_planes = 1;
+                       gs->input = INPUT_RGBA;
                        break;
                case EGL_TEXTURE_EXTERNAL_WL:
                        num_planes = 1;
                        gs->target = GL_TEXTURE_EXTERNAL_OES;
-                       gs->shader = &gr->texture_shader_egl_external;
+                       gs->input = INPUT_EGL_EXTERNAL;
                        break;
                case EGL_TEXTURE_Y_UV_WL:
                        num_planes = 2;
-                       gs->shader = &gr->texture_shader_y_uv;
+                       gs->input = INPUT_Y_UV;
                        break;
                case EGL_TEXTURE_Y_U_V_WL:
                        num_planes = 3;
-                       gs->shader = &gr->texture_shader_y_u_v;
+                       gs->input = INPUT_Y_U_V;
                        break;
                case EGL_TEXTURE_Y_XUXV_WL:
                        num_planes = 2;
-                       gs->shader = &gr->texture_shader_y_xuxv;
+                       gs->input = INPUT_Y_XUXV;
                        break;
                }
 
+               assert(num_planes <= MAX_PLANES);
+
                ensure_textures(gs, num_planes);
                for (i = 0; i < num_planes; i++) {
                        attribs[0] = EGL_WAYLAND_PLANE_WL;
@@ -1069,14 +1079,13 @@ gl_renderer_surface_set_color(struct weston_surface 
*surface,
                 float red, float green, float blue, float alpha)
 {
        struct gl_surface_state *gs = get_surface_state(surface);
-       struct gl_renderer *gr = get_renderer(surface->compositor);
 
        gs->color[0] = red;
        gs->color[1] = green;
        gs->color[2] = blue;
        gs->color[3] = alpha;
 
-       gs->shader = &gr->solid_shader;
+       gs->input = INPUT_SOLID;
 }
 
 static int
@@ -1293,6 +1302,8 @@ gl_renderer_destroy(struct weston_compositor *ec)
        if (gr->has_bind_display)
                gr->unbind_display(gr->egl_display, ec->wl_display);
 
+       gl_destroy_shaders(gr);
+
        /* Work around crash in egl_dri2.c's dri2_make_current() - when does 
this apply? */
        eglMakeCurrent(gr->egl_display,
                       EGL_NO_SURFACE, EGL_NO_SURFACE,
@@ -1300,6 +1311,9 @@ gl_renderer_destroy(struct weston_compositor *ec)
 
        eglTerminate(gr->egl_display);
        eglReleaseThread();
+
+       free(ec->renderer);
+       ec->renderer = NULL;
 }
 
 static int
@@ -1424,15 +1438,11 @@ fragment_debug_binding(struct wl_seat *seat, uint32_t 
time, uint32_t key,
 {
        struct weston_compositor *ec = data;
        struct gl_renderer *gr = get_renderer(ec);
-       struct weston_output *output;
 
        gr->fragment_shader_debug ^= 1;
 
-       gl_destroy_shaders(gr);
        gl_init_shaders(gr);
-
-       wl_list_for_each(output, &ec->output_list, link)
-               weston_output_damage(output);
+       weston_compositor_damage_all(ec);
 }
 
 static int
diff --git a/src/gl-shaders.c b/src/gl-shaders.c
index 35275ca..9c01731 100644
--- a/src/gl-shaders.c
+++ b/src/gl-shaders.c
@@ -23,155 +23,321 @@
 
 #include "gl-internal.h"
 
-void
-gl_use_shader(struct gl_renderer *gr,
-                            struct gl_shader *shader)
+#define STRINGIFY(expr) #expr
+#define STRINGIFY_VALUE(expr) STRINGIFY(expr)
+
+enum shader_string {
+       SHADER_STRING_DIRECTIVES,
+       SHADER_STRING_GLOBAL,
+       SHADER_STRING_MAIN,
+       SHADER_STRING_COUNT
+};
+
+enum shader_builder_error {
+       SHADER_BUILDER_SUCCESS,
+       SHADER_BUILDER_SKIP,
+       SHADER_BUILDER_ERROR_OUT_OF_MEMORY,
+       SHADER_BUILDER_ERROR_NO_STRING
+};
+
+static const size_t attribute_counts[ATTRIBUTE_COUNT] = {
+       INPUT_COUNT,
+       OUTPUT_COUNT,
+       CONVERSION_COUNT
+};
+
+struct shader_builder *builder;
+
+typedef enum shader_builder_error (*gl_shader_constructor_t)
+                                 (struct shader_builder *builder);
+typedef void (*gl_shader_setup_uniforms_t)(struct shader_builder *builder,
+                                          struct gl_shader *shader);
+
+enum gl_input_class {
+       INPUT_CLASS_OPAQUE,
+       INPUT_CLASS_ALPHA
+};
+
+struct gl_input_type_desc {
+       enum gl_input_class input_class;
+       gl_shader_constructor_t constructor;
+       gl_shader_setup_uniforms_t setup_uniforms;
+};
+
+struct shader_builder {
+       struct gl_renderer *renderer;
+       struct gl_input_type_desc *desc;
+       enum shader_builder_error error;
+       size_t attributes[ATTRIBUTE_COUNT];
+       struct wl_array strings[SHADER_STRING_COUNT];
+};
+
+static void
+shader_builder_init(struct shader_builder *builder)
 {
-       if (gr->current_shader == shader)
+       int i;
+
+       builder->error = SHADER_BUILDER_SUCCESS;
+
+       for (i = 0; i < SHADER_STRING_COUNT; i++)
+               wl_array_init(&builder->strings[i]);
+}
+
+static void
+shader_builder_release(struct shader_builder *builder)
+{
+       int i;
+
+       for (i = 0; i < SHADER_STRING_COUNT; i++)
+               wl_array_release(&builder->strings[i]);
+}
+
+static const char *
+shader_builder_get_string(struct shader_builder *builder)
+{
+       int i;
+       struct wl_array *bottom = &builder->strings[0];
+
+       for (i = SHADER_STRING_COUNT; i-- > 1;) {
+               struct wl_array *lower = &builder->strings[i - 1];
+               struct wl_array *upper = &builder->strings[i];
+
+               char *data = wl_array_add(lower, upper->size);
+
+               if (!data)
+                       return NULL;
+
+               memcpy(data, upper->data, upper->size);
+       }
+
+       char *data = wl_array_add(bottom, 1);
+
+       if (!data)
+               return NULL;
+
+       *data = 0;
+
+       return bottom->data;
+}
+
+static void
+shader_builder_add(struct shader_builder *builder, enum shader_string type,
+                  const char *string)
+{
+       char *data;
+       size_t length;
+
+       if (!string) {
+               builder->error = SHADER_BUILDER_ERROR_NO_STRING;
                return;
+       }
 
-       glUseProgram(shader->program);
-       gr->current_shader = shader;
+       length = strlen(string);
+       data = wl_array_add(&builder->strings[type], length);
+
+       if (!data)
+               builder->error = SHADER_BUILDER_ERROR_OUT_OF_MEMORY;
+       else
+               memcpy(data, string, length);
 }
 
-void
-gl_shader_uniforms(struct gl_shader *shader,
-                      struct weston_surface *surface,
-                      struct weston_output *output)
+
+
+
+static enum shader_builder_error
+shader_rgbx_constructor(struct shader_builder *builder)
+{
+       shader_builder_add(builder, SHADER_STRING_GLOBAL,
+               "uniform sampler2D texture;\n");
+
+       shader_builder_add(builder, SHADER_STRING_MAIN,
+               "gl_FragColor.rgb = texture2D(texture, texture_coord).rgb;\n" \
+               "gl_FragColor.a = 1.0;\n");
+
+       return SHADER_BUILDER_SUCCESS;
+}
+
+static enum shader_builder_error
+shader_rgba_constructor(struct shader_builder *builder)
+{
+       shader_builder_add(builder, SHADER_STRING_GLOBAL,
+               "uniform sampler2D texture;\n");
+       shader_builder_add(builder, SHADER_STRING_MAIN,
+               "gl_FragColor = texture2D(texture, texture_coord);\n");
+
+
+       return SHADER_BUILDER_SUCCESS;
+}
+
+static enum shader_builder_error
+shader_egl_external_constructor(struct shader_builder *builder)
+{
+       if (!builder->renderer->has_egl_image_external)
+               return SHADER_BUILDER_SKIP;
+
+       shader_builder_add(builder, SHADER_STRING_DIRECTIVES,
+               "#extension GL_OES_EGL_image_external : require;\n");
+       shader_builder_add(builder, SHADER_STRING_GLOBAL,
+               "uniform samplerExternalOES texture;\n");
+       shader_builder_add(builder, SHADER_STRING_MAIN,
+               "gl_FragColor = texture2D(texture, texture_coord);\n");
+
+       return SHADER_BUILDER_SUCCESS;
+}
+
+static void
+shader_texture_uniforms(struct shader_builder *builder,
+                       struct gl_shader *shader)
+{
+       glUniform1i(glGetUniformLocation(shader->program, "texture"), 0);
+}
+
+static enum shader_builder_error
+shader_yuv_constructor(struct shader_builder *builder)
+{
+       const char *sample;
+
+       shader_builder_add(builder, SHADER_STRING_GLOBAL,
+               "uniform sampler2D planes[" STRINGIFY_VALUE(MAX_PLANES) "];\n");
+
+       switch (builder->attributes[ATTRIBUTE_INPUT]) {
+       case INPUT_Y_UV:
+               sample = "vec3 yuv = vec3(" \
+                       "texture2D(planes[0], texture_coord).x," \
+                       "texture2D(planes[1], texture_coord).xy);\n";
+               break;
+       case INPUT_Y_U_V:
+               sample = "vec3 yuv = vec3(" \
+                       "texture2D(planes[0], texture_coord).x," \
+                       "texture2D(planes[1], texture_coord).x," \
+                       "texture2D(planes[2], texture_coord).x);\n";
+               break;
+       case INPUT_Y_XUXV:
+               sample = "vec3 yuv = vec3(" \
+                       "texture2D(planes[0], texture_coord).x," \
+                       "texture2D(planes[1], texture_coord).yw);\n";
+               break;
+       default:
+               sample = NULL;
+       }
+
+       shader_builder_add(builder, SHADER_STRING_MAIN, sample);
+       shader_builder_add(builder, SHADER_STRING_MAIN,
+               "yuv = vec3(1.16438356 * (yuv.x - 0.0625), yuv.yz - 0.5);\n" \
+               "gl_FragColor.r = yuv.x + 1.59602678 * yuv.z;\n" \
+               "gl_FragColor.g = yuv.x - 0.39176229 * yuv.y - " \
+                       "0.81296764 * yuv.z;\n" \
+               "gl_FragColor.b = yuv.x + 2.01723214 * yuv.y;\n" \
+               "gl_FragColor.a = 1.0;\n");
+
+       return SHADER_BUILDER_SUCCESS;
+}
+
+static void
+shader_yuv_uniforms(struct shader_builder *builder, struct gl_shader *shader)
 {
        int i;
-       struct gl_surface_state *gs = get_surface_state(surface);
+       GLint values[MAX_PLANES];
 
-       glUniformMatrix4fv(shader->proj_uniform,
-                          1, GL_FALSE, output->matrix.d);
-       glUniform4fv(shader->color_uniform, 1, gs->color);
-       glUniform1f(shader->alpha_uniform, surface->alpha);
+       for (i = 0; i < MAX_PLANES; i++)
+               values[i] = i;
 
-       for (i = 0; i < gs->num_textures; i++)
-               glUniform1i(shader->tex_uniforms[i], i);
+       glUniform1iv(glGetUniformLocation(shader->program, "planes"),
+                    MAX_PLANES, values);
 }
 
-static const char vertex_shader[] =
-       "uniform mat4 proj;\n"
-       "attribute vec2 position;\n"
-       "attribute vec2 texcoord;\n"
-       "varying vec2 v_texcoord;\n"
-       "void main()\n"
-       "{\n"
-       "   gl_Position = proj * vec4(position, 0.0, 1.0);\n"
-       "   v_texcoord = texcoord;\n"
-       "}\n";
+static enum shader_builder_error
+shader_solid_constructor(struct shader_builder *builder)
+{
+       shader_builder_add(builder, SHADER_STRING_GLOBAL,
+               "uniform vec4 color;\n");
+       shader_builder_add(builder, SHADER_STRING_MAIN,
+               "gl_FragColor = color;\n");
+
+       return SHADER_BUILDER_SUCCESS;
+}
+
+static void
+shader_solid_uniforms(struct shader_builder *builder, struct gl_shader *shader)
+{
+       shader->color_uniform = glGetUniformLocation(shader->program, "color");
+}
 
-/* Declare common fragment shader uniforms */
-#define FRAGMENT_CONVERT_YUV                                           \
-       "  y *= alpha;\n"                                               \
-       "  u *= alpha;\n"                                               \
-       "  v *= alpha;\n"                                               \
-       "  gl_FragColor.r = y + 1.59602678 * v;\n"                      \
-       "  gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v;\n"     \
-       "  gl_FragColor.b = y + 2.01723214 * u;\n"                      \
-       "  gl_FragColor.a = alpha;\n"
+static struct gl_input_type_desc input_type_descs[INPUT_COUNT] = {
+       /* INPUT_RGBX */
+       {INPUT_CLASS_OPAQUE, shader_rgbx_constructor, shader_texture_uniforms},
 
-static const char fragment_debug[] =
-       "  gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + gl_FragColor * 0.8;\n";
+       /* INPUT_RGBA */
+       {INPUT_CLASS_ALPHA, shader_rgba_constructor, shader_texture_uniforms},
 
-static const char fragment_brace[] =
-       "}\n";
+       /* INPUT_EGL_EXTERNAL */
+       {INPUT_CLASS_ALPHA, shader_egl_external_constructor,
+        shader_texture_uniforms},
 
-static const char texture_fragment_shader_rgba[] =
-       "precision mediump float;\n"
-       "varying vec2 v_texcoord;\n"
-       "uniform sampler2D tex;\n"
-       "uniform float alpha;\n"
-       "void main()\n"
-       "{\n"
-       "   gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;"
-       ;
-
-static const char texture_fragment_shader_rgbx[] =
-       "precision mediump float;\n"
-       "varying vec2 v_texcoord;\n"
-       "uniform sampler2D tex;\n"
-       "uniform float alpha;\n"
-       "void main()\n"
-       "{\n"
-       "   gl_FragColor.rgb = alpha * texture2D(tex, v_texcoord).rgb\n;"
-       "   gl_FragColor.a = alpha;\n"
-       ;
-
-static const char texture_fragment_shader_egl_external[] =
-       "#extension GL_OES_EGL_image_external : require\n"
-       "precision mediump float;\n"
-       "varying vec2 v_texcoord;\n"
-       "uniform samplerExternalOES tex;\n"
-       "uniform float alpha;\n"
-       "void main()\n"
-       "{\n"
-       "   gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;"
-       ;
-
-static const char texture_fragment_shader_y_uv[] =
-       "precision mediump float;\n"
-       "uniform sampler2D tex;\n"
-       "uniform sampler2D tex1;\n"
-       "varying vec2 v_texcoord;\n"
-       "uniform float alpha;\n"
-       "void main() {\n"
-       "  float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n"
-       "  float u = texture2D(tex1, v_texcoord).r - 0.5;\n"
-       "  float v = texture2D(tex1, v_texcoord).g - 0.5;\n"
-       FRAGMENT_CONVERT_YUV
-       ;
-
-static const char texture_fragment_shader_y_u_v[] =
-       "precision mediump float;\n"
-       "uniform sampler2D tex;\n"
-       "uniform sampler2D tex1;\n"
-       "uniform sampler2D tex2;\n"
-       "varying vec2 v_texcoord;\n"
-       "uniform float alpha;\n"
-       "void main() {\n"
-       "  float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n"
-       "  float u = texture2D(tex1, v_texcoord).x - 0.5;\n"
-       "  float v = texture2D(tex2, v_texcoord).x - 0.5;\n"
-       FRAGMENT_CONVERT_YUV
-       ;
-
-static const char texture_fragment_shader_y_xuxv[] =
-       "precision mediump float;\n"
-       "uniform sampler2D tex;\n"
-       "uniform sampler2D tex1;\n"
-       "varying vec2 v_texcoord;\n"
-       "uniform float alpha;\n"
-       "void main() {\n"
-       "  float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n"
-       "  float u = texture2D(tex1, v_texcoord).g - 0.5;\n"
-       "  float v = texture2D(tex1, v_texcoord).a - 0.5;\n"
-       FRAGMENT_CONVERT_YUV
-       ;
-
-static const char solid_fragment_shader[] =
-       "precision mediump float;\n"
-       "uniform vec4 color;\n"
-       "uniform float alpha;\n"
+       /* INPUT_Y_UV */
+       {INPUT_CLASS_OPAQUE, shader_yuv_constructor, shader_yuv_uniforms},
+
+       /* INPUT_Y_U_V */
+       {INPUT_CLASS_OPAQUE, shader_yuv_constructor, shader_yuv_uniforms},
+
+       /* INPUT_Y_XUXV */
+       {INPUT_CLASS_OPAQUE, shader_yuv_constructor, shader_yuv_uniforms},
+
+       /* INPUT_SOLID */
+       {INPUT_CLASS_ALPHA, shader_solid_constructor, shader_solid_uniforms},
+};
+
+static void
+attributes_from_permutation(size_t permutation, size_t *attributes)
+{
+       int i;
+       for (i = 0; i < ATTRIBUTE_COUNT; i++) {
+               size_t attribute_count = attribute_counts[i];
+               size_t attribute = permutation % attribute_count;
+               permutation /= attribute_count;
+               attributes[i] = attribute;
+       }
+}
+
+static size_t
+permutation_from_attributes(size_t *attributes)
+{
+       size_t i;
+       size_t result = 0, factor = 1;
+
+       for (i = 0; i < ATTRIBUTE_COUNT; i++) {
+               result += attributes[i] * factor;
+               factor *= attribute_counts[i];
+       }
+
+       return result;
+}
+
+static const char vertex_shader_source[] =
+       "uniform mat4 projection;\n"
+       "attribute vec2 position;\n"
+       "attribute vec2 attr_texture_coord;\n"
+       "varying vec2 texture_coord;\n"
        "void main()\n"
        "{\n"
-       "   gl_FragColor = alpha * color\n;"
-       ;
+       "   gl_Position = projection * vec4(position, 0.0, 1.0);\n"
+       "   texture_coord = attr_texture_coord;\n"
+       "}\n";
 
-static int
-compile_shader(GLenum type, int count, const char **sources)
+static GLuint
+compile_shader(GLenum type, const char *source)
 {
        GLuint s;
        char msg[512];
        GLint status;
 
        s = glCreateShader(type);
-       glShaderSource(s, count, sources, NULL);
+       glShaderSource(s, 1, &source, NULL);
        glCompileShader(s);
        glGetShaderiv(s, GL_COMPILE_STATUS, &status);
        if (!status) {
                glGetShaderInfoLog(s, sizeof msg, NULL, msg);
+               weston_log("shader source: %s\n", source);
                weston_log("shader info: %s\n", msg);
                return GL_NONE;
        }
@@ -179,106 +345,258 @@ compile_shader(GLenum type, int count, const char 
**sources)
        return s;
 }
 
-static int
-shader_init(struct gl_renderer *renderer, struct gl_shader *shader,
-                  const char *vertex_source, const char *fragment_source)
+static struct gl_shader *
+shader_create(GLuint vertex_shader,
+               const char *fragment_source)
 {
        char msg[512];
        GLint status;
-       int count;
-       const char *sources[3];
-
-       shader->vertex_shader =
-               compile_shader(GL_VERTEX_SHADER, 1, &vertex_source);
-
-       if (renderer->fragment_shader_debug) {
-               sources[0] = fragment_source;
-               sources[1] = fragment_debug;
-               sources[2] = fragment_brace;
-               count = 3;
-       } else {
-               sources[0] = fragment_source;
-               sources[1] = fragment_brace;
-               count = 2;
-       }
+       GLuint program, fragment_shader;
+
+       struct gl_shader *shader;
+
+       fragment_shader =
+               compile_shader(GL_FRAGMENT_SHADER, fragment_source);
+
+       if (!fragment_shader)
+               return NULL;
+
+       shader = calloc(1, sizeof(struct gl_shader));
+
+       if (!shader)
+               return NULL;
+
+       program = glCreateProgram();
+
+       glAttachShader(program, vertex_shader);
+       glAttachShader(program, fragment_shader);
+
+       glDeleteShader(fragment_shader);
 
-       shader->fragment_shader =
-               compile_shader(GL_FRAGMENT_SHADER, count, sources);
+       glBindAttribLocation(program, 0, "position");
+       glBindAttribLocation(program, 1, "attr_texture_coord");
 
-       shader->program = glCreateProgram();
-       glAttachShader(shader->program, shader->vertex_shader);
-       glAttachShader(shader->program, shader->fragment_shader);
-       glBindAttribLocation(shader->program, 0, "position");
-       glBindAttribLocation(shader->program, 1, "texcoord");
+       glLinkProgram(program);
+       glGetProgramiv(program, GL_LINK_STATUS, &status);
 
-       glLinkProgram(shader->program);
-       glGetProgramiv(shader->program, GL_LINK_STATUS, &status);
        if (!status) {
-               glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg);
+               glGetProgramInfoLog(program, sizeof msg, NULL, msg);
                weston_log("link info: %s\n", msg);
-               return -1;
+               free(shader);
+               return NULL;
        }
 
-       shader->proj_uniform = glGetUniformLocation(shader->program, "proj");
-       shader->tex_uniforms[0] = glGetUniformLocation(shader->program, "tex");
-       shader->tex_uniforms[1] = glGetUniformLocation(shader->program, "tex1");
-       shader->tex_uniforms[2] = glGetUniformLocation(shader->program, "tex2");
-       shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha");
-       shader->color_uniform = glGetUniformLocation(shader->program, "color");
+       shader->program = program;
+       shader->projection_uniform = glGetUniformLocation(program,
+                                                         "projection");
+       shader->alpha_uniform = glGetUniformLocation(program, "alpha");
 
-       return 0;
+       return shader;
 }
 
+
 static void
-shader_release(struct gl_shader *shader)
+destroy_shaders(struct gl_shader **shaders, size_t count)
 {
-       glDeleteShader(shader->vertex_shader);
-       glDeleteShader(shader->fragment_shader);
-       glDeleteProgram(shader->program);
+       size_t i;
 
-       shader->vertex_shader = 0;
-       shader->fragment_shader = 0;
-       shader->program = 0;
+       for (i = 0; i < count; i++)
+               if (shaders[i]) {
+                       glDeleteProgram(shaders[i]->program);
+                       free(shaders[i]);
+               }
+
+       free(shaders);
+}
+
+static int
+create_shader_permutation(struct gl_renderer *renderer,
+       struct gl_shader **shader, size_t permutation, GLuint vertex_shader)
+{
+       struct shader_builder builder;
+       enum shader_builder_error error;
+       const char *fragment_shader;
+
+       attributes_from_permutation(permutation, builder.attributes);
+
+       builder.renderer = renderer;
+       builder.desc = &input_type_descs[builder.attributes[ATTRIBUTE_INPUT]];
+
+       shader_builder_init(&builder);
+
+       shader_builder_add(&builder, SHADER_STRING_GLOBAL,
+               "precision mediump float;\n" \
+               "varying vec2 texture_coord;\n" \
+               "uniform float alpha;\n");
+
+       shader_builder_add(&builder, SHADER_STRING_MAIN, "void main()\n{\n");
+
+       error = builder.desc->constructor(&builder);
+
+       if (error != SHADER_BUILDER_SUCCESS) {
+               if (error == SHADER_BUILDER_SKIP) {
+                       shader_builder_release(&builder);
+                       return 0;
+               }
+
+               goto error;
+       }
+
+       shader_builder_add(&builder, SHADER_STRING_MAIN,
+               "gl_FragColor *= alpha;\n");
+
+       if (renderer->fragment_shader_debug)
+               shader_builder_add(&builder, SHADER_STRING_MAIN,
+                       "gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + " \
+                       "gl_FragColor * 0.8;\n");
+
+       shader_builder_add(&builder, SHADER_STRING_MAIN, "}\n");
+
+       fragment_shader = shader_builder_get_string(&builder);
+
+       if (!fragment_shader)
+               goto error;
+
+       *shader = shader_create(vertex_shader, fragment_shader);
+
+       if (!*shader)
+               goto error;
+
+       glUseProgram((*shader)->program);
+
+       builder.desc->setup_uniforms(&builder, *shader);
+
+       shader_builder_release(&builder);
+
+       return 0;
+
+error:
+       shader_builder_release(&builder);
+       return -1;
+}
+
+static struct gl_shader **
+create_shader_permutations(struct gl_renderer *gr)
+{
+       struct gl_shader **shaders;
+       size_t i, permutations = 1;
+       unsigned int created = 0;
+       GLuint vertex_shader;
+
+       vertex_shader = compile_shader(GL_VERTEX_SHADER, vertex_shader_source);
+
+       if (!vertex_shader)
+               return NULL;
+
+       for (i = 0; i < ATTRIBUTE_COUNT; i++)
+               permutations *= attribute_counts[i];
+
+       shaders = calloc(permutations, sizeof(shaders));
+
+       if (!shaders)
+               return NULL;
+
+       for (i = 0; i < permutations; i++) {
+               if (create_shader_permutation(gr, &shaders[i],
+                               i, vertex_shader) < 0)
+                       goto error;
+
+               if (shaders[i])
+                       created++;
+       }
+
+       gr->shader_count = permutations;
+
+       weston_log("Created %u shader permutations\n", created);
+
+       glDeleteShader(vertex_shader);
+
+       return shaders;
+
+error:
+       destroy_shaders(shaders, permutations);
+       glDeleteShader(vertex_shader);
+       return NULL;
+}
+
+struct gl_shader *
+gl_select_shader(struct gl_renderer *gr,
+                       enum gl_input_attribute input,
+                       enum gl_output_attribute output)
+{
+       struct gl_shader *shader;
+       size_t attributes[ATTRIBUTE_COUNT] = {
+               input,
+               output,
+               CONVERSION_NONE
+       };
+
+       shader = gr->shaders[permutation_from_attributes(attributes)];
+
+       assert(shader);
+
+       return shader;
+}
+
+void
+gl_use_shader(struct gl_renderer *gr,
+                            struct gl_shader *shader)
+{
+       if (gr->current_shader == shader)
+               return;
+
+       glUseProgram(shader->program);
+       gr->current_shader = shader;
+}
+
+void
+gl_shader_set_output(struct gl_shader *shader,
+                    struct weston_output *output)
+{
+       GLfloat matrix[16];
+       size_t i;
+
+       for (i = 0; i < ARRAY_LENGTH(matrix); i++)
+               matrix[i] = output->matrix.d[i];
+
+       glUniformMatrix4fv(shader->projection_uniform,
+                          1, GL_FALSE, matrix);
+}
+
+void
+gl_shader_setup(struct gl_shader *shader,
+                      struct weston_surface *surface,
+                      struct weston_output *output)
+{
+       struct gl_surface_state *gs = get_surface_state(surface);
+
+       gl_shader_set_output(shader, output);
+
+       if (gs->input == INPUT_SOLID)
+               glUniform4fv(shader->color_uniform, 1, gs->color);
+
+       glUniform1f(shader->alpha_uniform, surface->alpha);
 }
 
 int
 gl_init_shaders(struct gl_renderer *gr)
 {
-       if (shader_init(gr, &gr->texture_shader_rgba,
-                            vertex_shader, texture_fragment_shader_rgba) < 0)
-               return -1;
-       if (shader_init(gr, &gr->texture_shader_rgbx,
-                            vertex_shader, texture_fragment_shader_rgbx) < 0)
-               return -1;
-       if (gr->has_egl_image_external &&
-                       shader_init(gr, &gr->texture_shader_egl_external,
-                               vertex_shader, 
texture_fragment_shader_egl_external) < 0)
-               return -1;
-       if (shader_init(gr, &gr->texture_shader_y_uv,
-                              vertex_shader, texture_fragment_shader_y_uv) < 0)
-               return -1;
-       if (shader_init(gr, &gr->texture_shader_y_u_v,
-                              vertex_shader, texture_fragment_shader_y_u_v) < 
0)
-               return -1;
-       if (shader_init(gr, &gr->texture_shader_y_xuxv,
-                              vertex_shader, texture_fragment_shader_y_xuxv) < 
0)
-               return -1;
-       if (shader_init(gr, &gr->solid_shader,
-                            vertex_shader, solid_fragment_shader) < 0)
+       struct gl_shader **shaders = create_shader_permutations(gr);
+
+       if (!shaders)
                return -1;
 
+       if (gr->shaders)
+               gl_destroy_shaders(gr);
+
+       gr->shaders = shaders;
+       gr->solid_shader = gl_select_shader(gr, INPUT_SOLID, OUTPUT_BLEND);
+
        return 0;
 }
 
 void
 gl_destroy_shaders(struct gl_renderer *gr)
 {
-       shader_release(&gr->texture_shader_rgba);
-       shader_release(&gr->texture_shader_rgbx);
-       shader_release(&gr->texture_shader_egl_external);
-       shader_release(&gr->texture_shader_y_uv);
-       shader_release(&gr->texture_shader_y_u_v);
-       shader_release(&gr->texture_shader_y_xuxv);
-       shader_release(&gr->solid_shader);
-
+       destroy_shaders(gr->shaders, gr->shader_count);
 }
-- 
1.8.0

_______________________________________________
wayland-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to