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 33 static DevPrivateKeyRec glamor_glyph_private_key; 34 35 struct glamor_glyph_private { 36 int16_t x; 37 int16_t y; 38 uint32_t serial; 39 }; 40 41 struct 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 50 static inline struct glamor_glyph_private *glamor_get_glyph_private(PixmapPtr pixmap) { 51 return dixLookupPrivate(&pixmap->devPrivates, &glamor_glyph_private_key); 52 } 53 54 static inline void 55 glamor_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 121 static Bool 122 glamor_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 142 static Bool 143 glamor_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 159 static Bool 160 glamor_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 180 static const glamor_facet glamor_facet_composite_glyphs_es300 = { 181 .name = "composite_glyphs", 182 .version = 130, 183 .fs_extensions = ("#extension GL_EXT_blend_func_extended : enable\n"), 184 .vs_vars = ("in vec4 primitive;\n" 185 "in vec2 source;\n" 186 "out vec2 glyph_pos;\n"), 187 .vs_exec = (" vec2 pos = primitive.zw * vec2(gl_VertexID&1, (gl_VertexID&2)>>1);\n" 188 GLAMOR_POS(gl_Position, (primitive.xy + pos)) 189 " glyph_pos = (source + pos) * ATLAS_DIM_INV;\n"), 190 .fs_vars = ("in vec2 glyph_pos;\n" 191 "out vec4 color0;\n" 192 "out vec4 color1;\n"), 193 .fs_exec = (" vec4 mask = texture(atlas, glyph_pos);\n"), 194 .source_name = "source", 195 .locations = glamor_program_location_atlas, 196 }; 197 198 static const glamor_facet glamor_facet_composite_glyphs_130 = { 199 .name = "composite_glyphs", 200 .version = 130, 201 .vs_vars = ("in vec4 primitive;\n" 202 "in vec2 source;\n" 203 "out vec2 glyph_pos;\n"), 204 .vs_exec = (" vec2 pos = primitive.zw * vec2(gl_VertexID&1, (gl_VertexID&2)>>1);\n" 205 GLAMOR_POS(gl_Position, (primitive.xy + pos)) 206 " glyph_pos = (source + pos) * ATLAS_DIM_INV;\n"), 207 .fs_vars = ("in vec2 glyph_pos;\n" 208 "out vec4 color0;\n" 209 "out vec4 color1;\n"), 210 .fs_exec = (" vec4 mask = texture(atlas, glyph_pos);\n"), 211 .source_name = "source", 212 .locations = glamor_program_location_atlas, 213 }; 214 215 static const glamor_facet glamor_facet_composite_glyphs_120 = { 216 .name = "composite_glyphs", 217 .vs_vars = ("attribute vec2 primitive;\n" 218 "attribute vec2 source;\n" 219 "varying vec2 glyph_pos;\n"), 220 .vs_exec = (" vec2 pos = vec2(0,0);\n" 221 GLAMOR_POS(gl_Position, primitive.xy) 222 " glyph_pos = source.xy * ATLAS_DIM_INV;\n"), 223 .fs_vars = ("varying vec2 glyph_pos;\n"), 224 .fs_exec = (" vec4 mask = texture2D(atlas, glyph_pos);\n"), 225 .source_name = "source", 226 .locations = glamor_program_location_atlas, 227 }; 228 229 static const glamor_facet glamor_facet_composite_glyphs_gles2 = { 230 .name = "composite_glyphs", 231 .version = 100, 232 .fs_extensions = ("#extension GL_EXT_blend_func_extended : enable\n"), 233 .vs_vars = ("attribute vec2 primitive;\n" 234 "attribute vec2 source;\n" 235 "varying vec2 glyph_pos;\n"), 236 .vs_exec = (" vec2 pos = vec2(0,0);\n" 237 GLAMOR_POS(gl_Position, primitive.xy) 238 " glyph_pos = source.xy * ATLAS_DIM_INV;\n"), 239 .fs_vars = ("varying vec2 glyph_pos;\n"), 240 .fs_exec = (" vec4 mask = texture2D(atlas, glyph_pos);\n"), 241 .source_name = "source", 242 .locations = glamor_program_location_atlas, 243 }; 244 245 static Bool 246 glamor_glyphs_init_facet(ScreenPtr screen) 247 { 248 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); 249 250 return asprintf(&glamor_priv->glyph_defines, "#define ATLAS_DIM_INV %20.18f\n", 1.0/glamor_priv->glyph_atlas_dim) > 0; 251 } 252 253 static void 254 glamor_glyphs_fini_facet(ScreenPtr screen) 255 { 256 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); 257 258 free(glamor_priv->glyph_defines); 259 } 260 261 static void 262 glamor_glyphs_flush(CARD8 op, PicturePtr src, PicturePtr dst, 263 glamor_program *prog, 264 struct glamor_glyph_atlas *atlas, int nglyph) 265 { 266 DrawablePtr drawable = dst->pDrawable; 267 glamor_screen_private *glamor_priv = glamor_get_screen_private(drawable->pScreen); 268 PixmapPtr atlas_pixmap = atlas->atlas; 269 glamor_pixmap_private *atlas_priv = glamor_get_pixmap_private(atlas_pixmap); 270 glamor_pixmap_fbo *atlas_fbo = glamor_pixmap_fbo_at(atlas_priv, 0); 271 PixmapPtr pixmap = glamor_get_drawable_pixmap(drawable); 272 glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap); 273 int box_index; 274 int off_x, off_y; 275 276 glamor_put_vbo_space(drawable->pScreen); 277 278 glEnable(GL_SCISSOR_TEST); 279 glamor_bind_texture(glamor_priv, GL_TEXTURE1, atlas_fbo, FALSE); 280 281 for (;;) { 282 if (!glamor_use_program_render(prog, op, src, dst)) 283 break; 284 285 glUniform1i(prog->atlas_uniform, 1); 286 287 glamor_pixmap_loop(pixmap_priv, box_index) { 288 BoxPtr box = RegionRects(dst->pCompositeClip); 289 int nbox = RegionNumRects(dst->pCompositeClip); 290 291 glamor_set_destination_drawable(drawable, box_index, TRUE, FALSE, 292 prog->matrix_uniform, 293 &off_x, &off_y); 294 295 /* Run over the clip list, drawing the glyphs 296 * in each box 297 */ 298 299 while (nbox--) { 300 glScissor(box->x1 + off_x, 301 box->y1 + off_y, 302 box->x2 - box->x1, 303 box->y2 - box->y1); 304 box++; 305 306 if (glamor_glsl_has_ints(glamor_priv)) 307 glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, nglyph); 308 else 309 glamor_glDrawArrays_GL_QUADS(glamor_priv, nglyph); 310 } 311 } 312 if (prog->alpha != glamor_program_alpha_ca_first) 313 break; 314 prog++; 315 } 316 317 glDisable(GL_SCISSOR_TEST); 318 319 if (glamor_glsl_has_ints(glamor_priv)) { 320 glVertexAttribDivisor(GLAMOR_VERTEX_SOURCE, 0); 321 glVertexAttribDivisor(GLAMOR_VERTEX_POS, 0); 322 } 323 glDisableVertexAttribArray(GLAMOR_VERTEX_SOURCE); 324 glDisableVertexAttribArray(GLAMOR_VERTEX_POS); 325 glDisable(GL_BLEND); 326 } 327 328 static GLshort * 329 glamor_glyph_start(ScreenPtr screen, int count) 330 { 331 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); 332 GLshort *v; 333 char *vbo_offset; 334 335 /* Set up the vertex buffers for the font and destination */ 336 337 if (glamor_glsl_has_ints(glamor_priv)) { 338 v = glamor_get_vbo_space(screen, count * (6 * sizeof (GLshort)), &vbo_offset); 339 340 glEnableVertexAttribArray(GLAMOR_VERTEX_POS); 341 glVertexAttribDivisor(GLAMOR_VERTEX_POS, 1); 342 glVertexAttribPointer(GLAMOR_VERTEX_POS, 4, GL_SHORT, GL_FALSE, 343 6 * sizeof (GLshort), vbo_offset); 344 345 glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); 346 glVertexAttribDivisor(GLAMOR_VERTEX_SOURCE, 1); 347 glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_SHORT, GL_FALSE, 348 6 * sizeof (GLshort), vbo_offset + 4 * sizeof (GLshort)); 349 } else { 350 v = glamor_get_vbo_space(screen, count * (16 * sizeof (GLshort)), &vbo_offset); 351 352 glEnableVertexAttribArray(GLAMOR_VERTEX_POS); 353 glVertexAttribPointer(GLAMOR_VERTEX_POS, 2, GL_SHORT, GL_FALSE, 354 4 * sizeof (GLshort), vbo_offset); 355 356 glEnableVertexAttribArray(GLAMOR_VERTEX_SOURCE); 357 glVertexAttribPointer(GLAMOR_VERTEX_SOURCE, 2, GL_SHORT, GL_FALSE, 358 4 * sizeof (GLshort), vbo_offset + 2 * sizeof (GLshort)); 359 } 360 return v; 361 } 362 363 static inline struct glamor_glyph_atlas * 364 glamor_atlas_for_glyph(glamor_screen_private *glamor_priv, DrawablePtr drawable) 365 { 366 if (drawable->depth == 32) 367 return glamor_priv->glyph_atlas_argb; 368 else 369 return glamor_priv->glyph_atlas_a; 370 } 371 372 void 373 glamor_composite_glyphs(CARD8 op, 374 PicturePtr src, 375 PicturePtr dst, 376 PictFormatPtr glyph_format, 377 INT16 x_src, 378 INT16 y_src, int nlist, GlyphListPtr list, 379 GlyphPtr *glyphs) 380 { 381 int glyphs_queued; 382 GLshort *v = NULL; 383 DrawablePtr drawable = dst->pDrawable; 384 ScreenPtr screen = drawable->pScreen; 385 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); 386 glamor_program *prog = NULL; 387 glamor_program_render *glyphs_program = &glamor_priv->glyphs_program; 388 struct glamor_glyph_atlas *glyph_atlas = NULL; 389 int x = 0, y = 0; 390 int n; 391 int glyph_atlas_dim = glamor_priv->glyph_atlas_dim; 392 int glyph_max_dim = glamor_priv->glyph_max_dim; 393 int nglyph = 0; 394 int screen_num = screen->myNum; 395 396 for (n = 0; n < nlist; n++) 397 nglyph += list[n].len; 398 399 glamor_make_current(glamor_priv); 400 401 glyphs_queued = 0; 402 403 while (nlist--) { 404 x += list->xOff; 405 y += list->yOff; 406 n = list->len; 407 list++; 408 while (n--) { 409 GlyphPtr glyph = *glyphs++; 410 411 /* Glyph not empty? 412 */ 413 if (glyph->info.width && glyph->info.height) { 414 PicturePtr glyph_pict = GlyphPicture(glyph)[screen_num]; 415 DrawablePtr glyph_draw = glyph_pict->pDrawable; 416 417 /* Need to draw with slow path? 418 */ 419 if (_X_UNLIKELY(glyph_draw->width > glyph_max_dim || 420 glyph_draw->height > glyph_max_dim || 421 !glamor_pixmap_is_memory((PixmapPtr)glyph_draw))) 422 { 423 if (glyphs_queued) { 424 glamor_glyphs_flush(op, src, dst, prog, glyph_atlas, glyphs_queued); 425 glyphs_queued = 0; 426 } 427 bail_one: 428 glamor_composite(op, src, glyph_pict, dst, 429 x_src + (x - glyph->info.x), (y - glyph->info.y), 430 0, 0, 431 x - glyph->info.x, y - glyph->info.y, 432 glyph_draw->width, glyph_draw->height); 433 } else { 434 struct glamor_glyph_private *glyph_priv = glamor_get_glyph_private((PixmapPtr)(glyph_draw)); 435 struct glamor_glyph_atlas *next_atlas = glamor_atlas_for_glyph(glamor_priv, glyph_draw); 436 437 /* Switching source glyph format? 438 */ 439 if (_X_UNLIKELY(next_atlas != glyph_atlas)) { 440 if (glyphs_queued) { 441 glamor_glyphs_flush(op, src, dst, prog, glyph_atlas, glyphs_queued); 442 glyphs_queued = 0; 443 } 444 glyph_atlas = next_atlas; 445 } 446 447 /* Glyph not cached in current atlas? 448 */ 449 if (_X_UNLIKELY(glyph_priv->serial != glyph_atlas->serial)) { 450 if (!glamor_glyph_can_add(glyph_atlas, glyph_atlas_dim, glyph_draw)) { 451 if (glyphs_queued) { 452 glamor_glyphs_flush(op, src, dst, prog, glyph_atlas, glyphs_queued); 453 glyphs_queued = 0; 454 } 455 if (glyph_atlas->atlas) { 456 (*screen->DestroyPixmap)(glyph_atlas->atlas); 457 glyph_atlas->atlas = NULL; 458 } 459 } 460 if (!glyph_atlas->atlas) { 461 glamor_glyph_atlas_init(screen, glyph_atlas); 462 if (!glyph_atlas->atlas) 463 goto bail_one; 464 } 465 glamor_glyph_add(glyph_atlas, glyph_draw); 466 } 467 468 /* First glyph in the current atlas? 469 */ 470 if (_X_UNLIKELY(glyphs_queued == 0)) { 471 if (glamor_glsl_has_ints(glamor_priv)) 472 prog = glamor_setup_program_render(op, src, glyph_pict, dst, 473 glyphs_program, 474 glamor_priv->is_gles ? 475 &glamor_facet_composite_glyphs_es300 : 476 &glamor_facet_composite_glyphs_130, 477 glamor_priv->glyph_defines); 478 else 479 prog = glamor_setup_program_render(op, src, glyph_pict, dst, 480 glyphs_program, 481 glamor_priv->has_dual_blend ? 482 &glamor_facet_composite_glyphs_gles2 : 483 &glamor_facet_composite_glyphs_120, 484 glamor_priv->glyph_defines); 485 if (!prog) 486 goto bail_one; 487 v = glamor_glyph_start(screen, nglyph); 488 } 489 490 /* Add the glyph 491 */ 492 493 glyphs_queued++; 494 if (_X_LIKELY(glamor_glsl_has_ints(glamor_priv))) { 495 v[0] = x - glyph->info.x; 496 v[1] = y - glyph->info.y; 497 v[2] = glyph_draw->width; 498 v[3] = glyph_draw->height; 499 v[4] = glyph_priv->x; 500 v[5] = glyph_priv->y; 501 v += 6; 502 } else { 503 v[0] = x - glyph->info.x; 504 v[1] = y - glyph->info.y; 505 v[2] = glyph_priv->x; 506 v[3] = glyph_priv->y; 507 v += 4; 508 509 v[0] = x - glyph->info.x + glyph_draw->width; 510 v[1] = y - glyph->info.y; 511 v[2] = glyph_priv->x + glyph_draw->width; 512 v[3] = glyph_priv->y; 513 v += 4; 514 515 v[0] = x - glyph->info.x + glyph_draw->width; 516 v[1] = y - glyph->info.y + glyph_draw->height; 517 v[2] = glyph_priv->x + glyph_draw->width; 518 v[3] = glyph_priv->y + glyph_draw->height; 519 v += 4; 520 521 v[0] = x - glyph->info.x; 522 v[1] = y - glyph->info.y + glyph_draw->height; 523 v[2] = glyph_priv->x; 524 v[3] = glyph_priv->y + glyph_draw->height; 525 v += 4; 526 } 527 } 528 } 529 x += glyph->info.xOff; 530 y += glyph->info.yOff; 531 nglyph--; 532 } 533 } 534 535 if (glyphs_queued) 536 glamor_glyphs_flush(op, src, dst, prog, glyph_atlas, glyphs_queued); 537 538 return; 539 } 540 541 static struct glamor_glyph_atlas * 542 glamor_alloc_glyph_atlas(ScreenPtr screen, int depth, CARD32 f) 543 { 544 PictFormatPtr format; 545 struct glamor_glyph_atlas *glyph_atlas; 546 547 format = PictureMatchFormat(screen, depth, f); 548 if (!format) 549 return NULL; 550 glyph_atlas = calloc (1, sizeof (struct glamor_glyph_atlas)); 551 if (!glyph_atlas) 552 return NULL; 553 glyph_atlas->format = format; 554 glyph_atlas->serial = 1; 555 556 return glyph_atlas; 557 } 558 559 Bool 560 glamor_composite_glyphs_init(ScreenPtr screen) 561 { 562 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); 563 564 if (!dixRegisterPrivateKey(&glamor_glyph_private_key, PRIVATE_PIXMAP, sizeof (struct glamor_glyph_private))) 565 return FALSE; 566 567 /* Make glyph atlases of a reasonable size, but no larger than the maximum 568 * supported by the hardware 569 */ 570 glamor_priv->glyph_atlas_dim = MIN(DEFAULT_ATLAS_DIM, glamor_priv->max_fbo_size); 571 572 /* Don't stick huge glyphs in the atlases */ 573 glamor_priv->glyph_max_dim = glamor_priv->glyph_atlas_dim / 8; 574 575 glamor_priv->glyph_atlas_a = glamor_alloc_glyph_atlas(screen, 8, PICT_a8); 576 if (!glamor_priv->glyph_atlas_a) 577 return FALSE; 578 glamor_priv->glyph_atlas_argb = glamor_alloc_glyph_atlas(screen, 32, PICT_a8r8g8b8); 579 if (!glamor_priv->glyph_atlas_argb) { 580 free (glamor_priv->glyph_atlas_a); 581 return FALSE; 582 } 583 if (!glamor_glyphs_init_facet(screen)) 584 return FALSE; 585 return TRUE; 586 } 587 588 static void 589 glamor_free_glyph_atlas(struct glamor_glyph_atlas *atlas) 590 { 591 if (!atlas) 592 return; 593 if (atlas->atlas) 594 (*atlas->atlas->drawable.pScreen->DestroyPixmap)(atlas->atlas); 595 free (atlas); 596 } 597 598 void 599 glamor_composite_glyphs_fini(ScreenPtr screen) 600 { 601 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); 602 603 glamor_glyphs_fini_facet(screen); 604 glamor_free_glyph_atlas(glamor_priv->glyph_atlas_a); 605 glamor_free_glyph_atlas(glamor_priv->glyph_atlas_argb); 606 } 607