From df84e9f2173f2c3715d4f3b02a8a52189d992423 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Fri, 10 Aug 2012 11:43:07 +0200 Subject: [PATCH] text: gltex: add OpenGL text renderer This adds an OpenGL text renderer. It uses textures to store glyph information and renders the characters by assembling a vertex-list first. To improve performance, we use texture-atlases. Signed-off-by: David Herrmann --- Makefile.am | 9 +- configure.ac | 12 + src/main.c | 3 + src/static_gltex.frag | 47 ++++ src/static_gltex.vert | 50 ++++ src/text.h | 18 ++ src/text_gltex.c | 627 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 765 insertions(+), 1 deletion(-) create mode 100644 src/static_gltex.frag create mode 100644 src/static_gltex.vert create mode 100644 src/text_gltex.c diff --git a/Makefile.am b/Makefile.am index cae5d08..c29a8d0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -88,7 +88,9 @@ SHADERS = \ src/static_blend.vert \ src/static_blend.frag \ src/static_blit.vert \ - src/static_blit.frag + src/static_blit.frag \ + src/static_gltex.vert \ + src/static_gltex.frag EXTRA_DIST += \ $(SHADERS) @@ -142,6 +144,11 @@ libkmscon_core_la_SOURCES += \ src/text_bblit.c endif +if KMSCON_HAVE_GLES2 +libkmscon_core_la_SOURCES += \ + src/text_gltex.c +endif + libkmscon_core_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(GLES2_CFLAGS) \ diff --git a/configure.ac b/configure.ac index 150dae4..8db1885 100644 --- a/configure.ac +++ b/configure.ac @@ -459,6 +459,17 @@ fi AM_CONDITIONAL([KMSCON_HAVE_BBLIT], [test x$bblit_enabled = xyes]) +# +# OpenGL Texture rendering backend +# This is not really an option but automatically enabled if OpenGLES2 support +# was selected. +# + +gltex_enabled=no +if test x$gles2_enabled = xyes ; then + gltex_enabled=yes +fi + # # Makefile vars # After everything is configured, we correctly substitute the values for the @@ -518,5 +529,6 @@ AC_MSG_NOTICE([Build configuration: rendering backends: bblit: $bblit_enabled + gltex: $gltex_enabled Run "make" to start compilation process]) diff --git a/src/main.c b/src/main.c index 6a1dbc2..66cb37d 100644 --- a/src/main.c +++ b/src/main.c @@ -338,6 +338,7 @@ int main(int argc, char **argv) kmscon_font_pango_load(); kmscon_font_freetype2_load(); kmscon_text_bblit_load(); + kmscon_text_gltex_load(); memset(&app, 0, sizeof(app)); ret = setup_app(&app); @@ -367,6 +368,7 @@ int main(int argc, char **argv) } destroy_app(&app); + kmscon_text_gltex_unload(); kmscon_text_bblit_unload(); kmscon_font_freetype2_unload(); kmscon_font_pango_unload(); @@ -377,6 +379,7 @@ int main(int argc, char **argv) return EXIT_SUCCESS; err_unload: + kmscon_text_gltex_unload(); kmscon_text_bblit_unload(); kmscon_font_freetype2_unload(); kmscon_font_pango_unload(); diff --git a/src/static_gltex.frag b/src/static_gltex.frag new file mode 100644 index 0000000..3346273 --- /dev/null +++ b/src/static_gltex.frag @@ -0,0 +1,47 @@ +/* + * kmscon - Fragment Shader + * + * Copyright (c) 2011-2012 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 and blends foreground and + * background colors. + */ + +uniform sampler2D atlas; +uniform float advance_htex; +uniform float advance_vtex; + +varying vec2 texpos; +varying vec3 fgcol; +varying vec3 bgcol; + +void main() +{ + vec2 pos = vec2(texpos.x * advance_htex, texpos.y * advance_vtex); + float alpha = texture2D(atlas, pos).a; + vec3 val = alpha * fgcol + (1.0 - alpha) * bgcol; + gl_FragColor = vec4(val, 1.0); +} diff --git a/src/static_gltex.vert b/src/static_gltex.vert new file mode 100644 index 0000000..e99e1b4 --- /dev/null +++ b/src/static_gltex.vert @@ -0,0 +1,50 @@ +/* + * kmscon - Vertex Shader + * + * Copyright (c) 2011-2012 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; +attribute vec3 fgcolor; +attribute vec3 bgcolor; + +varying vec2 texpos; +varying vec3 fgcol; +varying vec3 bgcol; + +void main() +{ + gl_Position = projection * vec4(position, 0.0, 1.0); + texpos = texture_position; + fgcol = fgcolor; + bgcol = bgcolor; +} diff --git a/src/text.h b/src/text.h index 0b612ae..1e12c8b 100644 --- a/src/text.h +++ b/src/text.h @@ -251,4 +251,22 @@ static inline void kmscon_text_bblit_unload(void) #endif +#ifdef KMSCON_HAVE_GLES2 + +int kmscon_text_gltex_load(void); +void kmscon_text_gltex_unload(void); + +#else + +static inline int kmscon_text_gltex_load(void) +{ + return -EOPNOTSUPP; +} + +static inline void kmscon_text_gltex_unload(void) +{ +} + +#endif + #endif /* KMSCON_TEXT_H */ diff --git a/src/text_gltex.c b/src/text_gltex.c new file mode 100644 index 0000000..f56a96e --- /dev/null +++ b/src/text_gltex.c @@ -0,0 +1,627 @@ +/* + * kmscon - OpenGL Textures Text Renderer Backend + * + * Copyright (c) 2012 David Herrmann + * + * 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. + */ + +/** + * SECTION:text_gltex.c + * @short_description: OpenGL Textures Text Renderer Backend + * @include: text.h + * + * Uses OpenGL textures to store glyph information and draws these textures with + * a custom fragment shader. + * Glyphs are stored in texture-atlases. OpenGL has heavy restrictions on + * texture sizes so we need to use multiple atlases. As there is no way to pass + * a varying amount of textures to a shader, we need to render the screen for + * each atlas we have. + */ + +#define GL_GLEXT_PROTOTYPES + +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "static_gl.h" +#include "static_misc.h" +#include "text.h" +#include "unicode.h" +#include "uterm.h" + +#define LOG_SUBSYSTEM "text_gltex" + +struct atlas { + struct kmscon_dlist list; + + GLuint tex; + unsigned int height; + unsigned int width; + unsigned int count; + unsigned int fill; + + unsigned int cache_size; + unsigned int cache_num; + GLfloat *cache_pos; + GLfloat *cache_texpos; + GLfloat *cache_fgcol; + GLfloat *cache_bgcol; + + GLfloat advance_htex; + GLfloat advance_vtex; +}; + +struct glyph { + const struct kmscon_glyph *glyph; + struct atlas *atlas; + unsigned int texoff; +}; + +#define GLYPH_WIDTH(gly) ((gly)->glyph->buf.width) +#define GLYPH_HEIGHT(gly) ((gly)->glyph->buf.height) +#define GLYPH_STRIDE(gly) ((gly)->glyph->buf.stride) +#define GLYPH_DATA(gly) ((gly)->glyph->buf.data) + +struct gltex { + struct kmscon_hashtable *glyphs; + unsigned int max_tex_size; + + struct kmscon_dlist atlases; + + GLfloat advance_x; + GLfloat advance_y; + + struct gl_shader *shader; + GLuint uni_proj; + GLuint uni_atlas; + GLuint uni_advance_htex; + GLuint uni_advance_vtex; +}; + +#define FONT_WIDTH(txt) ((txt)->font->attr.width) +#define FONT_HEIGHT(txt) ((txt)->font->attr.height) + +#define SCREEN_WIDTH(txt) uterm_screen_width((txt)->screen) +#define SCREEN_HEIGHT(txt) uterm_screen_height((txt)->screen) + +static int gltex_init(struct kmscon_text *txt) +{ + struct gltex *gt; + + gt = malloc(sizeof(*gt)); + if (!gt) + return -ENOMEM; + + txt->data = gt; + return 0; +} + +static void gltex_destroy(struct kmscon_text *txt) +{ + struct gltex *gt = txt->data; + + free(gt); +} + +static void free_glyph(void *data) +{ + struct glyph *glyph = data; + + free(glyph); +} + +extern const char *gl_static_gltex_vert; +extern const char *gl_static_gltex_frag; + +static int gltex_set(struct kmscon_text *txt) +{ + struct gltex *gt = txt->data; + int ret; + static char *attr[] = { "position", "texture_position", + "fgcolor", "bgcolor" }; + unsigned int sw, sh; + GLint s; + + memset(gt, 0, sizeof(*gt)); + kmscon_dlist_init(>->atlases); + + ret = kmscon_hashtable_new(>->glyphs, kmscon_direct_hash, + kmscon_direct_equal, NULL, + free_glyph); + if (ret) + return ret; + + ret = uterm_screen_use(txt->screen); + if (ret) + goto err_htable; + + gl_clear_error(); + + ret = gl_shader_new(>->shader, gl_static_gltex_vert, + gl_static_gltex_frag, attr, 4, log_llog); + if (ret) + goto err_htable; + + gt->uni_proj = gl_shader_get_uniform(gt->shader, "projection"); + gt->uni_atlas = gl_shader_get_uniform(gt->shader, "atlas"); + gt->uni_advance_htex = gl_shader_get_uniform(gt->shader, + "advance_htex"); + gt->uni_advance_vtex = gl_shader_get_uniform(gt->shader, + "advance_vtex"); + + if (gl_has_error(gt->shader)) { + log_warning("cannot create shader"); + goto err_shader; + } + + sw = SCREEN_WIDTH(txt); + sh = SCREEN_HEIGHT(txt); + + txt->cols = sw / FONT_WIDTH(txt); + txt->rows = sh / FONT_HEIGHT(txt); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &s); + if (s <= 0) + s = 64; + else if (s > 2048) + s = 2048; + gt->max_tex_size = s; + + gl_clear_error(); + + return 0; + +err_shader: + gl_shader_unref(gt->shader); +err_htable: + kmscon_hashtable_free(gt->glyphs); + return ret; +} + +static void gltex_unset(struct kmscon_text *txt) +{ + struct gltex *gt = txt->data; + int ret; + struct kmscon_dlist *iter; + struct atlas *atlas; + bool gl = true; + + ret = uterm_screen_use(txt->screen); + if (ret) { + gl = false; + log_warning("cannot activate OpenGL-CTX during destruction"); + } + + kmscon_hashtable_free(gt->glyphs); + + while (!kmscon_dlist_empty(>->atlases)) { + iter = gt->atlases.next; + kmscon_dlist_unlink(iter); + atlas = kmscon_dlist_entry(iter, struct atlas, list); + + free(atlas->cache_pos); + free(atlas->cache_texpos); + free(atlas->cache_fgcol); + free(atlas->cache_bgcol); + + if (gl) + gl_tex_free(&atlas->tex, 1); + } + + if (gl) { + gl_shader_unref(gt->shader); + + gl_clear_error(); + } +} + +static unsigned int next_pow2(unsigned int num) +{ + int i; + + if (!num) + return num; + + --num; + for (i = 1; i < sizeof(unsigned int) * CHAR_BIT; i <<= 1) + num = num | num >> i; + + return num + 1; +} + +/* returns an atlas with at least 1 free glyph position; NULL on error */ +static struct atlas *get_atlas(struct kmscon_text *txt) +{ + struct gltex *gt = txt->data; + struct atlas *atlas; + size_t newsize; + unsigned int width, height, nsize; + GLenum err; + + /* check whether the last added atlas has still room for one glyph */ + if (!kmscon_dlist_empty(>->atlases)) { + atlas = kmscon_dlist_entry(gt->atlases.next, struct atlas, + list); + if (atlas->fill < atlas->count) + return atlas; + } + + /* all atlases are full so we have to create a new atlas */ + atlas = malloc(sizeof(*atlas)); + if (!atlas) + return NULL; + memset(atlas, 0, sizeof(*atlas)); + + gl_clear_error(); + + gl_tex_new(&atlas->tex, 1); + err = glGetError(); + if (err != GL_NO_ERROR || !atlas->tex) { + gl_clear_error(); + log_warning("cannot create new OpenGL texture: %d", err); + goto err_free; + } + + newsize = gt->max_tex_size / FONT_WIDTH(txt); + if (newsize < 1) + newsize = 1; + + /* OpenGL texture sizes are heavily restricted so we need to find a + * valid texture size that is big enough to hold as many glyphs as + * possible but at least 1 */ +try_next: + width = next_pow2(FONT_WIDTH(txt) * newsize); + height = next_pow2(FONT_HEIGHT(txt)); + + gl_clear_error(); + + glBindTexture(GL_TEXTURE_2D, atlas->tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, + 0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL); + + err = glGetError(); + if (err != GL_NO_ERROR) { + if (newsize > 1) { + --newsize; + goto try_next; + } + gl_clear_error(); + log_warning("OpenGL textures too small for a single glyph (%d)", + err); + goto err_tex; + } + + log_debug("new atlas of size %ux%u for %u", width, height, newsize); + + nsize = txt->cols * txt->rows; + + atlas->cache_pos = malloc(sizeof(GLfloat) * nsize * 2 * 6); + if (!atlas->cache_pos) + goto err_mem; + + atlas->cache_texpos = malloc(sizeof(GLfloat) * nsize * 2 * 6); + if (!atlas->cache_texpos) + goto err_mem; + + atlas->cache_fgcol = malloc(sizeof(GLfloat) * nsize * 3 * 6); + if (!atlas->cache_fgcol) + goto err_mem; + + atlas->cache_bgcol = malloc(sizeof(GLfloat) * nsize * 3 * 6); + if (!atlas->cache_bgcol) + goto err_mem; + + atlas->cache_size = nsize; + atlas->count = newsize; + atlas->width = width; + atlas->height = height; + atlas->advance_htex = 1.0 / atlas->width * FONT_WIDTH(txt); + atlas->advance_vtex = 1.0 / atlas->height * FONT_HEIGHT(txt); + + kmscon_dlist_link(>->atlases, &atlas->list); + return atlas; + +err_mem: + free(atlas->cache_pos); + free(atlas->cache_texpos); + free(atlas->cache_fgcol); + free(atlas->cache_bgcol); +err_tex: + gl_tex_free(&atlas->tex, 1); +err_free: + free(atlas); + return NULL; +} + +static int find_glyph(struct kmscon_text *txt, struct glyph **out, + kmscon_symbol_t ch) +{ + struct gltex *gt = txt->data; + struct atlas *atlas; + struct glyph *glyph; + bool res; + int ret; + GLenum err; + + res = kmscon_hashtable_find(gt->glyphs, (void**)&glyph, + (void*)(unsigned long)ch); + if (res) { + *out = glyph; + return 0; + } + + atlas = get_atlas(txt); + if (!atlas) + return -EFAULT; + + glyph = malloc(sizeof(*glyph)); + if (!glyph) + return -ENOMEM; + memset(glyph, 0, sizeof(*glyph)); + + if (ch == 0 || ch == ' ') { + ret = kmscon_font_render_empty(txt->font, &glyph->glyph); + } else { + ret = kmscon_font_render(txt->font, ch, &glyph->glyph); + } + + if (ret) { + ret = kmscon_font_render_inval(txt->font, &glyph->glyph); + if (ret) + goto err_free; + } + + gl_clear_error(); + + glBindTexture(GL_TEXTURE_2D, atlas->tex); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, GLYPH_STRIDE(glyph)); + glTexSubImage2D(GL_TEXTURE_2D, 0, + FONT_WIDTH(txt) * atlas->fill, 0, + GLYPH_WIDTH(glyph), + GLYPH_HEIGHT(glyph), + GL_ALPHA, GL_UNSIGNED_BYTE, + GLYPH_DATA(glyph)); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + err = glGetError(); + if (err != GL_NO_ERROR) { + gl_clear_error(); + log_warning("cannot load glyph data into OpenGL texture (%d)", + err); + ret = -EFAULT; + goto err_free; + } + + glyph->atlas = atlas; + glyph->texoff = atlas->fill; + + ret = kmscon_hashtable_insert(gt->glyphs, (void*)(long)ch, glyph); + if (ret) + goto err_free; + + ++atlas->fill; + + *out = glyph; + return 0; + +err_free: + free(glyph); + return ret; +} + +static int gltex_prepare(struct kmscon_text *txt) +{ + struct gltex *gt = txt->data; + struct atlas *atlas; + struct kmscon_dlist *iter; + unsigned int sw, sh; + int ret; + + ret = uterm_screen_use(txt->screen); + if (ret) + return ret; + + kmscon_dlist_for_each(iter, >->atlases) { + atlas = kmscon_dlist_entry(iter, struct atlas, list); + + atlas->cache_num = 0; + } + + sw = SCREEN_WIDTH(txt); + sh = SCREEN_HEIGHT(txt); + + gt->advance_x = 2.0 / sw * FONT_WIDTH(txt); + gt->advance_y = 2.0 / sh * FONT_HEIGHT(txt); + + return 0; +} + +static int gltex_draw(struct kmscon_text *txt, kmscon_symbol_t ch, + unsigned int posx, unsigned int posy, + const struct font_char_attr *attr) +{ + struct gltex *gt = txt->data; + struct atlas *atlas; + struct glyph *glyph; + int ret, i, idx; + + ret = find_glyph(txt, &glyph, ch); + if (ret) + return ret; + atlas = glyph->atlas; + + if (atlas->cache_num >= atlas->cache_size) + return -ERANGE; + + atlas->cache_pos[atlas->cache_num * 2 * 6 + 0] = + gt->advance_x * posx - 1; + atlas->cache_pos[atlas->cache_num * 2 * 6 + 1] = + gt->advance_y * posy - 1; + atlas->cache_pos[atlas->cache_num * 2 * 6 + 2] = + gt->advance_x * posx - 1; + atlas->cache_pos[atlas->cache_num * 2 * 6 + 3] = + gt->advance_y * posy + gt->advance_y - 1; + atlas->cache_pos[atlas->cache_num * 2 * 6 + 4] = + gt->advance_x * posx + gt->advance_x - 1; + atlas->cache_pos[atlas->cache_num * 2 * 6 + 5] = + gt->advance_y * posy + gt->advance_y - 1; + + atlas->cache_pos[atlas->cache_num * 2 * 6 + 6] = + gt->advance_x * posx - 1; + atlas->cache_pos[atlas->cache_num * 2 * 6 + 7] = + gt->advance_y * posy - 1; + atlas->cache_pos[atlas->cache_num * 2 * 6 + 8] = + gt->advance_x * posx + gt->advance_x - 1; + atlas->cache_pos[atlas->cache_num * 2 * 6 + 9] = + gt->advance_y * posy + gt->advance_y - 1; + atlas->cache_pos[atlas->cache_num * 2 * 6 + 10] = + gt->advance_x * posx + gt->advance_x - 1; + atlas->cache_pos[atlas->cache_num * 2 * 6 + 11] = + gt->advance_y * posy - 1; + + atlas->cache_texpos[atlas->cache_num * 2 * 6 + 0] = glyph->texoff; + atlas->cache_texpos[atlas->cache_num * 2 * 6 + 1] = 0.0; + atlas->cache_texpos[atlas->cache_num * 2 * 6 + 2] = glyph->texoff; + atlas->cache_texpos[atlas->cache_num * 2 * 6 + 3] = 1.0; + atlas->cache_texpos[atlas->cache_num * 2 * 6 + 4] = glyph->texoff + 1; + atlas->cache_texpos[atlas->cache_num * 2 * 6 + 5] = 1.0; + + atlas->cache_texpos[atlas->cache_num * 2 * 6 + 6] = glyph->texoff; + atlas->cache_texpos[atlas->cache_num * 2 * 6 + 7] = 0.0; + atlas->cache_texpos[atlas->cache_num * 2 * 6 + 8] = glyph->texoff + 1; + atlas->cache_texpos[atlas->cache_num * 2 * 6 + 9] = 1.0; + atlas->cache_texpos[atlas->cache_num * 2 * 6 + 10] = glyph->texoff + 1; + atlas->cache_texpos[atlas->cache_num * 2 * 6 + 11] = 0.0; + + for (i = 0; i < 6; ++i) { + idx = atlas->cache_num * 3 * 6 + i * 3; + if (attr->inverse) { + atlas->cache_fgcol[idx + 0] = attr->br / 255.0; + atlas->cache_fgcol[idx + 1] = attr->bg / 255.0; + atlas->cache_fgcol[idx + 2] = attr->bb / 255.0; + atlas->cache_bgcol[idx + 0] = attr->fr / 255.0; + atlas->cache_bgcol[idx + 1] = attr->fg / 255.0; + atlas->cache_bgcol[idx + 2] = attr->fb / 255.0; + } else { + atlas->cache_fgcol[idx + 0] = attr->fr / 255.0; + atlas->cache_fgcol[idx + 1] = attr->fg / 255.0; + atlas->cache_fgcol[idx + 2] = attr->fb / 255.0; + atlas->cache_bgcol[idx + 0] = attr->br / 255.0; + atlas->cache_bgcol[idx + 1] = attr->bg / 255.0; + atlas->cache_bgcol[idx + 2] = attr->bb / 255.0; + } + } + + ++atlas->cache_num; + + return 0; +} + +static int gltex_render(struct kmscon_text *txt) +{ + struct gltex *gt = txt->data; + struct atlas *atlas; + struct kmscon_dlist *iter; + float mat[16]; + + gl_clear_error(); + + gl_shader_use(gt->shader); + + glViewport(0, 0, SCREEN_WIDTH(txt), SCREEN_HEIGHT(txt)); + glDisable(GL_BLEND); + + gl_m4_identity(mat); + glUniformMatrix4fv(gt->uni_proj, 1, GL_FALSE, mat); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + + glActiveTexture(GL_TEXTURE0); + glUniform1i(gt->uni_atlas, 0); + + kmscon_dlist_for_each(iter, >->atlases) { + atlas = kmscon_dlist_entry(iter, struct atlas, list); + if (!atlas->cache_num) + continue; + + glBindTexture(GL_TEXTURE_2D, atlas->tex); + glUniform1f(gt->uni_advance_htex, atlas->advance_htex); + glUniform1f(gt->uni_advance_vtex, atlas->advance_vtex); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, atlas->cache_pos); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, atlas->cache_texpos); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, atlas->cache_fgcol); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, atlas->cache_bgcol); + glDrawArrays(GL_TRIANGLES, 0, 6 * atlas->cache_num); + } + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(3); + + if (gl_has_error(gt->shader)) { + log_warning("rendering console caused OpenGL errors"); + return -EFAULT; + } + + return 0; +} + +static const struct kmscon_text_ops kmscon_text_gltex_ops = { + .name = "gltex", + .init = gltex_init, + .destroy = gltex_destroy, + .set = gltex_set, + .unset = gltex_unset, + .prepare = gltex_prepare, + .draw = gltex_draw, + .render = gltex_render, + .abort = NULL, +}; + +int kmscon_text_gltex_load(void) +{ + int ret; + + ret = kmscon_text_register(&kmscon_text_gltex_ops); + if (ret) { + log_error("cannot register gltex renderer"); + return ret; + } + + return 0; +} + +void kmscon_text_gltex_unload(void) +{ + kmscon_text_unregister(kmscon_text_gltex_ops.name); +}