uvtd: vt: implement VT_GETMODE/SETMODE ioctl state-tracking
[platform/upstream/kmscon.git] / src / text_gltex.c
1 /*
2  * kmscon - OpenGL Textures Text Renderer Backend
3  *
4  * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 /**
27  * SECTION:text_gltex.c
28  * @short_description: OpenGL Textures Text Renderer Backend
29  * @include: text.h
30  *
31  * Uses OpenGL textures to store glyph information and draws these textures with
32  * a custom fragment shader.
33  * Glyphs are stored in texture-atlases. OpenGL has heavy restrictions on
34  * texture sizes so we need to use multiple atlases. As there is no way to pass
35  * a varying amount of textures to a shader, we need to render the screen for
36  * each atlas we have.
37  */
38
39 #define GL_GLEXT_PROTOTYPES
40
41 #include <errno.h>
42 #include <GLES2/gl2.h>
43 #include <GLES2/gl2ext.h>
44 #include <limits.h>
45 #include <stdbool.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include "shl_dlist.h"
49 #include "shl_hashtable.h"
50 #include "shl_log.h"
51 #include "shl_misc.h"
52 #include "static_gl.h"
53 #include "text.h"
54 #include "uterm_video.h"
55
56 #define LOG_SUBSYSTEM "text_gltex"
57
58 struct atlas {
59         struct shl_dlist list;
60
61         GLuint tex;
62         unsigned int height;
63         unsigned int width;
64         unsigned int count;
65         unsigned int fill;
66
67         unsigned int cache_size;
68         unsigned int cache_num;
69         GLfloat *cache_pos;
70         GLfloat *cache_texpos;
71         GLfloat *cache_fgcol;
72         GLfloat *cache_bgcol;
73
74         GLfloat advance_htex;
75         GLfloat advance_vtex;
76 };
77
78 struct glyph {
79         const struct kmscon_glyph *glyph;
80         struct atlas *atlas;
81         unsigned int texoff;
82 };
83
84 #define GLYPH_WIDTH(gly) ((gly)->glyph->buf.width)
85 #define GLYPH_HEIGHT(gly) ((gly)->glyph->buf.height)
86 #define GLYPH_STRIDE(gly) ((gly)->glyph->buf.stride)
87 #define GLYPH_DATA(gly) ((gly)->glyph->buf.data)
88
89 struct gltex {
90         struct shl_hashtable *glyphs;
91         struct shl_hashtable *bold_glyphs;
92         unsigned int max_tex_size;
93         bool supports_rowlen;
94
95         struct shl_dlist atlases;
96
97         GLfloat advance_x;
98         GLfloat advance_y;
99
100         struct gl_shader *shader;
101         GLuint uni_proj;
102         GLuint uni_atlas;
103         GLuint uni_advance_htex;
104         GLuint uni_advance_vtex;
105
106         unsigned int sw;
107         unsigned int sh;
108 };
109
110 #define FONT_WIDTH(txt) ((txt)->font->attr.width)
111 #define FONT_HEIGHT(txt) ((txt)->font->attr.height)
112
113 static int gltex_init(struct kmscon_text *txt)
114 {
115         struct gltex *gt;
116
117         gt = malloc(sizeof(*gt));
118         if (!gt)
119                 return -ENOMEM;
120
121         txt->data = gt;
122         return 0;
123 }
124
125 static void gltex_destroy(struct kmscon_text *txt)
126 {
127         struct gltex *gt = txt->data;
128
129         free(gt);
130 }
131
132 static void free_glyph(void *data)
133 {
134         struct glyph *glyph = data;
135
136         free(glyph);
137 }
138
139 extern const char *gl_static_gltex_vert;
140 extern const char *gl_static_gltex_frag;
141
142 static int gltex_set(struct kmscon_text *txt)
143 {
144         struct gltex *gt = txt->data;
145         int ret;
146         static char *attr[] = { "position", "texture_position",
147                                 "fgcolor", "bgcolor" };
148         GLint s;
149         const char *ext;
150         struct uterm_mode *mode;
151         bool opengl;
152
153         memset(gt, 0, sizeof(*gt));
154         shl_dlist_init(&gt->atlases);
155
156         ret = shl_hashtable_new(&gt->glyphs, shl_direct_hash,
157                                 shl_direct_equal, NULL,
158                                 free_glyph);
159         if (ret)
160                 return ret;
161
162         ret = shl_hashtable_new(&gt->bold_glyphs, shl_direct_hash,
163                                 shl_direct_equal, NULL,
164                                 free_glyph);
165         if (ret)
166                 goto err_htable;
167
168         ret = uterm_display_use(txt->disp, &opengl);
169         if (ret < 0 || !opengl) {
170                 if (ret == -EOPNOTSUPP)
171                         log_error("display doesn't support hardware-acceleration");
172                 goto err_bold_htable;
173         }
174
175         gl_clear_error();
176
177         ret = gl_shader_new(&gt->shader, gl_static_gltex_vert,
178                             gl_static_gltex_frag, attr, 4, log_llog, NULL);
179         if (ret)
180                 goto err_bold_htable;
181
182         gt->uni_proj = gl_shader_get_uniform(gt->shader, "projection");
183         gt->uni_atlas = gl_shader_get_uniform(gt->shader, "atlas");
184         gt->uni_advance_htex = gl_shader_get_uniform(gt->shader,
185                                                      "advance_htex");
186         gt->uni_advance_vtex = gl_shader_get_uniform(gt->shader,
187                                                      "advance_vtex");
188
189         if (gl_has_error(gt->shader)) {
190                 log_warning("cannot create shader");
191                 goto err_shader;
192         }
193
194         mode = uterm_display_get_current(txt->disp);
195         gt->sw = uterm_mode_get_width(mode);
196         gt->sh = uterm_mode_get_height(mode);
197
198         txt->cols = gt->sw / FONT_WIDTH(txt);
199         txt->rows = gt->sh / FONT_HEIGHT(txt);
200
201         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &s);
202         if (s <= 0)
203                 s = 64;
204         else if (s > 2048)
205                 s = 2048;
206         gt->max_tex_size = s;
207
208         gl_clear_error();
209
210         ext = (const char*)glGetString(GL_EXTENSIONS);
211         if (ext && strstr((const char*)ext, "GL_EXT_unpack_subimage")) {
212                 gt->supports_rowlen = true;
213         } else {
214                 log_warning("your GL implementation does not support GL_EXT_unpack_subimage, glyph-rendering may be slower than usual");
215         }
216
217         return 0;
218
219 err_shader:
220         gl_shader_unref(gt->shader);
221 err_bold_htable:
222         shl_hashtable_free(gt->bold_glyphs);
223 err_htable:
224         shl_hashtable_free(gt->glyphs);
225         return ret;
226 }
227
228 static void gltex_unset(struct kmscon_text *txt)
229 {
230         struct gltex *gt = txt->data;
231         int ret;
232         struct shl_dlist *iter;
233         struct atlas *atlas;
234         bool gl = true;
235
236         ret = uterm_display_use(txt->disp, NULL);
237         if (ret) {
238                 gl = false;
239                 log_warning("cannot activate OpenGL-CTX during destruction");
240         }
241
242         shl_hashtable_free(gt->bold_glyphs);
243         shl_hashtable_free(gt->glyphs);
244
245         while (!shl_dlist_empty(&gt->atlases)) {
246                 iter = gt->atlases.next;
247                 shl_dlist_unlink(iter);
248                 atlas = shl_dlist_entry(iter, struct atlas, list);
249
250                 free(atlas->cache_pos);
251                 free(atlas->cache_texpos);
252                 free(atlas->cache_fgcol);
253                 free(atlas->cache_bgcol);
254
255                 if (gl)
256                         gl_tex_free(&atlas->tex, 1);
257                 free(atlas);
258         }
259
260         if (gl) {
261                 gl_shader_unref(gt->shader);
262
263                 gl_clear_error();
264         }
265 }
266
267 /* returns an atlas with at least 1 free glyph position; NULL on error */
268 static struct atlas *get_atlas(struct kmscon_text *txt, unsigned int num)
269 {
270         struct gltex *gt = txt->data;
271         struct atlas *atlas;
272         size_t newsize;
273         unsigned int width, height, nsize;
274         GLenum err;
275
276         /* check whether the last added atlas has still room for one glyph */
277         if (!shl_dlist_empty(&gt->atlases)) {
278                 atlas = shl_dlist_entry(gt->atlases.next, struct atlas,
279                                            list);
280                 if (atlas->fill + num <= atlas->count)
281                         return atlas;
282         }
283
284         /* all atlases are full so we have to create a new atlas */
285         atlas = malloc(sizeof(*atlas));
286         if (!atlas)
287                 return NULL;
288         memset(atlas, 0, sizeof(*atlas));
289
290         gl_clear_error();
291
292         gl_tex_new(&atlas->tex, 1);
293         err = glGetError();
294         if (err != GL_NO_ERROR || !atlas->tex) {
295                 gl_clear_error();
296                 log_warning("cannot create new OpenGL texture: %d", err);
297                 goto err_free;
298         }
299
300         newsize = gt->max_tex_size / FONT_WIDTH(txt);
301         if (newsize < 1)
302                 newsize = 1;
303
304         /* OpenGL texture sizes are heavily restricted so we need to find a
305          * valid texture size that is big enough to hold as many glyphs as
306          * possible but at least 1 */
307 try_next:
308         width = shl_next_pow2(FONT_WIDTH(txt) * newsize);
309         height = shl_next_pow2(FONT_HEIGHT(txt));
310
311         gl_clear_error();
312
313         glBindTexture(GL_TEXTURE_2D, atlas->tex);
314         glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height,
315                      0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL);
316
317         err = glGetError();
318         if (err != GL_NO_ERROR) {
319                 if (newsize > 1) {
320                         --newsize;
321                         goto try_next;
322                 }
323                 gl_clear_error();
324                 log_warning("OpenGL textures too small for a single glyph (%d)",
325                             err);
326                 goto err_tex;
327         }
328
329         log_debug("new atlas of size %ux%u for %zu", width, height, newsize);
330
331         nsize = txt->cols * txt->rows;
332
333         atlas->cache_pos = malloc(sizeof(GLfloat) * nsize * 2 * 6);
334         if (!atlas->cache_pos)
335                 goto err_mem;
336
337         atlas->cache_texpos = malloc(sizeof(GLfloat) * nsize * 2 * 6);
338         if (!atlas->cache_texpos)
339                 goto err_mem;
340
341         atlas->cache_fgcol = malloc(sizeof(GLfloat) * nsize * 3 * 6);
342         if (!atlas->cache_fgcol)
343                 goto err_mem;
344
345         atlas->cache_bgcol = malloc(sizeof(GLfloat) * nsize * 3 * 6);
346         if (!atlas->cache_bgcol)
347                 goto err_mem;
348
349         atlas->cache_size = nsize;
350         atlas->count = newsize;
351         atlas->width = width;
352         atlas->height = height;
353         atlas->advance_htex = 1.0 / atlas->width * FONT_WIDTH(txt);
354         atlas->advance_vtex = 1.0 / atlas->height * FONT_HEIGHT(txt);
355
356         shl_dlist_link(&gt->atlases, &atlas->list);
357         return atlas;
358
359 err_mem:
360         free(atlas->cache_pos);
361         free(atlas->cache_texpos);
362         free(atlas->cache_fgcol);
363         free(atlas->cache_bgcol);
364 err_tex:
365         gl_tex_free(&atlas->tex, 1);
366 err_free:
367         free(atlas);
368         return NULL;
369 }
370
371 static int find_glyph(struct kmscon_text *txt, struct glyph **out,
372                       uint32_t id, const uint32_t *ch, size_t len, bool bold)
373 {
374         struct gltex *gt = txt->data;
375         struct atlas *atlas;
376         struct glyph *glyph;
377         bool res;
378         int ret, i;
379         GLenum err;
380         uint8_t *packed_data, *dst, *src;
381         struct shl_hashtable *gtable;
382         struct kmscon_font *font;
383
384         if (bold) {
385                 gtable = gt->bold_glyphs;
386                 font = txt->bold_font;
387         } else {
388                 gtable = gt->glyphs;
389                 font = txt->font;
390         }
391
392         res = shl_hashtable_find(gtable, (void**)&glyph,
393                                  (void*)(unsigned long)id);
394         if (res) {
395                 *out = glyph;
396                 return 0;
397         }
398
399         glyph = malloc(sizeof(*glyph));
400         if (!glyph)
401                 return -ENOMEM;
402         memset(glyph, 0, sizeof(*glyph));
403
404         if (!len)
405                 ret = kmscon_font_render_empty(font, &glyph->glyph);
406         else
407                 ret = kmscon_font_render(font, id, ch, len, &glyph->glyph);
408
409         if (ret) {
410                 ret = kmscon_font_render_inval(font, &glyph->glyph);
411                 if (ret)
412                         goto err_free;
413         }
414
415         atlas = get_atlas(txt, glyph->glyph->width);
416         if (!atlas) {
417                 ret = -EFAULT;
418                 goto err_free;
419         }
420
421         /* Funnily, not all OpenGLESv2 implementations support specifying the
422          * stride of a texture. Therefore, we then need to create a
423          * temporary image with a stride equal to the image width for loading
424          * the texture. This may slow down loading new glyphs but doesn't affect
425          * overall rendering performance. But driver developers should really
426          * add this! */
427
428         gl_clear_error();
429
430         glBindTexture(GL_TEXTURE_2D, atlas->tex);
431         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
432         if (!gt->supports_rowlen) {
433                 if (GLYPH_STRIDE(glyph) == GLYPH_WIDTH(glyph)) {
434                         glTexSubImage2D(GL_TEXTURE_2D, 0,
435                                         FONT_WIDTH(txt) * atlas->fill, 0,
436                                         GLYPH_WIDTH(glyph),
437                                         GLYPH_HEIGHT(glyph),
438                                         GL_ALPHA, GL_UNSIGNED_BYTE,
439                                         GLYPH_DATA(glyph));
440                 } else {
441                         packed_data = malloc(GLYPH_WIDTH(glyph) * GLYPH_HEIGHT(glyph));
442                         if (!packed_data) {
443                                 log_error("cannot allocate memory for glyph storage");
444                                 ret = -ENOMEM;
445                                 goto err_free;
446                         }
447
448                         src = GLYPH_DATA(glyph);
449                         dst = packed_data;
450                         for (i = 0; i < GLYPH_HEIGHT(glyph); ++i) {
451                                 memcpy(dst, src, GLYPH_WIDTH(glyph));
452                                 dst += GLYPH_WIDTH(glyph);
453                                 src += GLYPH_STRIDE(glyph);
454                         }
455
456                         glTexSubImage2D(GL_TEXTURE_2D, 0,
457                                         FONT_WIDTH(txt) * atlas->fill, 0,
458                                         GLYPH_WIDTH(glyph),
459                                         GLYPH_HEIGHT(glyph),
460                                         GL_ALPHA, GL_UNSIGNED_BYTE,
461                                         packed_data);
462                         free(packed_data);
463                 }
464         } else {
465                 glPixelStorei(GL_UNPACK_ROW_LENGTH, GLYPH_STRIDE(glyph));
466                 glTexSubImage2D(GL_TEXTURE_2D, 0,
467                                 FONT_WIDTH(txt) * atlas->fill, 0,
468                                 GLYPH_WIDTH(glyph),
469                                 GLYPH_HEIGHT(glyph),
470                                 GL_ALPHA, GL_UNSIGNED_BYTE,
471                                 GLYPH_DATA(glyph));
472                 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
473         }
474         glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
475
476         /* Check for GL-errors
477          * As OpenGL is a state-machine, we cannot really tell which call failed
478          * without adding a glGetError() after each call. This is totally
479          * overkill so let us at least catch the error afterwards.
480          * We also add a hint to disable OpenGL if this does not work. This
481          * should _always_ work but OpenGL is kind of a black-box that isn't
482          * verbose at all and many things can go wrong. */
483
484         err = glGetError();
485         if (err != GL_NO_ERROR) {
486                 gl_clear_error();
487                 log_warning("cannot load glyph data into OpenGL texture (%d: %s); disable the GL-renderer if this does not work reliably",
488                             err, gl_err_to_str(err));
489                 ret = -EFAULT;
490                 goto err_free;
491         }
492
493         glyph->atlas = atlas;
494         glyph->texoff = atlas->fill;
495
496         ret = shl_hashtable_insert(gtable, (void*)(long)id, glyph);
497         if (ret)
498                 goto err_free;
499
500         atlas->fill += glyph->glyph->width;
501
502         *out = glyph;
503         return 0;
504
505 err_free:
506         free(glyph);
507         return ret;
508 }
509
510 static int gltex_prepare(struct kmscon_text *txt)
511 {
512         struct gltex *gt = txt->data;
513         struct atlas *atlas;
514         struct shl_dlist *iter;
515         int ret;
516
517         ret = uterm_display_use(txt->disp, NULL);
518         if (ret)
519                 return ret;
520
521         shl_dlist_for_each(iter, &gt->atlases) {
522                 atlas = shl_dlist_entry(iter, struct atlas, list);
523
524                 atlas->cache_num = 0;
525         }
526
527         gt->advance_x = 2.0 / gt->sw * FONT_WIDTH(txt);
528         gt->advance_y = 2.0 / gt->sh * FONT_HEIGHT(txt);
529
530         return 0;
531 }
532
533 static int gltex_draw(struct kmscon_text *txt,
534                       uint32_t id, const uint32_t *ch, size_t len,
535                       unsigned int width,
536                       unsigned int posx, unsigned int posy,
537                       const struct tsm_screen_attr *attr)
538 {
539         struct gltex *gt = txt->data;
540         struct atlas *atlas;
541         struct glyph *glyph;
542         int ret, i, idx;
543
544         if (!width)
545                 return 0;
546
547         ret = find_glyph(txt, &glyph, id, ch, len, attr->bold);
548         if (ret)
549                 return ret;
550         atlas = glyph->atlas;
551
552         if (atlas->cache_num >= atlas->cache_size)
553                 return -ERANGE;
554
555         atlas->cache_pos[atlas->cache_num * 2 * 6 + 0] =
556                 gt->advance_x * posx - 1;
557         atlas->cache_pos[atlas->cache_num * 2 * 6 + 1] =
558                 1 - gt->advance_y * posy;
559         atlas->cache_pos[atlas->cache_num * 2 * 6 + 2] =
560                 gt->advance_x * posx - 1;
561         atlas->cache_pos[atlas->cache_num * 2 * 6 + 3] =
562                 1 - (gt->advance_y * posy + gt->advance_y);
563         atlas->cache_pos[atlas->cache_num * 2 * 6 + 4] =
564                 gt->advance_x * posx + width * gt->advance_x - 1;
565         atlas->cache_pos[atlas->cache_num * 2 * 6 + 5] =
566                 1 - (gt->advance_y * posy + gt->advance_y);
567
568         atlas->cache_pos[atlas->cache_num * 2 * 6 + 6] =
569                 gt->advance_x * posx - 1;
570         atlas->cache_pos[atlas->cache_num * 2 * 6 + 7] =
571                 1 - gt->advance_y * posy;
572         atlas->cache_pos[atlas->cache_num * 2 * 6 + 8] =
573                 gt->advance_x * posx + width * gt->advance_x - 1;
574         atlas->cache_pos[atlas->cache_num * 2 * 6 + 9] =
575                 1 - (gt->advance_y * posy + gt->advance_y);
576         atlas->cache_pos[atlas->cache_num * 2 * 6 + 10] =
577                 gt->advance_x * posx + width * gt->advance_x - 1;
578         atlas->cache_pos[atlas->cache_num * 2 * 6 + 11] =
579                 1 - gt->advance_y * posy;
580
581         atlas->cache_texpos[atlas->cache_num * 2 * 6 + 0] = glyph->texoff;
582         atlas->cache_texpos[atlas->cache_num * 2 * 6 + 1] = 0.0;
583         atlas->cache_texpos[atlas->cache_num * 2 * 6 + 2] = glyph->texoff;
584         atlas->cache_texpos[atlas->cache_num * 2 * 6 + 3] = 1.0;
585         atlas->cache_texpos[atlas->cache_num * 2 * 6 + 4] = glyph->texoff + width;
586         atlas->cache_texpos[atlas->cache_num * 2 * 6 + 5] = 1.0;
587
588         atlas->cache_texpos[atlas->cache_num * 2 * 6 + 6] = glyph->texoff;
589         atlas->cache_texpos[atlas->cache_num * 2 * 6 + 7] = 0.0;
590         atlas->cache_texpos[atlas->cache_num * 2 * 6 + 8] = glyph->texoff + width;
591         atlas->cache_texpos[atlas->cache_num * 2 * 6 + 9] = 1.0;
592         atlas->cache_texpos[atlas->cache_num * 2 * 6 + 10] = glyph->texoff + width;
593         atlas->cache_texpos[atlas->cache_num * 2 * 6 + 11] = 0.0;
594
595         for (i = 0; i < 6; ++i) {
596                 idx = atlas->cache_num * 3 * 6 + i * 3;
597                 if (attr->inverse) {
598                         atlas->cache_fgcol[idx + 0] = attr->br / 255.0;
599                         atlas->cache_fgcol[idx + 1] = attr->bg / 255.0;
600                         atlas->cache_fgcol[idx + 2] = attr->bb / 255.0;
601                         atlas->cache_bgcol[idx + 0] = attr->fr / 255.0;
602                         atlas->cache_bgcol[idx + 1] = attr->fg / 255.0;
603                         atlas->cache_bgcol[idx + 2] = attr->fb / 255.0;
604                 } else {
605                         atlas->cache_fgcol[idx + 0] = attr->fr / 255.0;
606                         atlas->cache_fgcol[idx + 1] = attr->fg / 255.0;
607                         atlas->cache_fgcol[idx + 2] = attr->fb / 255.0;
608                         atlas->cache_bgcol[idx + 0] = attr->br / 255.0;
609                         atlas->cache_bgcol[idx + 1] = attr->bg / 255.0;
610                         atlas->cache_bgcol[idx + 2] = attr->bb / 255.0;
611                 }
612         }
613
614         ++atlas->cache_num;
615
616         return 0;
617 }
618
619 static int gltex_render(struct kmscon_text *txt)
620 {
621         struct gltex *gt = txt->data;
622         struct atlas *atlas;
623         struct shl_dlist *iter;
624         float mat[16];
625
626         gl_clear_error();
627
628         gl_shader_use(gt->shader);
629
630         glViewport(0, 0, gt->sw, gt->sh);
631         glDisable(GL_BLEND);
632
633         gl_m4_identity(mat);
634         glUniformMatrix4fv(gt->uni_proj, 1, GL_FALSE, mat);
635
636         glEnableVertexAttribArray(0);
637         glEnableVertexAttribArray(1);
638         glEnableVertexAttribArray(2);
639         glEnableVertexAttribArray(3);
640
641         glActiveTexture(GL_TEXTURE0);
642         glUniform1i(gt->uni_atlas, 0);
643
644         shl_dlist_for_each(iter, &gt->atlases) {
645                 atlas = shl_dlist_entry(iter, struct atlas, list);
646                 if (!atlas->cache_num)
647                         continue;
648
649                 glBindTexture(GL_TEXTURE_2D, atlas->tex);
650                 glUniform1f(gt->uni_advance_htex, atlas->advance_htex);
651                 glUniform1f(gt->uni_advance_vtex, atlas->advance_vtex);
652
653                 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, atlas->cache_pos);
654                 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, atlas->cache_texpos);
655                 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, atlas->cache_fgcol);
656                 glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, atlas->cache_bgcol);
657                 glDrawArrays(GL_TRIANGLES, 0, 6 * atlas->cache_num);
658         }
659
660         glDisableVertexAttribArray(0);
661         glDisableVertexAttribArray(1);
662         glDisableVertexAttribArray(2);
663         glDisableVertexAttribArray(3);
664
665         if (gl_has_error(gt->shader)) {
666                 log_warning("rendering console caused OpenGL errors");
667                 return -EFAULT;
668         }
669
670         return 0;
671 }
672
673 struct kmscon_text_ops kmscon_text_gltex_ops = {
674         .name = "gltex",
675         .owner = NULL,
676         .init = gltex_init,
677         .destroy = gltex_destroy,
678         .set = gltex_set,
679         .unset = gltex_unset,
680         .prepare = gltex_prepare,
681         .draw = gltex_draw,
682         .render = gltex_render,
683         .abort = NULL,
684 };