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