1/*
2 * Copyright © 2014 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22#include <stdlib.h>
23#include "Xprintf.h"
24
25#include "glamor_priv.h"
26#include "glamor_transform.h"
27#include "glamor_transfer.h"
28
29#include <mipict.h>
30
31#define DEFAULT_ATLAS_DIM       1024
32
33static DevPrivateKeyRec        glamor_glyph_private_key;
34
35struct glamor_glyph_private {
36    int16_t     x;
37    int16_t     y;
38    uint32_t    serial;
39};
40
41struct glamor_glyph_atlas {
42    PixmapPtr           atlas;
43    PictFormatPtr       format;
44    int                 x, y;
45    int                 row_height;
46    int                 nglyph;
47    uint32_t            serial;
48};
49
50static inline struct glamor_glyph_private *glamor_get_glyph_private(PixmapPtr pixmap) {
51    return dixLookupPrivate(&pixmap->devPrivates, &glamor_glyph_private_key);
52}
53
54static inline void
55glamor_copy_glyph(PixmapPtr     glyph_pixmap,
56                  DrawablePtr   atlas_draw,
57                  int16_t x,
58                  int16_t y)
59{
60    DrawablePtr glyph_draw = &glyph_pixmap->drawable;
61    BoxRec      box = {
62        .x1 = 0,
63        .y1 = 0,
64        .x2 = glyph_draw->width,
65        .y2 = glyph_draw->height,
66    };
67    PixmapPtr upload_pixmap = glyph_pixmap;
68
69    if (glyph_pixmap->drawable.bitsPerPixel != atlas_draw->bitsPerPixel) {
70
71        /* If we're dealing with 1-bit glyphs, we copy them to a
72         * temporary 8-bit pixmap and upload them from there, since
73         * that's what GL can handle.
74         */
75        ScreenPtr       screen = atlas_draw->pScreen;
76        GCPtr           scratch_gc;
77        ChangeGCVal     changes[2];
78
79        upload_pixmap = glamor_create_pixmap(screen,
80                                             glyph_draw->width,
81                                             glyph_draw->height,
82                                             atlas_draw->depth,
83                                             GLAMOR_CREATE_PIXMAP_CPU);
84        if (!upload_pixmap)
85            return;
86
87        scratch_gc = GetScratchGC(upload_pixmap->drawable.depth, screen);
88        if (!scratch_gc) {
89            glamor_destroy_pixmap(upload_pixmap);
90            return;
91        }
92        changes[0].val = 0xff;
93        changes[1].val = 0x00;
94        if (ChangeGC(NullClient, scratch_gc,
95                     GCForeground|GCBackground, changes) != Success) {
96            glamor_destroy_pixmap(upload_pixmap);
97            FreeScratchGC(scratch_gc);
98            return;
99        }
100        ValidateGC(&upload_pixmap->drawable, scratch_gc);
101
102        (*scratch_gc->ops->CopyPlane)(glyph_draw,
103                                      &upload_pixmap->drawable,
104                                      scratch_gc,
105                                      0, 0,
106                                      glyph_draw->width,
107                                      glyph_draw->height,
108                                      0, 0, 0x1);
109    }
110    glamor_upload_boxes((PixmapPtr) atlas_draw,
111                        &box, 1,
112                        0, 0,
113                        x, y,
114                        upload_pixmap->devPrivate.ptr,
115                        upload_pixmap->devKind);
116
117    if (upload_pixmap != glyph_pixmap)
118        glamor_destroy_pixmap(upload_pixmap);
119}
120
121static Bool
122glamor_glyph_atlas_init(ScreenPtr screen, struct glamor_glyph_atlas *atlas)
123{
124    glamor_screen_private       *glamor_priv = glamor_get_screen_private(screen);
125    PictFormatPtr               format = atlas->format;
126
127    atlas->atlas = glamor_create_pixmap(screen, glamor_priv->glyph_atlas_dim,
128                                        glamor_priv->glyph_atlas_dim, format->depth,
129                                        GLAMOR_CREATE_FBO_NO_FBO);
130    if (!glamor_pixmap_has_fbo(atlas->atlas)) {
131        glamor_destroy_pixmap(atlas->atlas);
132        atlas->atlas = NULL;
133    }
134    atlas->x = 0;
135    atlas->y = 0;
136    atlas->row_height = 0;
137    atlas->serial++;
138    atlas->nglyph = 0;
139    return TRUE;
140}
141
142static Bool
143glamor_glyph_can_add(struct glamor_glyph_atlas *atlas, int dim, DrawablePtr glyph_draw)
144{
145    /* Step down */
146    if (atlas->x + glyph_draw->width > dim) {
147        atlas->x = 0;
148        atlas->y += atlas->row_height;
149        atlas->row_height = 0;
150    }
151
152    /* Check for overfull */
153    if (atlas->y + glyph_draw->height > dim)
154        return FALSE;
155
156    return TRUE;
157}
158
159static Bool
160glamor_glyph_add(struct glamor_glyph_atlas *atlas, DrawablePtr glyph_draw)
161{
162    PixmapPtr                   glyph_pixmap = (PixmapPtr) glyph_draw;
163    struct glamor_glyph_private *glyph_priv = glamor_get_glyph_private(glyph_pixmap);
164
165    glamor_copy_glyph(glyph_pixmap, &atlas->atlas->drawable, atlas->x, atlas->y);
166
167    glyph_priv->x = atlas->x;
168    glyph_priv->y = atlas->y;
169    glyph_priv->serial = atlas->serial;
170
171    atlas->x += glyph_draw->width;
172    if (atlas->row_height < glyph_draw->height)
173        atlas->row_height = glyph_draw->height;
174
175    atlas->nglyph++;
176
177    return TRUE;
178}
179
180static const glamor_facet glamor_facet_composite_glyphs_130 = {
181    .name = "composite_glyphs",
182    .version = 130,
183    .vs_vars = ("attribute vec4 primitive;\n"
184                "attribute vec2 source;\n"
185                "varying vec2 glyph_pos;\n"),
186    .vs_exec = ("       vec2 pos = primitive.zw * vec2(gl_VertexID&1, (gl_VertexID&2)>>1);\n"
187                GLAMOR_POS(gl_Position, (primitive.xy + pos))
188                "       glyph_pos = (source + pos) * ATLAS_DIM_INV;\n"),
189    .fs_vars = ("varying vec2 glyph_pos;\n"
190                "out vec4 color0;\n"
191                "out vec4 color1;\n"),
192    .fs_exec = ("       vec4 mask = texture2D(atlas, glyph_pos);\n"),
193    .source_name = "source",
194    .locations = glamor_program_location_atlas,
195};
196
197static const glamor_facet glamor_facet_composite_glyphs_120 = {
198    .name = "composite_glyphs",
199    .vs_vars = ("attribute vec2 primitive;\n"
200                "attribute vec2 source;\n"
201                "varying vec2 glyph_pos;\n"),
202    .vs_exec = ("       vec2 pos = vec2(0,0);\n"
203                GLAMOR_POS(gl_Position, primitive.xy)
204                "       glyph_pos = source.xy * ATLAS_DIM_INV;\n"),
205    .fs_vars = ("varying vec2 glyph_pos;\n"),
206    .fs_exec = ("       vec4 mask = texture2D(atlas, glyph_pos);\n"),
207    .source_name = "source",
208    .locations = glamor_program_location_atlas,
209};
210
211static Bool
212glamor_glyphs_init_facet(ScreenPtr screen)
213{
214    glamor_screen_private       *glamor_priv = glamor_get_screen_private(screen);
215
216    return asprintf(&glamor_priv->glyph_defines, "#define ATLAS_DIM_INV %20.18f\n", 1.0/glamor_priv->glyph_atlas_dim) > 0;
217}
218
219static void
220glamor_glyphs_fini_facet(ScreenPtr screen)
221{
222    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
223
224    free(glamor_priv->glyph_defines);
225}
226
227static void
228glamor_glyphs_flush(CARD8 op, PicturePtr src, PicturePtr dst,
229                   glamor_program *prog,
230                   struct glamor_glyph_atlas *atlas, int nglyph)
231{
232    DrawablePtr drawable = dst->pDrawable;
233    glamor_screen_private *glamor_priv = glamor_get_screen_private(drawable->pScreen);
234    PixmapPtr atlas_pixmap = atlas->atlas;
235    glamor_pixmap_private *atlas_priv = glamor_get_pixmap_private(atlas_pixmap);
236    glamor_pixmap_fbo *atlas_fbo = glamor_pixmap_fbo_at(atlas_priv, 0);
237    PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable);
238    glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap);
239    int box_index;
240    int off_x, off_y;
241
242    glamor_put_vbo_space(drawable->pScreen);
243
244    glEnable(GL_SCISSOR_TEST);
245    glamor_bind_texture(glamor_priv, GL_TEXTURE1, atlas_fbo, FALSE);
246
247    for (;;) {
248        if (!glamor_use_program_render(prog, op, src, dst))
249            break;
250
251        glUniform1i(prog->atlas_uniform, 1);
252
253        glamor_pixmap_loop(pixmap_priv, box_index) {
254            BoxPtr box = RegionRects(dst->pCompositeClip);
255            int nbox = RegionNumRects(dst->pCompositeClip);
256
257            glamor_set_destination_drawable(drawable, box_index, TRUE, FALSE,
258                                            prog->matrix_uniform,
259                                            &off_x, &off_y);
260
261            /* Run over the clip list, drawing the glyphs
262             * in each box
263             */
264
265            while (nbox--) {
266                glScissor(box->x1 + off_x,
267                          box->y1 + off_y,
268                          box->x2 - box->x1,
269                          box->y2 - box->y1);
270                box++;
271
272                if (glamor_glsl_has_ints(glamor_priv))
273                    glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, nglyph);
274                else
275                    glamor_glDrawArrays_GL_QUADS(glamor_priv, nglyph);
276            }
277        }
278        if (prog->alpha != glamor_program_alpha_ca_first)
279            break;
280        prog++;
281    }
282
283    glDisable(GL_SCISSOR_TEST);
284
285    if (glamor_glsl_has_ints(glamor_priv)) {
286        glVertexAttribDivisor(GLAMOR_VERTEX_SOURCE, 0);
287        glVertexAttribDivisor(GLAMOR_VERTEX_POS, 0);
288    }
289    glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE);
290    glDisableVertexAttribArray(GLAMOR_VERTEX_POS);
291    glDisable(GL_BLEND);
292}
293
294static GLshort *
295glamor_glyph_start(ScreenPtr screen, int count)
296{
297    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
298    GLshort *v;
299    char *vbo_offset;
300
301    /* Set up the vertex buffers for the font and destination */
302
303    if (glamor_glsl_has_ints(glamor_priv)) {
304        v = glamor_get_vbo_space(screen, count * (6 * sizeof (GLshort)), &vbo_offset);
305
306        glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
307        glVertexAttribDivisor(GLAMOR_VERTEX_POS, 1);
308        glVertexAttribPointer(GLAMOR_VERTEX_POS, 4, GL_SHORT, GL_FALSE,
309                              6 * sizeof (GLshort), vbo_offset);
310
311        glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE);
312        glVertexAttribDivisor(GLAMOR_VERTEX_SOURCE, 1);
313        glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_SHORT, GL_FALSE,
314                              6 * sizeof (GLshort), vbo_offset + 4 * sizeof (GLshort));
315    } else {
316        v = glamor_get_vbo_space(screen, count * (16 * sizeof (GLshort)), &vbo_offset);
317
318        glEnableVertexAttribArray(GLAMOR_VERTEX_POS);
319        glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_SHORT, GL_FALSE,
320                              4 * sizeof (GLshort), vbo_offset);
321
322        glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE);
323        glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_SHORT, GL_FALSE,
324                              4 * sizeof (GLshort), vbo_offset + 2 * sizeof (GLshort));
325    }
326    return v;
327}
328
329static inline struct glamor_glyph_atlas *
330glamor_atlas_for_glyph(glamor_screen_private *glamor_priv, DrawablePtr drawable)
331{
332    if (drawable->depth == 32)
333        return glamor_priv->glyph_atlas_argb;
334    else
335        return glamor_priv->glyph_atlas_a;
336}
337
338void
339glamor_composite_glyphs(CARD8 op,
340                        PicturePtr src,
341                        PicturePtr dst,
342                        PictFormatPtr glyph_format,
343                        INT16 x_src,
344                        INT16 y_src, int nlist, GlyphListPtr list,
345                        GlyphPtr *glyphs)
346{
347    int glyphs_queued;
348    GLshort *v = NULL;
349    DrawablePtr drawable = dst->pDrawable;
350    ScreenPtr screen = drawable->pScreen;
351    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
352    glamor_program *prog = NULL;
353    glamor_program_render       *glyphs_program = &glamor_priv->glyphs_program;
354    struct glamor_glyph_atlas    *glyph_atlas = NULL;
355    int x = 0, y = 0;
356    int n;
357    int glyph_atlas_dim = glamor_priv->glyph_atlas_dim;
358    int glyph_max_dim = glamor_priv->glyph_max_dim;
359    int nglyph = 0;
360    int screen_num = screen->myNum;
361
362    for (n = 0; n < nlist; n++)
363        nglyph += list[n].len;
364
365    glamor_make_current(glamor_priv);
366
367    glyphs_queued = 0;
368
369    while (nlist--) {
370        x += list->xOff;
371        y += list->yOff;
372        n = list->len;
373        list++;
374        while (n--) {
375            GlyphPtr glyph = *glyphs++;
376
377            /* Glyph not empty?
378             */
379            if (glyph->info.width && glyph->info.height) {
380                PicturePtr glyph_pict = GlyphPicture(glyph)[screen_num];
381                DrawablePtr glyph_draw = glyph_pict->pDrawable;
382
383                /* Need to draw with slow path?
384                 */
385                if (_X_UNLIKELY(glyph_draw->width > glyph_max_dim ||
386                                glyph_draw->height > glyph_max_dim ||
387                                !glamor_pixmap_is_memory((PixmapPtr)glyph_draw)))
388                {
389                    if (glyphs_queued) {
390                        glamor_glyphs_flush(op, src, dst, prog, glyph_atlas, glyphs_queued);
391                        glyphs_queued = 0;
392                    }
393                bail_one:
394                    glamor_composite(op, src, glyph_pict, dst,
395                                     x_src + (x - glyph->info.x), (y - glyph->info.y),
396                                     0, 0,
397                                     x - glyph->info.x, y - glyph->info.y,
398                                     glyph_draw->width, glyph_draw->height);
399                } else {
400                    struct glamor_glyph_private *glyph_priv = glamor_get_glyph_private((PixmapPtr)(glyph_draw));
401                    struct glamor_glyph_atlas *next_atlas = glamor_atlas_for_glyph(glamor_priv, glyph_draw);
402
403                    /* Switching source glyph format?
404                     */
405                    if (_X_UNLIKELY(next_atlas != glyph_atlas)) {
406                        if (glyphs_queued) {
407                            glamor_glyphs_flush(op, src, dst, prog, glyph_atlas, glyphs_queued);
408                            glyphs_queued = 0;
409                        }
410                        glyph_atlas = next_atlas;
411                    }
412
413                    /* Glyph not cached in current atlas?
414                     */
415                    if (_X_UNLIKELY(glyph_priv->serial != glyph_atlas->serial)) {
416                        if (!glamor_glyph_can_add(glyph_atlas, glyph_atlas_dim, glyph_draw)) {
417                            if (glyphs_queued) {
418                                glamor_glyphs_flush(op, src, dst, prog, glyph_atlas, glyphs_queued);
419                                glyphs_queued = 0;
420                            }
421                            if (glyph_atlas->atlas) {
422                                (*screen->DestroyPixmap)(glyph_atlas->atlas);
423                                glyph_atlas->atlas = NULL;
424                            }
425                        }
426                        if (!glyph_atlas->atlas) {
427                            glamor_glyph_atlas_init(screen, glyph_atlas);
428                            if (!glyph_atlas->atlas)
429                                goto bail_one;
430                        }
431                        glamor_glyph_add(glyph_atlas, glyph_draw);
432                    }
433
434                    /* First glyph in the current atlas?
435                     */
436                    if (_X_UNLIKELY(glyphs_queued == 0)) {
437                        if (glamor_glsl_has_ints(glamor_priv))
438                            prog = glamor_setup_program_render(op, src, glyph_pict, dst,
439                                                               glyphs_program,
440                                                               &glamor_facet_composite_glyphs_130,
441                                                               glamor_priv->glyph_defines);
442                        else
443                            prog = glamor_setup_program_render(op, src, glyph_pict, dst,
444                                                               glyphs_program,
445                                                               &glamor_facet_composite_glyphs_120,
446                                                               glamor_priv->glyph_defines);
447                        if (!prog)
448                            goto bail_one;
449                        v = glamor_glyph_start(screen, nglyph);
450                    }
451
452                    /* Add the glyph
453                     */
454
455                    glyphs_queued++;
456                    if (_X_LIKELY(glamor_glsl_has_ints(glamor_priv))) {
457                        v[0] = x - glyph->info.x;
458                        v[1] = y - glyph->info.y;
459                        v[2] = glyph_draw->width;
460                        v[3] = glyph_draw->height;
461                        v[4] = glyph_priv->x;
462                        v[5] = glyph_priv->y;
463                        v += 6;
464                    } else {
465                        v[0] = x - glyph->info.x;
466                        v[1] = y - glyph->info.y;
467                        v[2] = glyph_priv->x;
468                        v[3] = glyph_priv->y;
469                        v += 4;
470
471                        v[0] = x - glyph->info.x + glyph_draw->width;
472                        v[1] = y - glyph->info.y;
473                        v[2] = glyph_priv->x + glyph_draw->width;
474                        v[3] = glyph_priv->y;
475                        v += 4;
476
477                        v[0] = x - glyph->info.x + glyph_draw->width;
478                        v[1] = y - glyph->info.y + glyph_draw->height;
479                        v[2] = glyph_priv->x + glyph_draw->width;
480                        v[3] = glyph_priv->y + glyph_draw->height;
481                        v += 4;
482
483                        v[0] = x - glyph->info.x;
484                        v[1] = y - glyph->info.y + glyph_draw->height;
485                        v[2] = glyph_priv->x;
486                        v[3] = glyph_priv->y + glyph_draw->height;
487                        v += 4;
488                    }
489                }
490            }
491            x += glyph->info.xOff;
492            y += glyph->info.yOff;
493            nglyph--;
494        }
495    }
496
497    if (glyphs_queued)
498        glamor_glyphs_flush(op, src, dst, prog, glyph_atlas, glyphs_queued);
499
500    return;
501}
502
503static struct glamor_glyph_atlas *
504glamor_alloc_glyph_atlas(ScreenPtr screen, int depth, CARD32 f)
505{
506    PictFormatPtr               format;
507    struct glamor_glyph_atlas    *glyph_atlas;
508
509    format = PictureMatchFormat(screen, depth, f);
510    if (!format)
511        return NULL;
512    glyph_atlas = calloc (1, sizeof (struct glamor_glyph_atlas));
513    if (!glyph_atlas)
514        return NULL;
515    glyph_atlas->format = format;
516    glyph_atlas->serial = 1;
517
518    return glyph_atlas;
519}
520
521Bool
522glamor_composite_glyphs_init(ScreenPtr screen)
523{
524    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
525
526    if (!dixRegisterPrivateKey(&glamor_glyph_private_key, PRIVATE_PIXMAP, sizeof (struct glamor_glyph_private)))
527        return FALSE;
528
529    /* Make glyph atlases of a reasonable size, but no larger than the maximum
530     * supported by the hardware
531     */
532    glamor_priv->glyph_atlas_dim = MIN(DEFAULT_ATLAS_DIM, glamor_priv->max_fbo_size);
533
534    /* Don't stick huge glyphs in the atlases */
535    glamor_priv->glyph_max_dim = glamor_priv->glyph_atlas_dim / 8;
536
537    glamor_priv->glyph_atlas_a = glamor_alloc_glyph_atlas(screen, 8, PICT_a8);
538    if (!glamor_priv->glyph_atlas_a)
539        return FALSE;
540    glamor_priv->glyph_atlas_argb = glamor_alloc_glyph_atlas(screen, 32, PICT_a8r8g8b8);
541    if (!glamor_priv->glyph_atlas_argb) {
542        free (glamor_priv->glyph_atlas_a);
543        return FALSE;
544    }
545    if (!glamor_glyphs_init_facet(screen))
546        return FALSE;
547    return TRUE;
548}
549
550static void
551glamor_free_glyph_atlas(struct glamor_glyph_atlas *atlas)
552{
553    if (!atlas)
554        return;
555    if (atlas->atlas)
556        (*atlas->atlas->drawable.pScreen->DestroyPixmap)(atlas->atlas);
557    free (atlas);
558}
559
560void
561glamor_composite_glyphs_fini(ScreenPtr screen)
562{
563    glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
564
565    glamor_glyphs_fini_facet(screen);
566    glamor_free_glyph_atlas(glamor_priv->glyph_atlas_a);
567    glamor_free_glyph_atlas(glamor_priv->glyph_atlas_argb);
568}
569