From 2d71ef276c3084760b72e2ff377710a92346d4b6 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Tue, 17 Jan 2012 15:19:43 +0100 Subject: output: add shader support To avoid the fixed function pipeline we should use shaders instead of old glBegin/glEnd. This patch adds two basic shaders and functions that load and initialize them. To avoid loading the shaders at runtime we generate a source file which contains them as strings so they are embedded in the binary. Signed-off-by: David Herrmann --- .gitignore | 2 + Makefile.am | 17 +++- src/genshader.c | 141 +++++++++++++++++++++++++++++++ src/output_context.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++ src/output_shader.frag | 38 +++++++++ src/output_shader.vert | 42 ++++++++++ 6 files changed, 457 insertions(+), 2 deletions(-) create mode 100644 src/genshader.c create mode 100644 src/output_shader.frag create mode 100644 src/output_shader.vert diff --git a/.gitignore b/.gitignore index 8febbb8..d946916 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ stamp-* .deps .dirstamp .libs +genshader +src/output_shaders.c diff --git a/Makefile.am b/Makefile.am index 7b6b9b0..a7538ef 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,8 +1,11 @@ ACLOCAL_AMFLAGS = -I m4 +EXTRA_DIST = README TODO COPYING +CLEANFILES = bin_PROGRAMS = kmscon check_PROGRAMS = test_console test_output test_vt test_buffer test_terminal \ test_input +noinst_PROGRAMS = genshader noinst_LTLIBRARIES = libkmscon-core.la AM_CFLAGS = \ @@ -19,6 +22,18 @@ else AM_CFLAGS += -O2 endif +EXTRA_DIST += src/output_shader.vert src/output_shader.frag +CLEANFILES += src/output_shaders.c + +genshader_SOURCES = \ + src/genshader.c + +src/output_shaders.c: src/output_shader.vert src/output_shader.frag genshader$(EXEEXT) + ./genshader$(EXEEXT) src/output_shaders.c src/output_shader.vert src/output_shader.frag + +nodist_libkmscon_core_la_SOURCES = \ + src/output_shaders.c + libkmscon_core_la_SOURCES = \ src/console.c src/console.h \ src/output.c src/output.h \ @@ -99,5 +114,3 @@ test_input_LDADD = \ test_input_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(XKBCOMMON_CFLAGS) - -EXTRA_DIST = README TODO COPYING diff --git a/src/genshader.c b/src/genshader.c new file mode 100644 index 0000000..25c7bf7 --- /dev/null +++ b/src/genshader.c @@ -0,0 +1,141 @@ +/* + * kmscon - Generate Shader Files + * + * Copyright (c) 2011 David Herrmann + * 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 Generator + * This takes as arguments two shaders and creates a C-source file which + * contains these shaders as constants. + */ + +#include +#include + +static char *read_file(const char *path, size_t *size) +{ + FILE *ffile; + ssize_t len; + char *buf; + + ffile = fopen(path, "rb"); + if (!ffile) { + fprintf(stderr, "genshader: cannot open %s: %m\n", path); + abort(); + } + + if (fseek(ffile, 0, SEEK_END) != 0) { + fprintf(stderr, "genshader: cannot seek %s: %m\n", path); + abort(); + } + + len = ftell(ffile); + if (len < 0) { + fprintf(stderr, "genshader: cannot tell %s: %m\n", path); + abort(); + } + + if (len < 1) { + fprintf(stderr, "genshader: empty file %s\n", path); + abort(); + } + + rewind(ffile); + + buf = malloc(len + 1); + if (!buf) { + fprintf(stderr, "genshader: memory allocation failed\n"); + abort(); + } + + if (len != fread(buf, 1, len, ffile)) { + fprintf(stderr, "genshader: cannot read %s: %m\n", path); + abort(); + } + + buf[len] = 0; + *size = len; + fclose(ffile); + + return buf; +} + +static void write_seq(FILE *out, const char *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) { + if (src[i] == '\n') { + fwrite("\\n\"\n\"", 5, 1, out); + } else if (src[i] == '"') { + fwrite("\\\"", 2, 1, out); + } else { + fwrite(&src[i], 1, 1, out); + } + } +} + +static void write_file(const char *path, const char *vs, size_t l1, + const char *fs, size_t l2) +{ + FILE *out; + static const char c1[] = "/* This file is generated by genshader.c */\n" + "const char *kmscon_vert_shader = \""; + static const char c2[] = "\";\nconst char *kmscon_frag_shader = \""; + static const char c3[] = "\";"; + + out = fopen(path, "wb"); + if (!out) { + fprintf(stderr, "genshader: cannot open %s: %m\n", path); + abort(); + } + + fwrite(c1, sizeof(c1) - 1, 1, out); + write_seq(out, vs, l1); + fwrite(c2, sizeof(c2) - 1, 1, out); + write_seq(out, fs, l2); + fwrite(c3, sizeof(c3) - 1, 1, out); + + fclose(out); +} + +int main(int argc, char *argv[]) +{ + char *vert, *frag; + size_t vs, fs; + + if (argc < 4) { + fprintf(stderr, "genshader: missing parameters\n"); + return EXIT_FAILURE; + } + + vert = read_file(argv[2], &vs); + frag = read_file(argv[3], &fs); + + write_file(argv[1], vert, vs, frag, fs); + free(vert); + free(frag); + + return EXIT_SUCCESS; +} diff --git a/src/output_context.c b/src/output_context.c index 8815083..ded82fd 100644 --- a/src/output_context.c +++ b/src/output_context.c @@ -43,20 +43,86 @@ #include "log.h" #include "output.h" +/* OpenGL extension definitions */ +typedef void (*PFNGLGENRENDERBUFFERSPROC) + (GLsizei n, GLuint *renderbuffers); +typedef void (*PFNGLBINDRENDERBUFFERPROC) + (GLenum target, GLuint renderbuffer); +typedef void (*PFNGLDELETERENDERBUFFERSPROC) + (GLsizei n, const GLuint *renderbuffers); + +typedef void (*PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, + GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef GLenum (*PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); +typedef void (*PFNGLGENFRAMEBUFFERSPROC) + (GLsizei n, GLuint *framebuffers); +typedef void (*PFNGLBINDFRAMEBUFFERPROC) + (GLenum target, GLuint framebuffer); +typedef void (*PFNGLDELETEFRAMEBUFFERSPROC) + (GLsizei n, const GLuint *framebuffers); + +typedef GLuint (*PFNGLCREATESHADERPROC) (GLenum type); +typedef void (*PFNGLDELETESHADERPROC) (GLuint shader); +typedef void (*PFNGLGETSHADERSOURCEPROC) + (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +typedef void (*PFNGLCOMPILESHADERPROC) (GLuint shader); +typedef void (*PFNGLGETSHADERIVPROC) + (GLuint shader, GLenum pname, GLint *params); +typedef void (*PFNGLGETSHADERINFOLOGPROC) + (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + +typedef GLuint (*PFNGLCREATEPROGRAMPROC) (void); +typedef void (*PFNGLDELETEPROGRAMPROC) (GLuint program); +typedef void (*PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (*PFNGLBINDATTRIBLOCATIONPROC) + (GLuint program, GLuint index, const GLchar *name); +typedef void (*PFNGLLINKPROGRAMPROC) (GLuint program); +typedef void (*PFNGLGETPROGRAMIVPROC) + (GLuint program, GLenum pname, GLint *params); +typedef void (*PFNGLGETPROGRAMINFOLOGPROC) + (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef GLint (*PFNGLGETUNIFORMLOCATIONPROC) + (GLuint program, const GLchar *name); + struct kmscon_context { EGLDisplay display; EGLContext context; + + GLuint program; + GLuint vshader; + GLuint fshader; + GLuint uni_projection; + GLuint uni_texture; + PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC proc_rbuf_storage; PFNEGLCREATEIMAGEKHRPROC proc_create_image; PFNEGLDESTROYIMAGEKHRPROC proc_destroy_image; + PFNGLGENRENDERBUFFERSPROC proc_gen_renderbuffers; PFNGLBINDRENDERBUFFERPROC proc_bind_renderbuffer; PFNGLDELETERENDERBUFFERSPROC proc_delete_renderbuffers; + PFNGLFRAMEBUFFERRENDERBUFFERPROC proc_framebuffer_renderbuffer; PFNGLCHECKFRAMEBUFFERSTATUSPROC proc_check_framebuffer_status; PFNGLGENFRAMEBUFFERSPROC proc_gen_framebuffers; PFNGLBINDFRAMEBUFFERPROC proc_bind_framebuffer; PFNGLDELETEFRAMEBUFFERSPROC proc_delete_framebuffers; + + PFNGLCREATESHADERPROC proc_create_shader; + PFNGLDELETESHADERPROC proc_delete_shader; + PFNGLSHADERSOURCEPROC proc_shader_source; + PFNGLCOMPILESHADERPROC proc_compile_shader; + PFNGLGETSHADERIVPROC proc_get_shader_iv; + PFNGLGETSHADERINFOLOGPROC proc_get_shader_info_log; + + PFNGLCREATEPROGRAMPROC proc_create_program; + PFNGLDELETEPROGRAMPROC proc_delete_program; + PFNGLATTACHSHADERPROC proc_attach_shader; + PFNGLBINDATTRIBLOCATIONPROC proc_bind_attrib_location; + PFNGLLINKPROGRAMPROC proc_link_program; + PFNGLGETPROGRAMIVPROC proc_get_program_iv; + PFNGLGETPROGRAMINFOLOGPROC proc_get_program_info_log; + PFNGLGETUNIFORMLOCATIONPROC proc_get_uniform_location; }; struct renderbuffer { @@ -93,6 +159,95 @@ static bool has_gl_error() return glGetError() != GL_NO_ERROR; } +/* external shader sources; generated during build */ +extern const char *kmscon_vert_shader; +extern const char *kmscon_frag_shader; + +static int compile_shader(struct kmscon_context *ctx, GLenum type, + const char *source) +{ + char msg[512]; + GLint status = 1; + GLuint s; + + s = ctx->proc_create_shader(type); + ctx->proc_shader_source(s, 1, &source, NULL); + ctx->proc_compile_shader(s); + + ctx->proc_get_shader_iv(s, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) { + msg[0] = 0; + ctx->proc_get_shader_info_log(s, sizeof(msg), NULL, msg); + log_warning("context: cannot compile shader: %s\n", msg); + return GL_NONE; + } + + return s; +} + +static int init_shader(struct kmscon_context *ctx) +{ + char msg[512]; + GLint status = 1; + int ret; + + if (!ctx) + return -EINVAL; + + ctx->vshader = compile_shader(ctx, GL_VERTEX_SHADER, + kmscon_vert_shader); + if (ctx->vshader == GL_NONE) + return -EFAULT; + + ctx->fshader = compile_shader(ctx, GL_FRAGMENT_SHADER, + kmscon_frag_shader); + if (ctx->fshader == GL_NONE) { + ret = -EFAULT; + goto err_vshader; + } + + ctx->program = ctx->proc_create_program(); + ctx->proc_attach_shader(ctx->program, ctx->vshader); + ctx->proc_attach_shader(ctx->program, ctx->fshader); + ctx->proc_bind_attrib_location(ctx->program, 0, "position"); + ctx->proc_bind_attrib_location(ctx->program, 1, "texture_position"); + + ctx->proc_link_program(ctx->program); + ctx->proc_get_program_iv(ctx->program, GL_LINK_STATUS, &status); + if (status == GL_FALSE) { + msg[0] = 0; + ctx->proc_get_program_info_log(ctx->program, sizeof(msg), + NULL, msg); + log_warning("context: cannot link shader: %s\n", msg); + ret = -EFAULT; + goto err_link; + } + + ctx->uni_projection = + ctx->proc_get_uniform_location(ctx->program, "projection"); + ctx->uni_texture = + ctx->proc_get_uniform_location(ctx->program, "texture"); + + return 0; + +err_link: + ctx->proc_delete_program(ctx->program); + ctx->proc_delete_shader(ctx->fshader); +err_vshader: + ctx->proc_delete_shader(ctx->vshader); + return ret; +} + +static void destroy_shader(struct kmscon_context *ctx) +{ + if (!ctx) + return; + + ctx->proc_delete_program(ctx->program); + ctx->proc_delete_shader(ctx->fshader); + ctx->proc_delete_shader(ctx->vshader); +} + /* * Create the GL context * This uses the EGL library for context creation and needs a valid gbm device @@ -141,6 +296,36 @@ int kmscon_context_new(struct kmscon_context **out, void *gbm) ctx->proc_delete_framebuffers = (void*) eglGetProcAddress("glDeleteFramebuffers"); + ctx->proc_create_shader = + (void*) eglGetProcAddress("glCreateShader"); + ctx->proc_delete_shader = + (void*) eglGetProcAddress("glDeleteShader"); + ctx->proc_shader_source = + (void*) eglGetProcAddress("glShaderSource"); + ctx->proc_compile_shader = + (void*) eglGetProcAddress("glCompileShader"); + ctx->proc_get_shader_iv = + (void*) eglGetProcAddress("glGetShaderiv"); + ctx->proc_get_shader_info_log = + (void*) eglGetProcAddress("glGetShaderInfoLog"); + + ctx->proc_create_program = + (void*) eglGetProcAddress("glCreateProgram"); + ctx->proc_delete_program = + (void*) eglGetProcAddress("glDeleteProgram"); + ctx->proc_attach_shader = + (void*) eglGetProcAddress("glAttachShader"); + ctx->proc_bind_attrib_location = + (void*) eglGetProcAddress("glBindAttribLocation"); + ctx->proc_link_program = + (void*) eglGetProcAddress("glLinkProgram"); + ctx->proc_get_program_iv = + (void*) eglGetProcAddress("glGetProgramiv"); + ctx->proc_get_program_info_log = + (void*) eglGetProcAddress("glGetProgramInfoLog"); + ctx->proc_get_uniform_location = + (void*) eglGetProcAddress("glGetUniformLocation"); + if (!ctx->proc_rbuf_storage || !ctx->proc_create_image || !ctx->proc_destroy_image) { log_warning("context: KHR images not supported\n"); @@ -154,6 +339,26 @@ int kmscon_context_new(struct kmscon_context **out, void *gbm) log_warning("context: renderbuffers not supported\n"); ret = -ENOTSUP; goto err_free; + } else if (!ctx->proc_create_shader || + !ctx->proc_delete_shader || + !ctx->proc_shader_source || + !ctx->proc_compile_shader || + !ctx->proc_get_shader_iv || + !ctx->proc_get_shader_info_log) { + log_warning("context: shaders not supported\n"); + ret = -ENOTSUP; + goto err_free; + } else if (!ctx->proc_create_program || + !ctx->proc_delete_program || + !ctx->proc_attach_shader || + !ctx->proc_bind_attrib_location || + !ctx->proc_link_program || + !ctx->proc_get_program_iv || + !ctx->proc_get_program_info_log || + !ctx->proc_get_uniform_location) { + log_warning("context: shaders not supported\n"); + ret = -ENOTSUP; + goto err_free; } ctx->display = eglGetDisplay((EGLNativeDisplayType) gbm); @@ -191,9 +396,22 @@ int kmscon_context_new(struct kmscon_context **out, void *gbm) goto err_display; } + if (!eglMakeCurrent(ctx->display, EGL_NO_SURFACE, EGL_NO_SURFACE, + ctx->context)) { + log_warning("context: cannot use EGL context\n"); + ret = -EFAULT; + goto err_ctx; + } + + ret = init_shader(ctx); + if (ret) + goto err_ctx; + *out = ctx; return 0; +err_ctx: + eglDestroyContext(ctx->display, ctx->context); err_display: eglTerminate(ctx->display); err_free: @@ -206,6 +424,7 @@ void kmscon_context_destroy(struct kmscon_context *ctx) if (!ctx) return; + destroy_shader(ctx); eglDestroyContext(ctx->display, ctx->context); eglTerminate(ctx->display); free(ctx); diff --git a/src/output_shader.frag b/src/output_shader.frag new file mode 100644 index 0000000..38be69f --- /dev/null +++ b/src/output_shader.frag @@ -0,0 +1,38 @@ +/* + * kmscon - Fragment Shader + * + * Copyright (c) 2011 David Herrmann + * 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. + */ + +/* + * Fragement Shader + * A basic fragment shader which applies a 2D texture. + */ + +uniform sampler2D texture; +varying vec2 texpos; + +void main() +{ + gl_FragColor = texture2D(texture, texpos); +} diff --git a/src/output_shader.vert b/src/output_shader.vert new file mode 100644 index 0000000..2ca0129 --- /dev/null +++ b/src/output_shader.vert @@ -0,0 +1,42 @@ +/* + * kmscon - Vertex Shader + * + * Copyright (c) 2011 David Herrmann + * 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. + */ + +/* + * Vertex Shader + * This shader is a very basic vertex shader which forwards all data and + * performs basic matrix multiplications. + */ + +uniform mat4 projection; +attribute vec2 position; +attribute vec2 texture_position; +varying vec2 texpos; + +void main() +{ + gl_Position = projection * vec4(position, 0.0, 1.0); + texpos = texture_position; +} -- cgit v1.2.3