glamor_fbo.c revision 35c4bbdf
1/* 2 * Copyright © 2009 Intel Corporation 3 * Copyright © 1998 Keith Packard 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 * 24 * Authors: 25 * Zhigang Gong <zhigang.gong@gmail.com> 26 * 27 */ 28 29#include <stdlib.h> 30 31#include "glamor_priv.h" 32 33#define GLAMOR_CACHE_EXPIRE_MAX 100 34 35#define GLAMOR_CACHE_DEFAULT 0 36#define GLAMOR_CACHE_EXACT_SIZE 1 37 38//#define NO_FBO_CACHE 1 39#define FBO_CACHE_THRESHOLD (256*1024*1024) 40 41/* Loop from the tail to the head. */ 42#define xorg_list_for_each_entry_reverse(pos, head, member) \ 43 for (pos = __container_of((head)->prev, pos, member); \ 44 &pos->member != (head); \ 45 pos = __container_of(pos->member.prev, pos, member)) 46 47#define xorg_list_for_each_entry_safe_reverse(pos, tmp, head, member) \ 48 for (pos = __container_of((head)->prev, pos, member), \ 49 tmp = __container_of(pos->member.prev, pos, member); \ 50 &pos->member != (head); \ 51 pos = tmp, tmp = __container_of(pos->member.prev, tmp, member)) 52 53inline static int 54cache_wbucket(int size) 55{ 56 int order = __fls(size / 32); 57 58 if (order >= CACHE_BUCKET_WCOUNT) 59 order = CACHE_BUCKET_WCOUNT - 1; 60 return order; 61} 62 63inline static int 64cache_hbucket(int size) 65{ 66 int order = __fls(size / 32); 67 68 if (order >= CACHE_BUCKET_HCOUNT) 69 order = CACHE_BUCKET_HCOUNT - 1; 70 return order; 71} 72 73static int 74cache_format(GLenum format) 75{ 76 switch (format) { 77 case GL_ALPHA: 78 case GL_LUMINANCE: 79 case GL_RED: 80 return 2; 81 case GL_RGB: 82 return 1; 83 case GL_RGBA: 84 return 0; 85 default: 86 return -1; 87 } 88} 89 90static glamor_pixmap_fbo * 91glamor_pixmap_fbo_cache_get(glamor_screen_private *glamor_priv, 92 int w, int h, GLenum format) 93{ 94 struct xorg_list *cache; 95 glamor_pixmap_fbo *fbo_entry, *ret_fbo = NULL; 96 int n_format; 97 98#ifdef NO_FBO_CACHE 99 return NULL; 100#else 101 n_format = cache_format(format); 102 if (n_format == -1) 103 return NULL; 104 cache = &glamor_priv->fbo_cache[n_format] 105 [cache_wbucket(w)] 106 [cache_hbucket(h)]; 107 108 xorg_list_for_each_entry(fbo_entry, cache, list) { 109 if (fbo_entry->width == w && fbo_entry->height == h) { 110 111 DEBUGF("Request w %d h %d format %x \n", w, h, format); 112 DEBUGF("got cache entry %p w %d h %d fbo %d tex %d format %x\n", 113 fbo_entry, fbo_entry->width, fbo_entry->height, 114 fbo_entry->fb, fbo_entry->tex, fbo_entry->format); 115 assert(format == fbo_entry->format); 116 xorg_list_del(&fbo_entry->list); 117 ret_fbo = fbo_entry; 118 break; 119 } 120 } 121 122 if (ret_fbo) 123 glamor_priv->fbo_cache_watermark -= ret_fbo->width * ret_fbo->height; 124 125 assert(glamor_priv->fbo_cache_watermark >= 0); 126 127 return ret_fbo; 128#endif 129} 130 131static void 132glamor_purge_fbo(glamor_screen_private *glamor_priv, 133 glamor_pixmap_fbo *fbo) 134{ 135 glamor_make_current(glamor_priv); 136 137 if (fbo->fb) 138 glDeleteFramebuffers(1, &fbo->fb); 139 if (fbo->tex) 140 glDeleteTextures(1, &fbo->tex); 141 142 free(fbo); 143} 144 145static void 146glamor_pixmap_fbo_cache_put(glamor_screen_private *glamor_priv, 147 glamor_pixmap_fbo *fbo) 148{ 149 struct xorg_list *cache; 150 int n_format; 151 152#ifdef NO_FBO_CACHE 153 glamor_purge_fbo(fbo); 154 return; 155#else 156 n_format = cache_format(fbo->format); 157 158 if (fbo->fb == 0 || fbo->external || n_format == -1 159 || glamor_priv->fbo_cache_watermark >= FBO_CACHE_THRESHOLD) { 160 glamor_priv->tick += GLAMOR_CACHE_EXPIRE_MAX; 161 glamor_fbo_expire(glamor_priv); 162 glamor_purge_fbo(glamor_priv, fbo); 163 return; 164 } 165 166 cache = &glamor_priv->fbo_cache[n_format] 167 [cache_wbucket(fbo->width)] 168 [cache_hbucket(fbo->height)]; 169 DEBUGF 170 ("Put cache entry %p to cache %p w %d h %d format %x fbo %d tex %d \n", 171 fbo, cache, fbo->width, fbo->height, fbo->format, fbo->fb, fbo->tex); 172 173 glamor_priv->fbo_cache_watermark += fbo->width * fbo->height; 174 xorg_list_add(&fbo->list, cache); 175 fbo->expire = glamor_priv->tick + GLAMOR_CACHE_EXPIRE_MAX; 176#endif 177} 178 179static int 180glamor_pixmap_ensure_fb(glamor_screen_private *glamor_priv, 181 glamor_pixmap_fbo *fbo) 182{ 183 int status, err = 0; 184 185 glamor_make_current(glamor_priv); 186 187 if (fbo->fb == 0) 188 glGenFramebuffers(1, &fbo->fb); 189 assert(fbo->tex != 0); 190 glBindFramebuffer(GL_FRAMEBUFFER, fbo->fb); 191 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 192 GL_TEXTURE_2D, fbo->tex, 0); 193 status = glCheckFramebufferStatus(GL_FRAMEBUFFER); 194 if (status != GL_FRAMEBUFFER_COMPLETE) { 195 const char *str; 196 197 switch (status) { 198 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 199 str = "incomplete attachment"; 200 break; 201 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 202 str = "incomplete/missing attachment"; 203 break; 204 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: 205 str = "incomplete draw buffer"; 206 break; 207 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: 208 str = "incomplete read buffer"; 209 break; 210 case GL_FRAMEBUFFER_UNSUPPORTED: 211 str = "unsupported"; 212 break; 213 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: 214 str = "incomplete multiple"; 215 break; 216 default: 217 str = "unknown error"; 218 break; 219 } 220 221 glamor_fallback("glamor: Failed to create fbo, %s\n", str); 222 err = -1; 223 } 224 225 return err; 226} 227 228glamor_pixmap_fbo * 229glamor_create_fbo_from_tex(glamor_screen_private *glamor_priv, 230 int w, int h, GLenum format, GLint tex, int flag) 231{ 232 glamor_pixmap_fbo *fbo; 233 234 fbo = calloc(1, sizeof(*fbo)); 235 if (fbo == NULL) 236 return NULL; 237 238 xorg_list_init(&fbo->list); 239 240 fbo->tex = tex; 241 fbo->width = w; 242 fbo->height = h; 243 fbo->external = FALSE; 244 fbo->format = format; 245 246 if (flag == CREATE_PIXMAP_USAGE_SHARED) 247 fbo->external = TRUE; 248 249 if (flag != GLAMOR_CREATE_FBO_NO_FBO) { 250 if (glamor_pixmap_ensure_fb(glamor_priv, fbo) != 0) { 251 glamor_purge_fbo(glamor_priv, fbo); 252 fbo = NULL; 253 } 254 } 255 256 return fbo; 257} 258 259void 260glamor_fbo_expire(glamor_screen_private *glamor_priv) 261{ 262 struct xorg_list *cache; 263 glamor_pixmap_fbo *fbo_entry, *tmp; 264 int i, j, k; 265 266 for (i = 0; i < CACHE_FORMAT_COUNT; i++) 267 for (j = 0; j < CACHE_BUCKET_WCOUNT; j++) 268 for (k = 0; k < CACHE_BUCKET_HCOUNT; k++) { 269 cache = &glamor_priv->fbo_cache[i][j][k]; 270 xorg_list_for_each_entry_safe_reverse(fbo_entry, tmp, cache, 271 list) { 272 if (GLAMOR_TICK_AFTER(fbo_entry->expire, glamor_priv->tick)) { 273 break; 274 } 275 276 glamor_priv->fbo_cache_watermark -= 277 fbo_entry->width * fbo_entry->height; 278 xorg_list_del(&fbo_entry->list); 279 DEBUGF("cache %p fbo %p expired %d current %d \n", cache, 280 fbo_entry, fbo_entry->expire, glamor_priv->tick); 281 glamor_purge_fbo(glamor_priv, fbo_entry); 282 } 283 } 284 285} 286 287void 288glamor_init_pixmap_fbo(ScreenPtr screen) 289{ 290 glamor_screen_private *glamor_priv; 291 int i, j, k; 292 293 glamor_priv = glamor_get_screen_private(screen); 294 for (i = 0; i < CACHE_FORMAT_COUNT; i++) 295 for (j = 0; j < CACHE_BUCKET_WCOUNT; j++) 296 for (k = 0; k < CACHE_BUCKET_HCOUNT; k++) { 297 xorg_list_init(&glamor_priv->fbo_cache[i][j][k]); 298 } 299 glamor_priv->fbo_cache_watermark = 0; 300} 301 302void 303glamor_fini_pixmap_fbo(ScreenPtr screen) 304{ 305 struct xorg_list *cache; 306 glamor_screen_private *glamor_priv; 307 glamor_pixmap_fbo *fbo_entry, *tmp; 308 int i, j, k; 309 310 glamor_priv = glamor_get_screen_private(screen); 311 for (i = 0; i < CACHE_FORMAT_COUNT; i++) 312 for (j = 0; j < CACHE_BUCKET_WCOUNT; j++) 313 for (k = 0; k < CACHE_BUCKET_HCOUNT; k++) { 314 cache = &glamor_priv->fbo_cache[i][j][k]; 315 xorg_list_for_each_entry_safe_reverse(fbo_entry, tmp, cache, 316 list) { 317 xorg_list_del(&fbo_entry->list); 318 glamor_purge_fbo(glamor_priv, fbo_entry); 319 } 320 } 321} 322 323void 324glamor_destroy_fbo(glamor_screen_private *glamor_priv, 325 glamor_pixmap_fbo *fbo) 326{ 327 xorg_list_del(&fbo->list); 328 glamor_pixmap_fbo_cache_put(glamor_priv, fbo); 329 330} 331 332static int 333_glamor_create_tex(glamor_screen_private *glamor_priv, 334 int w, int h, GLenum format) 335{ 336 unsigned int tex; 337 338 glamor_make_current(glamor_priv); 339 glGenTextures(1, &tex); 340 glBindTexture(GL_TEXTURE_2D, tex); 341 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 342 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 343 if (format == glamor_priv->one_channel_format && format == GL_RED) 344 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED); 345 glamor_priv->suppress_gl_out_of_memory_logging = true; 346 glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, 347 format, GL_UNSIGNED_BYTE, NULL); 348 glamor_priv->suppress_gl_out_of_memory_logging = false; 349 350 if (glGetError() == GL_OUT_OF_MEMORY) { 351 if (!glamor_priv->logged_any_fbo_allocation_failure) { 352 LogMessageVerb(X_WARNING, 0, "glamor: Failed to allocate %dx%d " 353 "FBO due to GL_OUT_OF_MEMORY.\n", w, h); 354 LogMessageVerb(X_WARNING, 0, 355 "glamor: Expect reduced performance.\n"); 356 glamor_priv->logged_any_fbo_allocation_failure = true; 357 } 358 glDeleteTextures(1, &tex); 359 return 0; 360 } 361 362 return tex; 363} 364 365glamor_pixmap_fbo * 366glamor_create_fbo(glamor_screen_private *glamor_priv, 367 int w, int h, GLenum format, int flag) 368{ 369 glamor_pixmap_fbo *fbo; 370 GLint tex = 0; 371 372 if (flag == GLAMOR_CREATE_FBO_NO_FBO || flag == CREATE_PIXMAP_USAGE_SHARED) 373 goto new_fbo; 374 375 fbo = glamor_pixmap_fbo_cache_get(glamor_priv, w, h, format); 376 if (fbo) 377 return fbo; 378 new_fbo: 379 tex = _glamor_create_tex(glamor_priv, w, h, format); 380 if (!tex) 381 return NULL; 382 fbo = glamor_create_fbo_from_tex(glamor_priv, w, h, format, tex, flag); 383 384 return fbo; 385} 386 387/** 388 * Create storage for the w * h region, using FBOs of the GL's maximum 389 * supported size. 390 */ 391glamor_pixmap_fbo * 392glamor_create_fbo_array(glamor_screen_private *glamor_priv, 393 int w, int h, GLenum format, int flag, 394 int block_w, int block_h, 395 glamor_pixmap_private *priv) 396{ 397 int block_wcnt; 398 int block_hcnt; 399 glamor_pixmap_fbo **fbo_array; 400 BoxPtr box_array; 401 int i, j; 402 403 priv->block_w = block_w; 404 priv->block_h = block_h; 405 406 block_wcnt = (w + block_w - 1) / block_w; 407 block_hcnt = (h + block_h - 1) / block_h; 408 409 box_array = calloc(block_wcnt * block_hcnt, sizeof(box_array[0])); 410 if (box_array == NULL) 411 return NULL; 412 413 fbo_array = calloc(block_wcnt * block_hcnt, sizeof(glamor_pixmap_fbo *)); 414 if (fbo_array == NULL) { 415 free(box_array); 416 return FALSE; 417 } 418 for (i = 0; i < block_hcnt; i++) { 419 int block_y1, block_y2; 420 int fbo_w, fbo_h; 421 422 block_y1 = i * block_h; 423 block_y2 = (block_y1 + block_h) > h ? h : (block_y1 + block_h); 424 fbo_h = block_y2 - block_y1; 425 426 for (j = 0; j < block_wcnt; j++) { 427 box_array[i * block_wcnt + j].x1 = j * block_w; 428 box_array[i * block_wcnt + j].y1 = block_y1; 429 box_array[i * block_wcnt + j].x2 = 430 (j + 1) * block_w > w ? w : (j + 1) * block_w; 431 box_array[i * block_wcnt + j].y2 = block_y2; 432 fbo_w = 433 box_array[i * block_wcnt + j].x2 - box_array[i * block_wcnt + 434 j].x1; 435 fbo_array[i * block_wcnt + j] = glamor_create_fbo(glamor_priv, 436 fbo_w, fbo_h, 437 format, 438 GLAMOR_CREATE_PIXMAP_FIXUP); 439 if (fbo_array[i * block_wcnt + j] == NULL) 440 goto cleanup; 441 } 442 } 443 444 priv->box = box_array[0]; 445 priv->box_array = box_array; 446 priv->fbo_array = fbo_array; 447 priv->block_wcnt = block_wcnt; 448 priv->block_hcnt = block_hcnt; 449 return fbo_array[0]; 450 451 cleanup: 452 for (i = 0; i < block_wcnt * block_hcnt; i++) 453 if (fbo_array[i]) 454 glamor_destroy_fbo(glamor_priv, fbo_array[i]); 455 free(box_array); 456 free(fbo_array); 457 return NULL; 458} 459 460glamor_pixmap_fbo * 461glamor_pixmap_detach_fbo(glamor_pixmap_private *pixmap_priv) 462{ 463 glamor_pixmap_fbo *fbo; 464 465 if (pixmap_priv == NULL) 466 return NULL; 467 468 fbo = pixmap_priv->fbo; 469 if (fbo == NULL) 470 return NULL; 471 472 pixmap_priv->fbo = NULL; 473 return fbo; 474} 475 476/* The pixmap must not be attached to another fbo. */ 477void 478glamor_pixmap_attach_fbo(PixmapPtr pixmap, glamor_pixmap_fbo *fbo) 479{ 480 glamor_pixmap_private *pixmap_priv; 481 482 pixmap_priv = glamor_get_pixmap_private(pixmap); 483 484 if (pixmap_priv->fbo) 485 return; 486 487 pixmap_priv->fbo = fbo; 488 489 switch (pixmap_priv->type) { 490 case GLAMOR_TEXTURE_ONLY: 491 case GLAMOR_TEXTURE_DRM: 492 pixmap_priv->gl_fbo = GLAMOR_FBO_NORMAL; 493 pixmap->devPrivate.ptr = NULL; 494 default: 495 break; 496 } 497} 498 499void 500glamor_pixmap_destroy_fbo(PixmapPtr pixmap) 501{ 502 ScreenPtr screen = pixmap->drawable.pScreen; 503 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); 504 glamor_pixmap_private *priv = glamor_get_pixmap_private(pixmap); 505 glamor_pixmap_fbo *fbo; 506 507 if (glamor_pixmap_priv_is_large(priv)) { 508 int i; 509 510 for (i = 0; i < priv->block_wcnt * priv->block_hcnt; i++) 511 glamor_destroy_fbo(glamor_priv, priv->fbo_array[i]); 512 free(priv->fbo_array); 513 } 514 else { 515 fbo = glamor_pixmap_detach_fbo(priv); 516 if (fbo) 517 glamor_destroy_fbo(glamor_priv, fbo); 518 } 519} 520 521Bool 522glamor_pixmap_ensure_fbo(PixmapPtr pixmap, GLenum format, int flag) 523{ 524 glamor_screen_private *glamor_priv; 525 glamor_pixmap_private *pixmap_priv; 526 glamor_pixmap_fbo *fbo; 527 528 glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen); 529 pixmap_priv = glamor_get_pixmap_private(pixmap); 530 if (pixmap_priv->fbo == NULL) { 531 532 fbo = glamor_create_fbo(glamor_priv, pixmap->drawable.width, 533 pixmap->drawable.height, format, flag); 534 if (fbo == NULL) 535 return FALSE; 536 537 glamor_pixmap_attach_fbo(pixmap, fbo); 538 } 539 else { 540 /* We do have a fbo, but it may lack of fb or tex. */ 541 if (!pixmap_priv->fbo->tex) 542 pixmap_priv->fbo->tex = 543 _glamor_create_tex(glamor_priv, pixmap->drawable.width, 544 pixmap->drawable.height, format); 545 546 if (flag != GLAMOR_CREATE_FBO_NO_FBO && pixmap_priv->fbo->fb == 0) 547 if (glamor_pixmap_ensure_fb(glamor_priv, pixmap_priv->fbo) != 0) 548 return FALSE; 549 } 550 551 return TRUE; 552} 553 554_X_EXPORT void 555glamor_pixmap_exchange_fbos(PixmapPtr front, PixmapPtr back) 556{ 557 glamor_pixmap_private *front_priv, *back_priv; 558 glamor_pixmap_fbo *temp_fbo; 559 560 front_priv = glamor_get_pixmap_private(front); 561 back_priv = glamor_get_pixmap_private(back); 562 temp_fbo = front_priv->fbo; 563 front_priv->fbo = back_priv->fbo; 564 back_priv->fbo = temp_fbo; 565} 566