gl: add GL subsystem
authorDavid Herrmann <dh.herrmann@googlemail.com>
Fri, 23 Mar 2012 11:50:35 +0000 (12:50 +0100)
committerDavid Herrmann <dh.herrmann@googlemail.com>
Fri, 23 Mar 2012 11:50:35 +0000 (12:50 +0100)
The GL subsystem is copied from the old output_context subsystem and now
provides shader and GL-math. It will replace the old output_context subs
soon.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
Makefile.am
src/gl.h [new file with mode: 0644]
src/gl_math.c [new file with mode: 0644]
src/gl_shader.c [new file with mode: 0644]

index 31e1840..4c385dd 100644 (file)
@@ -54,7 +54,10 @@ libkmscon_core_la_SOURCES = \
        src/pty.c src/pty.h \
        src/uterm.h src/uterm_internal.h \
        src/uterm_video.c \
-       src/uterm_video_drm.c
+       src/uterm_video_drm.c \
+       src/gl.h \
+       src/gl_math.c \
+       src/gl_shader.c
 
 if USE_XKBCOMMON
 libkmscon_core_la_SOURCES += \
diff --git a/src/gl.h b/src/gl.h
new file mode 100644 (file)
index 0000000..5bf9694
--- /dev/null
+++ b/src/gl.h
@@ -0,0 +1,93 @@
+/*
+ * GL - Graphics Layer
+ *
+ * Copyright (c) 2011-2012 David Herrmann <dh.herrmann@googlemail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Graphics Layer
+ * This provides lots of helpers to work with OpenGL APIs. This includes
+ * math-helpers, basic shaders and a texture-API. If working with this API,
+ * there must always be a valid OpenGL-context!
+ */
+
+#ifndef GL_GL_H
+#define GL_GL_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+/* miscellaneous */
+void gl_clear_error();
+bool gl_has_error();
+
+/*
+ * Math Helpers
+ * The gl_m4 type is a 4x4 matrix of floats. The gl_m4_stack is a stack of m4
+ * matrices where you can only access the top-most member.
+ */
+
+struct gl_m4_stack;
+
+void gl_m4_identity(float *m);
+void gl_m4_copy(float *dest, const float *src);
+void gl_m4_mult_dest(float *dest, const float *n, const float *m);
+void gl_m4_mult(float *n, const float *m);
+void gl_m4_translate(float *m, float x, float y, float z);
+void gl_m4_scale(float *m, float x, float y, float z);
+void gl_m4_transpose_dest(float *dest, const float *src);
+void gl_m4_transpose(float *m);
+
+int gl_m4_stack_new(struct gl_m4_stack **out);
+void gl_m4_stack_free(struct gl_m4_stack *stack);
+float *gl_m4_stack_push(struct gl_m4_stack *stack);
+float *gl_m4_stack_pop(struct gl_m4_stack *stack);
+float *gl_m4_stack_tip(struct gl_m4_stack *stack);
+
+/*
+ * Texture API
+ * This allows to create new textures which can then be used to draw images with
+ * the shader API.
+ */
+
+unsigned int gl_tex_new();
+void gl_tex_free(unsigned int tex);
+void gl_tex_load(unsigned int tex, unsigned int width,
+                       unsigned int height, void *buf);
+
+/*
+ * Shader API
+ */
+
+struct gl_shader;
+
+int gl_shader_new(struct gl_shader **out);
+void gl_shader_ref(struct gl_shader *shader);
+void gl_shader_unref(struct gl_shader *shader);
+
+void gl_shader_draw_def(struct gl_shader *shader, float *vertices,
+                       float *colors, size_t num);
+void gl_shader_draw_tex(struct gl_shader *shader, const float *vertices,
+                       const float *texcoords, size_t num,
+                       unsigned int tex, const float *m);
+
+#endif /* GL_GL_H */
diff --git a/src/gl_math.c b/src/gl_math.c
new file mode 100644 (file)
index 0000000..5cc40e8
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * GL - Graphics Layer
+ *
+ * Copyright (c) 2011 David Herrmann <dh.herrmann@googlemail.com>
+ * Copyright (c) 2011 University of Tuebingen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Math Helper
+ * Basic linear algebra.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "gl.h"
+
+struct gl_m4_entry {
+       struct gl_m4_entry *next;
+       float matrix[16];
+};
+
+struct gl_m4_stack {
+       struct gl_m4_entry stack;
+       struct gl_m4_entry *cache;
+};
+
+void gl_m4_identity(float *m)
+{
+       if (!m)
+               return;
+
+       m[0] = 1;
+       m[1] = 0;
+       m[2] = 0;
+       m[3] = 0;
+
+       m[4] = 0;
+       m[5] = 1;
+       m[6] = 0;
+       m[7] = 0;
+
+       m[8] = 0;
+       m[9] = 0;
+       m[10] = 1;
+       m[11] = 0;
+
+       m[12] = 0;
+       m[13] = 0;
+       m[14] = 0;
+       m[15] = 1;
+}
+
+void gl_m4_copy(float *dest, const float *src)
+{
+       if (!dest || !src)
+               return;
+
+       dest[0] = src[0];
+       dest[1] = src[1];
+       dest[2] = src[2];
+       dest[3] = src[3];
+
+       dest[4] = src[4];
+       dest[5] = src[5];
+       dest[6] = src[6];
+       dest[7] = src[7];
+
+       dest[8] = src[8];
+       dest[9] = src[9];
+       dest[10] = src[10];
+       dest[11] = src[11];
+
+       dest[12] = src[12];
+       dest[13] = src[13];
+       dest[14] = src[14];
+       dest[15] = src[15];
+}
+
+/* Matrix Multiplication
+ * This is the simplest algorithm to multiply two matrices. It computes:
+ *    dest = n * m
+ * That is, n is left side and m is right side.
+ *
+ * Matrix-multiplication is heavy, avoid it if possible.
+ */
+void gl_m4_mult_dest(float *dest, const float *n, const float *m)
+{
+       unsigned int row, col, j;
+
+       if (!dest || !n || !m)
+               return;
+
+       for (row = 0; row < 4; ++row) {
+               for (col = 0; col < 4; ++col) {
+                       dest[row * 4 + col] = 0;
+                       for (j = 0; j < 4; ++j)
+                               dest[row * 4 + col] +=
+                                       n[row * 4 + j] * m[j * 4 + col];
+               }
+       }
+}
+
+/* this computes n = n * m */
+void gl_m4_mult(float *n, const float *m)
+{
+       float tmp[16];
+
+       if (!n || !m)
+               return;
+
+       gl_m4_mult_dest(tmp, n, m);
+       gl_m4_copy(n, tmp);
+}
+
+void gl_m4_translate(float *m, float x, float y, float z)
+{
+       float trans[16] = { 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1 };
+
+       if (!m)
+               return;
+
+       gl_m4_mult(m, trans);
+}
+
+void gl_m4_scale(float *m, float x, float y, float z)
+{
+       float scale[16] = { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 };
+
+       if (!m)
+               return;
+
+       gl_m4_mult(m, scale);
+}
+
+void gl_m4_transpose_dest(float *dest, const float *src)
+{
+       if (!dest || !src)
+               return;
+
+       dest[0] = src[0];
+       dest[5] = src[5];
+       dest[10] = src[10];
+       dest[15] = src[15];
+
+       dest[1] = src[4];
+       dest[4] = src[1];
+
+       dest[8] = src[2];
+       dest[2] = src[8];
+
+       dest[3] = src[12];
+       dest[12] = src[3];
+
+       dest[7] = src[13];
+       dest[13] = src[7];
+
+       dest[11] = src[14];
+       dest[14] = src[11];
+
+       dest[6] = src[9];
+       dest[9] = src[6];
+}
+
+void gl_m4_transpose(float *m)
+{
+       float tmp;
+
+       if (!m)
+               return;
+
+       tmp = m[1];
+       m[1] = m[4];
+       m[4] = tmp;
+
+       tmp = m[8];
+       m[8] = m[2];
+       m[2] = tmp;
+
+       tmp = m[3];
+       m[3] = m[12];
+       m[12] = tmp;
+
+       tmp = m[7];
+       m[7] = m[13];
+       m[13] = tmp;
+
+       tmp = m[11];
+       m[11] = m[14];
+       m[14] = tmp;
+
+       tmp = m[6];
+       m[6] = m[9];
+       m[9] = tmp;
+}
+
+int gl_m4_stack_new(struct gl_m4_stack **out)
+{
+       struct gl_m4_stack *stack;
+
+       if (!out)
+               return -EINVAL;
+
+       stack = malloc(sizeof(*stack));
+       if (!stack)
+               return -ENOMEM;
+
+       memset(stack, 0, sizeof(*stack));
+       gl_m4_identity(stack->stack.matrix);
+
+       *out = stack;
+       return 0;
+}
+
+void gl_m4_stack_free(struct gl_m4_stack *stack)
+{
+       struct gl_m4_entry *tmp;
+
+       if (!stack)
+               return;
+
+       while (stack->stack.next) {
+               tmp = stack->stack.next;
+               stack->stack.next = tmp->next;
+               free(tmp);
+       }
+
+       while (stack->cache) {
+               tmp = stack->cache;
+               stack->cache = tmp->next;
+               free(tmp);
+       }
+
+       free(stack);
+}
+
+float *gl_m4_stack_push(struct gl_m4_stack *stack)
+{
+       struct gl_m4_entry *entry;
+
+       if (stack->cache) {
+               entry = stack->cache;
+               stack->cache = entry->next;
+       } else {
+               entry = malloc(sizeof(*entry));
+               if (!entry)
+                       return NULL;
+       }
+
+       gl_m4_copy(entry->matrix, stack->stack.matrix);
+       entry->next = stack->stack.next;
+       stack->stack.next = entry;
+
+       return stack->stack.matrix;
+}
+
+float *gl_m4_stack_pop(struct gl_m4_stack *stack)
+{
+       struct gl_m4_entry *entry;
+
+       if (!stack)
+               return NULL;
+
+       entry = stack->stack.next;
+       if (!entry) {
+               gl_m4_identity(stack->stack.matrix);
+               return stack->stack.matrix;
+       }
+
+       stack->stack.next = entry->next;
+       entry->next = stack->cache;
+       stack->cache = entry;
+
+       gl_m4_copy(stack->stack.matrix, entry->matrix);
+
+       return stack->stack.matrix;
+}
+
+float *gl_m4_stack_tip(struct gl_m4_stack *stack)
+{
+       if (!stack)
+               return NULL;
+
+       return stack->stack.matrix;
+}
diff --git a/src/gl_shader.c b/src/gl_shader.c
new file mode 100644 (file)
index 0000000..d10ccd4
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * GL - Graphics Layer
+ *
+ * Copyright (c) 2011 David Herrmann <dh.herrmann@googlemail.com>
+ * Copyright (c) 2011 University of Tuebingen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Shader API
+ * This provides basic shader objects that are used to draw sprites and
+ * textures.
+ */
+
+#define GL_GLEXT_PROTOTYPES
+
+#include <errno.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "gl.h"
+#include "output.h"
+#include "log.h"
+
+#define LOG_SUBSYSTEM "gl"
+
+/* Clear the GL error stack. The standard says that the error value is just a
+ * single value and no list/stack. However, multiple error fields may be defined
+ * and glGetError() returns only one of them until all are cleared. Hence, we
+ * loop until no more error is retrieved.
+ */
+void gl_clear_error()
+{
+       GLenum err;
+
+       do {
+               err = glGetError();
+       } while (err != GL_NO_ERROR);
+}
+
+/* return true if there is a pending GL error */
+bool gl_has_error()
+{
+       GLenum err;
+
+       err = glGetError();
+       if (err != GL_NO_ERROR) {
+               log_err("GL error %d", err);
+               return true;
+       }
+
+       return false;
+}
+
+unsigned int gl_tex_new()
+{
+       GLuint tex = 0;
+
+       glGenTextures(1, &tex);
+       glBindTexture(GL_TEXTURE_2D, tex);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+       return tex;
+}
+
+void gl_tex_free(unsigned int tex)
+{
+       glDeleteTextures(1, &tex);
+}
+
+void gl_tex_load(unsigned int tex, unsigned int width,
+                       unsigned int height, void *buf)
+{
+       if (!buf || !width || !height)
+               return;
+
+       /* With OpenGL instead of OpenGLES2 we must use this on linux:
+        * glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA,
+        *              GL_UNSIGNED_BYTE, buf);
+        */
+
+       glBindTexture(GL_TEXTURE_2D, tex);
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, height, 0,
+                       GL_BGRA_EXT, GL_UNSIGNED_BYTE, buf);
+}
+
+struct gl_shader {
+       unsigned long ref;
+
+       GLuint def_program;
+       GLuint def_vshader;
+       GLuint def_fshader;
+       GLuint def_uni_projection;
+
+       GLuint tex_program;
+       GLuint tex_vshader;
+       GLuint tex_fshader;
+       GLuint tex_uni_projection;
+       GLuint tex_uni_texture;
+};
+
+/* external shader sources; generated during build */
+extern const char *kmscon_vert_def;
+extern const char *kmscon_frag_def;
+extern const char *kmscon_vert_tex;
+extern const char *kmscon_frag_tex;
+
+static int compile_shader(GLenum type, const char *source)
+{
+       char msg[512];
+       GLint status = 1;
+       GLuint s;
+
+       s = glCreateShader(type);
+       if (s == GL_NONE) {
+               log_warn("cannot allocate GL shader");
+               return GL_NONE;
+       }
+
+       glShaderSource(s, 1, &source, NULL);
+       glCompileShader(s);
+
+       glGetShaderiv(s, GL_COMPILE_STATUS, &status);
+       if (status == GL_FALSE) {
+               msg[0] = 0;
+               glGetShaderInfoLog(s, sizeof(msg), NULL, msg);
+               log_warn("cannot compile shader: %s", msg);
+               return GL_NONE;
+       }
+
+       return s;
+}
+
+static int init_def_shader(struct gl_shader *shader)
+{
+       char msg[512];
+       GLint status = 1;
+       int ret;
+
+       shader->def_vshader = compile_shader(GL_VERTEX_SHADER,
+                                               kmscon_vert_def);
+       if (shader->def_vshader == GL_NONE)
+               return -EFAULT;
+
+       shader->def_fshader = compile_shader(GL_FRAGMENT_SHADER,
+                                               kmscon_frag_def);
+       if (shader->def_fshader == GL_NONE) {
+               ret = -EFAULT;
+               goto err_vshader;
+       }
+
+       shader->def_program = glCreateProgram();
+       glAttachShader(shader->def_program, shader->def_vshader);
+       glAttachShader(shader->def_program, shader->def_fshader);
+       glBindAttribLocation(shader->def_program, 0, "position");
+       glBindAttribLocation(shader->def_program, 1, "color");
+
+       glLinkProgram(shader->def_program);
+       glGetProgramiv(shader->def_program, GL_LINK_STATUS, &status);
+       if (status == GL_FALSE) {
+               msg[0] = 0;
+               glGetProgramInfoLog(shader->def_program, sizeof(msg),
+                                       NULL, msg);
+               log_warn("cannot link shader: %s", msg);
+               ret = -EFAULT;
+               goto err_link;
+       }
+
+       shader->def_uni_projection =
+               glGetUniformLocation(shader->def_program, "projection");
+
+       return 0;
+
+err_link:
+       glDeleteProgram(shader->def_program);
+       glDeleteShader(shader->def_fshader);
+err_vshader:
+       glDeleteShader(shader->def_vshader);
+       return ret;
+}
+
+static void free_def_shader(struct gl_shader *shader)
+{
+       glDeleteProgram(shader->def_program);
+       glDeleteShader(shader->def_fshader);
+       glDeleteShader(shader->def_vshader);
+}
+
+static int init_tex_shader(struct gl_shader *shader)
+{
+       char msg[512];
+       GLint status = 1;
+       int ret;
+
+       shader->tex_vshader = compile_shader(GL_VERTEX_SHADER,
+                                               kmscon_vert_tex);
+       if (shader->tex_vshader == GL_NONE)
+               return -EFAULT;
+
+       shader->tex_fshader = compile_shader(GL_FRAGMENT_SHADER,
+                                               kmscon_frag_tex);
+       if (shader->tex_fshader == GL_NONE) {
+               ret = -EFAULT;
+               goto err_vshader;
+       }
+
+       shader->tex_program = glCreateProgram();
+       glAttachShader(shader->tex_program, shader->tex_vshader);
+       glAttachShader(shader->tex_program, shader->tex_fshader);
+       glBindAttribLocation(shader->tex_program, 0, "position");
+       glBindAttribLocation(shader->tex_program, 1, "texture_position");
+
+       glLinkProgram(shader->tex_program);
+       glGetProgramiv(shader->tex_program, GL_LINK_STATUS, &status);
+       if (status == GL_FALSE) {
+               msg[0] = 0;
+               glGetProgramInfoLog(shader->tex_program, sizeof(msg),
+                                       NULL, msg);
+               log_warn("cannot link shader: %s", msg);
+               ret = -EFAULT;
+               goto err_link;
+       }
+
+       shader->tex_uni_projection =
+               glGetUniformLocation(shader->tex_program, "projection");
+       shader->tex_uni_texture =
+               glGetUniformLocation(shader->tex_program, "texture");
+
+       return 0;
+
+err_link:
+       glDeleteProgram(shader->tex_program);
+       glDeleteShader(shader->tex_fshader);
+err_vshader:
+       glDeleteShader(shader->tex_vshader);
+       return ret;
+}
+
+static void free_tex_shader(struct gl_shader *shader)
+{
+       glDeleteProgram(shader->tex_program);
+       glDeleteShader(shader->tex_fshader);
+       glDeleteShader(shader->tex_vshader);
+}
+
+int gl_shader_new(struct gl_shader **out)
+{
+       struct gl_shader *shader;
+       int ret;
+
+       if (!out)
+               return -EINVAL;
+
+       shader = malloc(sizeof(*shader));
+       if (!shader)
+               return -ENOMEM;
+       memset(shader, 0, sizeof(*shader));
+       shader->ref = 1;
+
+       ret = init_def_shader(shader);
+       if (ret)
+               goto err_free;
+
+       ret = init_tex_shader(shader);
+       if (ret)
+               goto err_def;
+
+       glEnable(GL_BLEND);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+       *out = shader;
+       return 0;
+
+err_def:
+       free_def_shader(shader);
+err_free:
+       free(shader);
+       return ret;
+}
+
+void gl_shader_ref(struct gl_shader *shader)
+{
+       if (!shader || !shader->ref)
+               return;
+
+       ++shader->ref;
+}
+
+void gl_shader_unref(struct gl_shader *shader)
+{
+       if (!shader || !shader->ref || --shader->ref)
+               return;
+
+       free_tex_shader(shader);
+       free_def_shader(shader);
+       free(shader);
+}
+
+void gl_shader_draw_def(struct gl_shader *shader, float *vertices,
+                       float *colors, size_t num)
+{
+       float m[16] = { 1, 0, 0, 0,
+                       0, 1, 0, 0,
+                       0, 0, 1, 0,
+                       0, 0, 0, 1 };
+
+       if (!shader || !vertices || !colors || !num)
+               return;
+
+       glUseProgram(shader->def_program);
+       glUniformMatrix4fv(shader->def_uni_projection, 1, GL_FALSE, m);
+
+       glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
+       glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, colors);
+       glEnableVertexAttribArray(0);
+       glEnableVertexAttribArray(1);
+       glDrawArrays(GL_TRIANGLES, 0, num);
+}
+
+void gl_shader_draw_tex(struct gl_shader *shader, const float *vertices,
+                       const float *texcoords, size_t num,
+                       unsigned int tex, const float *m)
+{
+       float mat[16];
+
+       if (!shader || !vertices || !texcoords || !num || !m)
+               return;
+
+       gl_m4_transpose_dest(mat, m);
+       glActiveTexture(GL_TEXTURE0);
+       glBindTexture(GL_TEXTURE_2D, tex);
+
+       glUseProgram(shader->tex_program);
+       glUniformMatrix4fv(shader->tex_uni_projection, 1, GL_FALSE, mat);
+       glUniform1i(shader->tex_uni_texture, 0);
+
+       glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
+       glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texcoords);
+       glEnableVertexAttribArray(0);
+       glEnableVertexAttribArray(1);
+       glDrawArrays(GL_TRIANGLES, 0, num);
+}