[PATCH] gl-renderer: Add flexible shader generation.

John Kåre Alsaker john.kare.alsaker at gmail.com
Tue Apr 8 12:26:35 PDT 2014


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. It adds gl-internal.h for
shared definitions between the gl-renderer.c and gl-shaders.c.
---
 Makefile.am       |   2 +
 src/gl-internal.h | 238 ++++++++++++++++++++++
 src/gl-renderer.c | 512 +++++-----------------------------------------
 src/gl-shaders.c  | 596 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 881 insertions(+), 467 deletions(-)
 create mode 100644 src/gl-internal.h
 create mode 100644 src/gl-shaders.c

diff --git a/Makefile.am b/Makefile.am
index a247c3d..8952d1a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -158,6 +158,8 @@ gl_renderer_la_CFLAGS =				\
 	$(EGL_CFLAGS)				\
 	$(GCC_CFLAGS)
 gl_renderer_la_SOURCES =			\
+	src/gl-shaders.c			\
+	src/gl-internal.h			\
 	src/gl-renderer.h			\
 	src/gl-renderer.c			\
 	src/vertex-clipping.c			\
diff --git a/src/gl-internal.h b/src/gl-internal.h
new file mode 100644
index 0000000..15f8c8a
--- /dev/null
+++ b/src/gl-internal.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ * Copyright © 2012 John Kåre Alsaker
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _GL_INTERNAL_H_
+#define _GL_INTERNAL_H_
+
+#include "config.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <float.h>
+#include <assert.h>
+#include <linux/input.h>
+
+#include "gl-renderer.h"
+#include "vertex-clipping.h"
+
+#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;
+	GLint projection_uniform;
+	GLint color_uniform;
+	GLint alpha_uniform;
+};
+
+#define BUFFER_DAMAGE_COUNT 2
+
+enum gl_border_status {
+	BORDER_STATUS_CLEAN = 0,
+	BORDER_TOP_DIRTY = 1 << GL_RENDERER_BORDER_TOP,
+	BORDER_LEFT_DIRTY = 1 << GL_RENDERER_BORDER_LEFT,
+	BORDER_RIGHT_DIRTY = 1 << GL_RENDERER_BORDER_RIGHT,
+	BORDER_BOTTOM_DIRTY = 1 << GL_RENDERER_BORDER_BOTTOM,
+	BORDER_ALL_DIRTY = 0xf,
+	BORDER_SIZE_CHANGED = 0x10
+};
+
+struct gl_border_image {
+	GLuint tex;
+	int32_t width, height;
+	int32_t tex_width;
+	void *data;
+};
+
+struct gl_output_state {
+	EGLSurface egl_surface;
+	pixman_region32_t buffer_damage[BUFFER_DAMAGE_COUNT];
+	enum gl_border_status border_damage[BUFFER_DAMAGE_COUNT];
+	struct gl_border_image borders[4];
+	enum gl_border_status border_status;
+};
+
+enum buffer_type {
+	BUFFER_TYPE_NULL,
+	BUFFER_TYPE_SHM,
+	BUFFER_TYPE_EGL
+};
+
+struct gl_surface_state {
+	GLfloat color[4];
+	enum gl_input_attribute input;
+
+	GLuint textures[MAX_PLANES];
+	int num_textures;
+	int needs_full_upload;
+	pixman_region32_t texture_damage;
+
+	/* These are only used by SHM surfaces to detect when we need
+	 * to do a full upload to specify a new internal texture
+	 * format */
+	GLenum gl_format;
+	GLenum gl_pixel_type;
+
+	EGLImageKHR images[MAX_PLANES];
+	GLenum target;
+	int num_images;
+
+	struct weston_buffer_reference buffer_ref;
+	enum buffer_type buffer_type;
+	int pitch; /* in pixels */
+	int height; /* in pixels */
+	int y_inverted;
+
+	struct weston_surface *surface;
+
+	struct wl_listener surface_destroy_listener;
+	struct wl_listener renderer_destroy_listener;
+};
+
+struct gl_renderer {
+	struct weston_renderer base;
+	int fragment_shader_debug;
+	int fan_debug;
+	struct weston_binding *fragment_binding;
+	struct weston_binding *fan_binding;
+
+	EGLDisplay egl_display;
+	EGLContext egl_context;
+	EGLConfig egl_config;
+
+	struct wl_array vertices;
+	struct wl_array vtxcnt;
+
+	PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
+	PFNEGLCREATEIMAGEKHRPROC create_image;
+	PFNEGLDESTROYIMAGEKHRPROC destroy_image;
+
+#ifdef EGL_EXT_swap_buffers_with_damage
+	PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
+#endif
+
+	int has_unpack_subimage;
+
+	PFNEGLBINDWAYLANDDISPLAYWL bind_display;
+	PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
+	PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
+	int has_bind_display;
+
+	int has_egl_image_external;
+
+	int has_egl_buffer_age;
+
+	int has_configless_context;
+
+	struct gl_shader *solid_shader;
+	struct gl_shader *current_shader;
+
+	struct gl_shader **shaders;
+	size_t shader_count;
+
+	struct wl_signal destroy_signal;
+};
+
+static inline struct gl_output_state *
+get_output_state(struct weston_output *output)
+{
+	return (struct gl_output_state *)output->renderer_state;
+}
+
+int
+gl_renderer_create_surface(struct weston_surface *surface);
+
+static inline struct gl_surface_state *
+get_surface_state(struct weston_surface *surface)
+{
+	if (!surface->renderer_state)
+		gl_renderer_create_surface(surface);
+
+	return (struct gl_surface_state *)surface->renderer_state;
+}
+
+static inline struct gl_renderer *
+get_renderer(struct weston_compositor *ec)
+{
+	return (struct gl_renderer *)ec->renderer;
+}
+
+int
+gl_init_shaders(struct gl_renderer *gr);
+
+void
+gl_destroy_shaders(struct gl_renderer *gr);
+
+void
+gl_shader_set_matrix(struct gl_shader *shader,
+		     struct weston_matrix *matrix);
+
+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_setup(struct gl_shader *shader,
+		       struct weston_view *view,
+		       struct weston_output *output);
+
+#endif
diff --git a/src/gl-renderer.c b/src/gl-renderer.c
index 63af75d..8064ed6 100644
--- a/src/gl-renderer.c
+++ b/src/gl-renderer.c
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2012 Intel Corporation
+ * Copyright © 2012 John Kåre Alsaker
  *
  * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
@@ -20,169 +21,7 @@
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "config.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <float.h>
-#include <assert.h>
-#include <linux/input.h>
-
-#include "gl-renderer.h"
-#include "vertex-clipping.h"
-
-#include <EGL/eglext.h>
-#include "weston-egl-ext.h"
-
-struct gl_shader {
-	GLuint program;
-	GLuint vertex_shader, fragment_shader;
-	GLint proj_uniform;
-	GLint tex_uniforms[3];
-	GLint alpha_uniform;
-	GLint color_uniform;
-	const char *vertex_source, *fragment_source;
-};
-
-#define BUFFER_DAMAGE_COUNT 2
-
-enum gl_border_status {
-	BORDER_STATUS_CLEAN = 0,
-	BORDER_TOP_DIRTY = 1 << GL_RENDERER_BORDER_TOP,
-	BORDER_LEFT_DIRTY = 1 << GL_RENDERER_BORDER_LEFT,
-	BORDER_RIGHT_DIRTY = 1 << GL_RENDERER_BORDER_RIGHT,
-	BORDER_BOTTOM_DIRTY = 1 << GL_RENDERER_BORDER_BOTTOM,
-	BORDER_ALL_DIRTY = 0xf,
-	BORDER_SIZE_CHANGED = 0x10
-};
-
-struct gl_border_image {
-	GLuint tex;
-	int32_t width, height;
-	int32_t tex_width;
-	void *data;
-};
-
-struct gl_output_state {
-	EGLSurface egl_surface;
-	pixman_region32_t buffer_damage[BUFFER_DAMAGE_COUNT];
-	enum gl_border_status border_damage[BUFFER_DAMAGE_COUNT];
-	struct gl_border_image borders[4];
-	enum gl_border_status border_status;
-};
-
-enum buffer_type {
-	BUFFER_TYPE_NULL,
-	BUFFER_TYPE_SHM,
-	BUFFER_TYPE_EGL
-};
-
-struct gl_surface_state {
-	GLfloat color[4];
-	struct gl_shader *shader;
-
-	GLuint textures[3];
-	int num_textures;
-	int needs_full_upload;
-	pixman_region32_t texture_damage;
-
-	/* These are only used by SHM surfaces to detect when we need
-	 * to do a full upload to specify a new internal texture
-	 * format */
-	GLenum gl_format;
-	GLenum gl_pixel_type;
-
-	EGLImageKHR images[3];
-	GLenum target;
-	int num_images;
-
-	struct weston_buffer_reference buffer_ref;
-	enum buffer_type buffer_type;
-	int pitch; /* in pixels */
-	int height; /* in pixels */
-	int y_inverted;
-
-	struct weston_surface *surface;
-
-	struct wl_listener surface_destroy_listener;
-	struct wl_listener renderer_destroy_listener;
-};
-
-struct gl_renderer {
-	struct weston_renderer base;
-	int fragment_shader_debug;
-	int fan_debug;
-	struct weston_binding *fragment_binding;
-	struct weston_binding *fan_binding;
-
-	EGLDisplay egl_display;
-	EGLContext egl_context;
-	EGLConfig egl_config;
-
-	struct wl_array vertices;
-	struct wl_array vtxcnt;
-
-	PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
-	PFNEGLCREATEIMAGEKHRPROC create_image;
-	PFNEGLDESTROYIMAGEKHRPROC destroy_image;
-
-#ifdef EGL_EXT_swap_buffers_with_damage
-	PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
-#endif
-
-	int has_unpack_subimage;
-
-	PFNEGLBINDWAYLANDDISPLAYWL bind_display;
-	PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
-	PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
-	int has_bind_display;
-
-	int has_egl_image_external;
-
-	int has_egl_buffer_age;
-
-	int has_configless_context;
-
-	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 *current_shader;
-
-	struct wl_signal destroy_signal;
-};
-
-static inline struct gl_output_state *
-get_output_state(struct weston_output *output)
-{
-	return (struct gl_output_state *)output->renderer_state;
-}
-
-static int
-gl_renderer_create_surface(struct weston_surface *surface);
-
-static inline struct gl_surface_state *
-get_surface_state(struct weston_surface *surface)
-{
-	if (!surface->renderer_state)
-		gl_renderer_create_surface(surface);
-
-	return (struct gl_surface_state *)surface->renderer_state;
-}
-
-static inline struct gl_renderer *
-get_renderer(struct weston_compositor *ec)
-{
-	return (struct gl_renderer *)ec->renderer;
-}
+#include "gl-internal.h"
 
 static const char *
 egl_error_string(EGLint code)
@@ -403,8 +242,8 @@ triangle_fan_debug(struct weston_view *view, 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);
@@ -479,47 +318,6 @@ use_output(struct weston_output *output)
 	return 0;
 }
 
-static int
-shader_init(struct gl_shader *shader, struct gl_renderer *gr,
-		   const char *vertex_source, const char *fragment_source);
-
-static void
-use_shader(struct gl_renderer *gr, struct gl_shader *shader)
-{
-	if (!shader->program) {
-		int ret;
-
-		ret =  shader_init(shader, gr,
-				   shader->vertex_source,
-				   shader->fragment_source);
-
-		if (ret < 0)
-			weston_log("warning: failed to compile shader\n");
-	}
-
-	if (gr->current_shader == shader)
-		return;
-	glUseProgram(shader->program);
-	gr->current_shader = shader;
-}
-
-static void
-shader_uniforms(struct gl_shader *shader,
-		struct weston_view *view,
-		struct weston_output *output)
-{
-	int i;
-	struct gl_surface_state *gs = get_surface_state(view->surface);
-
-	glUniformMatrix4fv(shader->proj_uniform,
-			   1, GL_FALSE, output->matrix.d);
-	glUniform4fv(shader->color_uniform, 1, gs->color);
-	glUniform1f(shader->alpha_uniform, view->alpha);
-
-	for (i = 0; i < gs->num_textures; i++)
-		glUniform1i(shader->tex_uniforms[i], i);
-}
-
 static void
 draw_view(struct weston_view *ev, struct weston_output *output,
 	  pixman_region32_t *damage) /* in global coordinates */
@@ -527,6 +325,7 @@ draw_view(struct weston_view *ev, struct weston_output *output,
 	struct weston_compositor *ec = ev->surface->compositor;
 	struct gl_renderer *gr = get_renderer(ec);
 	struct gl_surface_state *gs = get_surface_state(ev->surface);
+	struct gl_shader *shader;
 	/* repaint bounding region in global coordinates: */
 	pixman_region32_t repaint;
 	/* non-opaque region in surface coordinates: */
@@ -537,7 +336,7 @@ draw_view(struct weston_view *ev, struct weston_output *output,
 	/* In case of a runtime switch of renderers, we may not have received
 	 * an attach for this surface since the switch. In that case we don't
 	 * have a valid buffer or a proper shader set up so skip rendering. */
-	if (!gs->shader)
+	if (gs->buffer_type == BUFFER_TYPE_NULL)
 		return;
 
 	pixman_region32_init(&repaint);
@@ -551,12 +350,14 @@ draw_view(struct weston_view *ev, struct weston_output *output,
 	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
 
 	if (gr->fan_debug) {
-		use_shader(gr, &gr->solid_shader);
-		shader_uniforms(&gr->solid_shader, ev, output);
+		gl_use_shader(gr, gr->solid_shader);
+		gl_shader_setup(gr->solid_shader, ev, output);
 	}
 
-	use_shader(gr, gs->shader);
-	shader_uniforms(gs->shader, ev, output);
+	shader = gl_select_shader(gr, gs->input, OUTPUT_BLEND);
+
+	gl_use_shader(gr, shader);
+	gl_shader_setup(shader, ev, output);
 
 	if (ev->transform.enabled || output->zoom.active ||
 	    output->current_scale != ev->surface->buffer_viewport.buffer.scale)
@@ -578,14 +379,15 @@ draw_view(struct weston_view *ev, struct weston_output *output,
 
 	/* XXX: Should we be using ev->transform.opaque here? */
 	if (pixman_region32_not_empty(&ev->surface->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.
 			 */
-			use_shader(gr, &gr->texture_shader_rgbx);
-			shader_uniforms(&gr->texture_shader_rgbx, ev, 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, ev, output);
 		}
 
 		if (ev->alpha < 1.0)
@@ -597,7 +399,7 @@ draw_view(struct weston_view *ev, struct weston_output *output,
 	}
 
 	if (pixman_region32_not_empty(&surface_blend)) {
-		use_shader(gr, gs->shader);
+		gl_use_shader(gr, shader);
 		glEnable(GL_BLEND);
 		repaint_region(ev, &repaint, &surface_blend);
 	}
@@ -706,7 +508,7 @@ draw_output_borders(struct weston_output *output,
 {
 	struct gl_output_state *go = get_output_state(output);
 	struct gl_renderer *gr = get_renderer(output->compositor);
-	struct gl_shader *shader = &gr->texture_shader_rgba;
+	struct gl_shader *shader;
 	struct gl_border_image *top, *bottom, *left, *right;
 	struct weston_matrix matrix;
 	int full_width, full_height;
@@ -714,6 +516,8 @@ draw_output_borders(struct weston_output *output,
 	if (border_status == BORDER_STATUS_CLEAN)
 		return; /* Clean. Nothing to do. */
 
+	shader = gl_select_shader(gr, INPUT_RGBA, OUTPUT_BLEND);
+
 	top = &go->borders[GL_RENDERER_BORDER_TOP];
 	bottom = &go->borders[GL_RENDERER_BORDER_BOTTOM];
 	left = &go->borders[GL_RENDERER_BORDER_LEFT];
@@ -722,17 +526,18 @@ draw_output_borders(struct weston_output *output,
 	full_width = output->current_mode->width + left->width + right->width;
 	full_height = output->current_mode->height + top->height + bottom->height;
 
+	shader = gl_select_shader(gr, INPUT_RGBA, OUTPUT_BLEND);
+
 	glDisable(GL_BLEND);
-	use_shader(gr, shader);
+	gl_use_shader(gr, shader);
 
 	glViewport(0, 0, full_width, full_height);
 
 	weston_matrix_init(&matrix);
 	weston_matrix_translate(&matrix, -full_width/2.0, -full_height/2.0, 0);
 	weston_matrix_scale(&matrix, 2.0/full_width, -2.0/full_height, 1);
-	glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, matrix.d);
+	gl_shader_set_matrix(shader, &matrix);
 
-	glUniform1i(shader->tex_uniforms[0], 0);
 	glUniform1f(shader->alpha_uniform, 1);
 	glActiveTexture(GL_TEXTURE0);
 
@@ -1124,19 +929,19 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer,
 
 	switch (wl_shm_buffer_get_format(shm_buffer)) {
 	case WL_SHM_FORMAT_XRGB8888:
-		gs->shader = &gr->texture_shader_rgbx;
+		gs->input = INPUT_RGBX;
 		pitch = wl_shm_buffer_get_stride(shm_buffer) / 4;
 		gl_format = GL_BGRA_EXT;
 		gl_pixel_type = GL_UNSIGNED_BYTE;
 		break;
 	case WL_SHM_FORMAT_ARGB8888:
-		gs->shader = &gr->texture_shader_rgba;
+		gs->input = INPUT_RGBA;
 		pitch = wl_shm_buffer_get_stride(shm_buffer) / 4;
 		gl_format = GL_BGRA_EXT;
 		gl_pixel_type = GL_UNSIGNED_BYTE;
 		break;
 	case WL_SHM_FORMAT_RGB565:
-		gs->shader = &gr->texture_shader_rgbx;
+		gs->input = INPUT_RGBX;
 		pitch = wl_shm_buffer_get_stride(shm_buffer) / 2;
 		gl_format = GL_RGB;
 		gl_pixel_type = GL_UNSIGNED_SHORT_5_6_5;
@@ -1194,30 +999,35 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer,
 	gs->target = GL_TEXTURE_2D;
 	switch (format) {
 	case EGL_TEXTURE_RGB:
+		num_planes = 1;
+		gs->input = INPUT_RGBX;
+		break;
 	case EGL_TEXTURE_RGBA:
 	default:
 		num_planes = 1;
-		gs->shader = &gr->texture_shader_rgba;
+		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;
@@ -1291,14 +1101,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 void
@@ -1349,7 +1158,7 @@ surface_state_handle_renderer_destroy(struct wl_listener *listener, void *data)
 	surface_state_destroy(gs, gr);
 }
 
-static int
+int
 gl_renderer_create_surface(struct weston_surface *surface)
 {
 	struct gl_surface_state *gs;
@@ -1389,196 +1198,6 @@ gl_renderer_create_surface(struct weston_surface *surface)
 	return 0;
 }
 
-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";
-
-/* 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 const char fragment_debug[] =
-	"  gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + gl_FragColor * 0.8;\n";
-
-static const char fragment_brace[] =
-	"}\n";
-
-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"
-	"void main()\n"
-	"{\n"
-	"   gl_FragColor = alpha * color\n;"
-	;
-
-static int
-compile_shader(GLenum type, int count, const char **sources)
-{
-	GLuint s;
-	char msg[512];
-	GLint status;
-
-	s = glCreateShader(type);
-	glShaderSource(s, count, sources, NULL);
-	glCompileShader(s);
-	glGetShaderiv(s, GL_COMPILE_STATUS, &status);
-	if (!status) {
-		glGetShaderInfoLog(s, sizeof msg, NULL, msg);
-		weston_log("shader info: %s\n", msg);
-		return GL_NONE;
-	}
-
-	return s;
-}
-
-static int
-shader_init(struct gl_shader *shader, struct gl_renderer *renderer,
-		   const char *vertex_source, 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;
-	}
-
-	shader->fragment_shader =
-		compile_shader(GL_FRAGMENT_SHADER, count, sources);
-
-	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(shader->program);
-	glGetProgramiv(shader->program, GL_LINK_STATUS, &status);
-	if (!status) {
-		glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg);
-		weston_log("link info: %s\n", msg);
-		return -1;
-	}
-
-	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");
-
-	return 0;
-}
-
-static void
-shader_release(struct gl_shader *shader)
-{
-	glDeleteShader(shader->vertex_shader);
-	glDeleteShader(shader->fragment_shader);
-	glDeleteProgram(shader->program);
-
-	shader->vertex_shader = 0;
-	shader->fragment_shader = 0;
-	shader->program = 0;
-}
-
 static void
 log_extensions(const char *name, const char *extensions)
 {
@@ -1821,6 +1440,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,
@@ -1838,6 +1459,7 @@ gl_renderer_destroy(struct weston_compositor *ec)
 		weston_binding_destroy(gr->fan_binding);
 
 	free(gr);
+	ec->renderer = NULL;
 }
 
 static int
@@ -1974,62 +1596,18 @@ gl_renderer_display(struct weston_compositor *ec)
 	return get_renderer(ec)->egl_display;
 }
 
-static int
-compile_shaders(struct weston_compositor *ec)
-{
-	struct gl_renderer *gr = get_renderer(ec);
-
-	gr->texture_shader_rgba.vertex_source = vertex_shader;
-	gr->texture_shader_rgba.fragment_source = texture_fragment_shader_rgba;
-
-	gr->texture_shader_rgbx.vertex_source = vertex_shader;
-	gr->texture_shader_rgbx.fragment_source = texture_fragment_shader_rgbx;
-
-	gr->texture_shader_egl_external.vertex_source = vertex_shader;
-	gr->texture_shader_egl_external.fragment_source =
-		texture_fragment_shader_egl_external;
-
-	gr->texture_shader_y_uv.vertex_source = vertex_shader;
-	gr->texture_shader_y_uv.fragment_source = texture_fragment_shader_y_uv;
-
-	gr->texture_shader_y_u_v.vertex_source = vertex_shader;
-	gr->texture_shader_y_u_v.fragment_source =
-		texture_fragment_shader_y_u_v;
-
-	gr->texture_shader_y_xuxv.vertex_source = vertex_shader;
-	gr->texture_shader_y_xuxv.fragment_source =
-		texture_fragment_shader_y_xuxv;
-
-	gr->solid_shader.vertex_source = vertex_shader;
-	gr->solid_shader.fragment_source = solid_fragment_shader;
-
-	return 0;
-}
-
 static void
 fragment_debug_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
 		       void *data)
 {
 	struct weston_compositor *ec = data;
 	struct gl_renderer *gr = get_renderer(ec);
-	struct weston_output *output;
 
 	gr->fragment_shader_debug ^= 1;
 
-	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);
-
-	/* Force use_shader() to call glUseProgram(), since we need to use
-	 * the recompiled version of the shader. */
-	gr->current_shader = NULL;
+	gl_init_shaders(gr);
 
-	wl_list_for_each(output, &ec->output_list, link)
-		weston_output_damage(output);
+	weston_compositor_damage_all(ec);
 }
 
 static void
@@ -2116,7 +1694,7 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface)
 
 	glActiveTexture(GL_TEXTURE0);
 
-	if (compile_shaders(ec))
+	if (gl_init_shaders(gr) < 0)
 		return -1;
 
 	gr->fragment_binding =
diff --git a/src/gl-shaders.c b/src/gl-shaders.c
new file mode 100644
index 0000000..be356dc
--- /dev/null
+++ b/src/gl-shaders.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright © 2012 John Kåre Alsaker
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "gl-internal.h"
+
+#define STRINGIFY(expr) #expr
+#define STRINGIFY_VALUE(expr) STRINGIFY(expr)
+
+static const size_t attribute_counts[ATTRIBUTE_COUNT] = {
+	INPUT_COUNT,
+	OUTPUT_COUNT,
+	CONVERSION_COUNT
+};
+
+struct shader_builder;
+
+typedef int (*gl_shader_constructor_t)
+				  (struct shader_builder *builder);
+typedef void (*gl_shader_setup_uniforms_t)(struct shader_builder *builder,
+					   struct gl_shader *shader);
+
+struct gl_input_type_desc {
+	int transparent;
+	gl_shader_constructor_t constructor;
+	gl_shader_setup_uniforms_t setup_uniforms;
+};
+
+struct shader_string {
+	size_t length;
+	const char *str;
+};
+
+struct shader_string_list {
+	struct shader_builder *builder;
+	struct wl_array array;
+};
+
+struct shader_builder {
+	struct gl_renderer *renderer;
+	struct gl_input_type_desc *desc;
+	int error;
+	size_t attributes[ATTRIBUTE_COUNT];
+	struct shader_string_list directives, global, body;
+	size_t result_size;
+	struct wl_array result;
+};
+
+static void
+shader_string_list_init(struct shader_string_list *strings,
+			struct shader_builder *builder)
+{
+	strings->builder = builder;
+	wl_array_init(&strings->array);
+}
+
+static void
+shader_builder_init(struct shader_builder *builder)
+{
+	builder->error = 0;
+	builder->result_size = 1;
+	wl_array_init(&builder->result);
+	shader_string_list_init(&builder->directives, builder);
+	shader_string_list_init(&builder->global, builder);
+	shader_string_list_init(&builder->body, builder);
+}
+
+static void
+shader_builder_release(struct shader_builder *builder)
+{
+	wl_array_release(&builder->directives.array);
+	wl_array_release(&builder->global.array);
+	wl_array_release(&builder->body.array);
+}
+
+static void
+append_shader_string_list(char **result, struct shader_string_list *string)
+{
+	struct shader_string *str;
+
+	wl_array_for_each(str, &string->array) {
+		memcpy(*result, str->str, str->length);
+		*result += str->length;
+	}
+}
+
+static const char *
+shader_builder_get_string(struct shader_builder *builder)
+{
+	char *data;
+
+	if (builder->error)
+		return NULL;
+
+	data = wl_array_add(&builder->result, builder->result_size);
+
+	if (!data)
+		return NULL;
+
+	append_shader_string_list(&data, &builder->directives);
+	append_shader_string_list(&data, &builder->global);
+	append_shader_string_list(&data, &builder->body);
+
+	*data = 0;
+
+	return builder->result.data;
+}
+
+static void
+append(struct shader_string_list *list, const char *string)
+{
+	struct shader_string str;
+	struct shader_string *data;
+
+	if (!string) {
+		list->builder->error = 1;
+		return;
+	}
+
+	str.str = string;
+	str.length = strlen(string);
+	list->builder->result_size += str.length;
+
+	if (str.length > list->builder->result_size)
+		list->builder->error = 3;
+
+	data = wl_array_add(&list->array, sizeof(str));
+	if (!data)
+		list->builder->error = 2;
+	else
+		*data = str;
+}
+
+static int
+shader_rgbx_constructor(struct shader_builder *sb)
+{
+	append(&sb->global, "uniform sampler2D texture;\n");
+	append(&sb->body,
+		"gl_FragColor.rgb = texture2D(texture, texture_coord).rgb;\n" \
+		"gl_FragColor.a = 1.0;\n");
+
+	return 1;
+}
+
+static int
+shader_rgba_constructor(struct shader_builder *sb)
+{
+	append(&sb->global, "uniform sampler2D texture;\n");
+	append(&sb->body,
+		"gl_FragColor = texture2D(texture, texture_coord);\n");
+
+
+	return 1;
+}
+
+static int
+shader_egl_external_constructor(struct shader_builder *sb)
+{
+	if (!sb->renderer->has_egl_image_external)
+		return 0;
+
+	append(&sb->directives,
+		"#extension GL_OES_EGL_image_external : require;\n");
+	append(&sb->global,
+		"uniform samplerExternalOES texture;\n");
+	append(&sb->body,
+		"gl_FragColor = texture2D(texture, texture_coord);\n");
+
+	return 1;
+}
+
+static void
+shader_texture_uniforms(struct shader_builder *sb,
+			struct gl_shader *shader)
+{
+	glUniform1i(glGetUniformLocation(shader->program, "texture"), 0);
+}
+
+static int
+shader_yuv_constructor(struct shader_builder *sb)
+{
+	const char *sample;
+
+	append(&sb->global,
+		"uniform sampler2D planes[" STRINGIFY_VALUE(MAX_PLANES) "];\n");
+
+	switch (sb->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;
+	}
+
+	append(&sb->body, sample);
+	append(&sb->body,
+		"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 1;
+}
+
+static void
+shader_yuv_uniforms(struct shader_builder *sb, struct gl_shader *shader)
+{
+	int i;
+	GLint values[MAX_PLANES];
+
+	for (i = 0; i < MAX_PLANES; i++)
+		values[i] = i;
+
+	glUniform1iv(glGetUniformLocation(shader->program, "planes"),
+		     MAX_PLANES, values);
+}
+
+static int
+shader_solid_constructor(struct shader_builder *sb)
+{
+	append(&sb->global, "uniform vec4 color;\n");
+	append(&sb->body, "gl_FragColor = color;\n");
+
+	return 1;
+}
+
+static void
+shader_solid_uniforms(struct shader_builder *sb, struct gl_shader *shader)
+{
+	shader->color_uniform = glGetUniformLocation(shader->program, "color");
+}
+
+static struct gl_input_type_desc input_type_descs[INPUT_COUNT] = {
+	/* INPUT_RGBX */
+	{0, shader_rgbx_constructor, shader_texture_uniforms},
+
+	/* INPUT_RGBA */
+	{1, shader_rgba_constructor, shader_texture_uniforms},
+
+	/* INPUT_EGL_EXTERNAL */
+	{1, shader_egl_external_constructor, shader_texture_uniforms},
+
+	/* INPUT_Y_UV */
+	{0, shader_yuv_constructor, shader_yuv_uniforms},
+
+	/* INPUT_Y_U_V */
+	{0, shader_yuv_constructor, shader_yuv_uniforms},
+
+	/* INPUT_Y_XUXV */
+	{0, shader_yuv_constructor, shader_yuv_uniforms},
+
+	/* INPUT_SOLID */
+	{1, 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_Position = projection * vec4(position, 0.0, 1.0);\n"
+	"   texture_coord = attr_texture_coord;\n"
+	"}\n";
+
+static GLuint
+compile_shader(GLenum type, const char *source)
+{
+	GLuint s;
+	char msg[512];
+	GLint status;
+
+	s = glCreateShader(type);
+	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;
+	}
+
+	return s;
+}
+
+static struct gl_shader *
+shader_create(GLuint vertex_shader,
+		const char *fragment_source)
+{
+	char msg[512];
+	GLint status;
+	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);
+
+	glBindAttribLocation(program, 0, "position");
+	glBindAttribLocation(program, 1, "attr_texture_coord");
+
+	glLinkProgram(program);
+	glGetProgramiv(program, GL_LINK_STATUS, &status);
+
+	if (!status) {
+		glGetProgramInfoLog(program, sizeof msg, NULL, msg);
+		weston_log("link info: %s\n", msg);
+		free(shader);
+		return NULL;
+	}
+
+	shader->program = program;
+	shader->projection_uniform = glGetUniformLocation(program,
+							  "projection");
+	shader->alpha_uniform = glGetUniformLocation(program, "alpha");
+
+	return shader;
+}
+
+
+static void
+destroy_shaders(struct gl_shader **shaders, size_t count)
+{
+	size_t i;
+
+	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 sb;
+	const char *fragment_shader;
+
+	attributes_from_permutation(permutation, sb.attributes);
+
+	sb.renderer = renderer;
+	sb.desc = &input_type_descs[sb.attributes[ATTRIBUTE_INPUT]];
+
+	shader_builder_init(&sb);
+
+	append(&sb.global, "precision mediump float;\n" \
+		"varying vec2 texture_coord;\n" \
+		"uniform float alpha;\n");
+
+	append(&sb.body, "void main()\n{\n");
+
+	if (!sb.desc->constructor(&sb)) {
+		shader_builder_release(&sb);
+		return 0;
+	}
+
+	append(&sb.body, "gl_FragColor *= alpha;\n");
+
+	if (renderer->fragment_shader_debug)
+		append(&sb.body, "gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + " \
+			"gl_FragColor * 0.8;\n");
+
+	append(&sb.body, "}\n");
+
+	fragment_shader = shader_builder_get_string(&sb);
+
+	if (!fragment_shader)
+		goto error;
+
+	*shader = shader_create(vertex_shader, fragment_shader);
+
+	if (!*shader)
+		goto error;
+
+	glUseProgram((*shader)->program);
+
+	sb.desc->setup_uniforms(&sb, *shader);
+
+	shader_builder_release(&sb);
+
+	return 0;
+
+error:
+	shader_builder_release(&sb);
+	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_matrix(struct gl_shader *shader,
+		     struct weston_matrix *matrix)
+{
+	GLfloat m[16];
+	size_t i;
+
+	for (i = 0; i < ARRAY_LENGTH(m); i++)
+		m[i] = matrix->d[i];
+
+	glUniformMatrix4fv(shader->projection_uniform,
+			   1, GL_FALSE, m);
+}
+
+void
+gl_shader_setup(struct gl_shader *shader,
+		       struct weston_view *view,
+		       struct weston_output *output)
+{
+	struct gl_surface_state *gs = get_surface_state(view->surface);
+
+	gl_shader_set_matrix(shader, &output->matrix);
+
+	if (gs->input == INPUT_SOLID)
+		glUniform4fv(shader->color_uniform, 1, gs->color);
+
+	glUniform1f(shader->alpha_uniform, view->alpha);
+}
+
+int
+gl_init_shaders(struct gl_renderer *gr)
+{
+	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);
+
+	/* Force use_shader() to call glUseProgram(), since we need to use
+	 * the recompiled version of the shader. */
+	gr->current_shader = NULL;
+
+	return 0;
+}
+
+void
+gl_destroy_shaders(struct gl_renderer *gr)
+{
+	destroy_shaders(gr->shaders, gr->shader_count);
+}
-- 
1.9.1



More information about the wayland-devel mailing list