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