glamor_fbo.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  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. #include <stdlib.h>
  29. #include "glamor_priv.h"
  30. #define GLAMOR_CACHE_EXPIRE_MAX 100
  31. #define GLAMOR_CACHE_DEFAULT 0
  32. #define GLAMOR_CACHE_EXACT_SIZE 1
  33. //#define NO_FBO_CACHE 1
  34. #define FBO_CACHE_THRESHOLD (256*1024*1024)
  35. /* Loop from the tail to the head. */
  36. #define xorg_list_for_each_entry_reverse(pos, head, member) \
  37. for (pos = __container_of((head)->prev, pos, member); \
  38. &pos->member != (head); \
  39. pos = __container_of(pos->member.prev, pos, member))
  40. #define xorg_list_for_each_entry_safe_reverse(pos, tmp, head, member) \
  41. for (pos = __container_of((head)->prev, pos, member), \
  42. tmp = __container_of(pos->member.prev, pos, member); \
  43. &pos->member != (head); \
  44. pos = tmp, tmp = __container_of(pos->member.prev, tmp, member))
  45. inline static int
  46. cache_wbucket(int size)
  47. {
  48. int order = __fls(size / 32);
  49. if (order >= CACHE_BUCKET_WCOUNT)
  50. order = CACHE_BUCKET_WCOUNT - 1;
  51. return order;
  52. }
  53. inline static int
  54. cache_hbucket(int size)
  55. {
  56. int order = __fls(size / 32);
  57. if (order >= CACHE_BUCKET_HCOUNT)
  58. order = CACHE_BUCKET_HCOUNT - 1;
  59. return order;
  60. }
  61. static glamor_pixmap_fbo *
  62. glamor_pixmap_fbo_cache_get(glamor_screen_private *glamor_priv,
  63. int w, int h, GLenum format, int flag)
  64. {
  65. struct xorg_list *cache;
  66. glamor_pixmap_fbo *fbo_entry, *ret_fbo = NULL;
  67. int n_format;
  68. #ifdef NO_FBO_CACHE
  69. return NULL;
  70. #else
  71. n_format = cache_format(format);
  72. if (n_format == -1)
  73. return NULL;
  74. cache = &glamor_priv->fbo_cache[n_format]
  75. [cache_wbucket(w)]
  76. [cache_hbucket(h)];
  77. if (!(flag & GLAMOR_CACHE_EXACT_SIZE)) {
  78. xorg_list_for_each_entry(fbo_entry, cache, list) {
  79. if (fbo_entry->width >= w && fbo_entry->height >= h) {
  80. DEBUGF("Request w %d h %d format %x \n", w, h, format);
  81. DEBUGF("got cache entry %p w %d h %d fbo %d tex %d format %x\n",
  82. fbo_entry, fbo_entry->width, fbo_entry->height,
  83. fbo_entry->fb, fbo_entry->tex);
  84. xorg_list_del(&fbo_entry->list);
  85. ret_fbo = fbo_entry;
  86. break;
  87. }
  88. }
  89. }
  90. else {
  91. xorg_list_for_each_entry(fbo_entry, cache, list) {
  92. if (fbo_entry->width == w && fbo_entry->height == h) {
  93. DEBUGF("Request w %d h %d format %x \n", w, h, format);
  94. DEBUGF("got cache entry %p w %d h %d fbo %d tex %d format %x\n",
  95. fbo_entry, fbo_entry->width, fbo_entry->height,
  96. fbo_entry->fb, fbo_entry->tex, fbo_entry->format);
  97. assert(format == fbo_entry->format);
  98. xorg_list_del(&fbo_entry->list);
  99. ret_fbo = fbo_entry;
  100. break;
  101. }
  102. }
  103. }
  104. if (ret_fbo)
  105. glamor_priv->fbo_cache_watermark -= ret_fbo->width * ret_fbo->height;
  106. assert(glamor_priv->fbo_cache_watermark >= 0);
  107. return ret_fbo;
  108. #endif
  109. }
  110. void
  111. glamor_purge_fbo(glamor_pixmap_fbo *fbo)
  112. {
  113. glamor_make_current(fbo->glamor_priv);
  114. if (fbo->fb)
  115. glDeleteFramebuffers(1, &fbo->fb);
  116. if (fbo->tex)
  117. glDeleteTextures(1, &fbo->tex);
  118. if (fbo->pbo)
  119. glDeleteBuffers(1, &fbo->pbo);
  120. free(fbo);
  121. }
  122. static void
  123. glamor_pixmap_fbo_cache_put(glamor_pixmap_fbo *fbo)
  124. {
  125. struct xorg_list *cache;
  126. int n_format;
  127. #ifdef NO_FBO_CACHE
  128. glamor_purge_fbo(fbo);
  129. return;
  130. #else
  131. n_format = cache_format(fbo->format);
  132. if (fbo->fb == 0 || n_format == -1
  133. || fbo->glamor_priv->fbo_cache_watermark >= FBO_CACHE_THRESHOLD) {
  134. fbo->glamor_priv->tick += GLAMOR_CACHE_EXPIRE_MAX;
  135. glamor_fbo_expire(fbo->glamor_priv);
  136. glamor_purge_fbo(fbo);
  137. return;
  138. }
  139. cache = &fbo->glamor_priv->fbo_cache[n_format]
  140. [cache_wbucket(fbo->width)]
  141. [cache_hbucket(fbo->height)];
  142. DEBUGF
  143. ("Put cache entry %p to cache %p w %d h %d format %x fbo %d tex %d \n",
  144. fbo, cache, fbo->width, fbo->height, fbo->format, fbo->fb, fbo->tex);
  145. fbo->glamor_priv->fbo_cache_watermark += fbo->width * fbo->height;
  146. xorg_list_add(&fbo->list, cache);
  147. fbo->expire = fbo->glamor_priv->tick + GLAMOR_CACHE_EXPIRE_MAX;
  148. #endif
  149. }
  150. static int
  151. glamor_pixmap_ensure_fb(glamor_pixmap_fbo *fbo)
  152. {
  153. int status, err = 0;
  154. glamor_make_current(fbo->glamor_priv);
  155. if (fbo->fb == 0)
  156. glGenFramebuffers(1, &fbo->fb);
  157. assert(fbo->tex != 0);
  158. glBindFramebuffer(GL_FRAMEBUFFER, fbo->fb);
  159. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  160. GL_TEXTURE_2D, fbo->tex, 0);
  161. status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  162. if (status != GL_FRAMEBUFFER_COMPLETE) {
  163. const char *str;
  164. switch (status) {
  165. case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
  166. str = "incomplete attachment";
  167. break;
  168. case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
  169. str = "incomplete/missing attachment";
  170. break;
  171. case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
  172. str = "incomplete draw buffer";
  173. break;
  174. case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
  175. str = "incomplete read buffer";
  176. break;
  177. case GL_FRAMEBUFFER_UNSUPPORTED:
  178. str = "unsupported";
  179. break;
  180. case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
  181. str = "incomplete multiple";
  182. break;
  183. default:
  184. str = "unknown error";
  185. break;
  186. }
  187. glamor_fallback("glamor: Failed to create fbo, %s\n", str);
  188. err = -1;
  189. }
  190. return err;
  191. }
  192. glamor_pixmap_fbo *
  193. glamor_create_fbo_from_tex(glamor_screen_private *glamor_priv,
  194. int w, int h, GLenum format, GLint tex, int flag)
  195. {
  196. glamor_pixmap_fbo *fbo;
  197. fbo = calloc(1, sizeof(*fbo));
  198. if (fbo == NULL)
  199. return NULL;
  200. xorg_list_init(&fbo->list);
  201. fbo->tex = tex;
  202. fbo->width = w;
  203. fbo->height = h;
  204. fbo->format = format;
  205. fbo->glamor_priv = glamor_priv;
  206. if (flag == GLAMOR_CREATE_PIXMAP_MAP) {
  207. glamor_make_current(glamor_priv);
  208. glGenBuffers(1, &fbo->pbo);
  209. goto done;
  210. }
  211. if (flag != GLAMOR_CREATE_FBO_NO_FBO) {
  212. if (glamor_pixmap_ensure_fb(fbo) != 0) {
  213. glamor_purge_fbo(fbo);
  214. fbo = NULL;
  215. }
  216. }
  217. done:
  218. return fbo;
  219. }
  220. void
  221. glamor_fbo_expire(glamor_screen_private *glamor_priv)
  222. {
  223. struct xorg_list *cache;
  224. glamor_pixmap_fbo *fbo_entry, *tmp;
  225. int i, j, k;
  226. for (i = 0; i < CACHE_FORMAT_COUNT; i++)
  227. for (j = 0; j < CACHE_BUCKET_WCOUNT; j++)
  228. for (k = 0; k < CACHE_BUCKET_HCOUNT; k++) {
  229. cache = &glamor_priv->fbo_cache[i][j][k];
  230. xorg_list_for_each_entry_safe_reverse(fbo_entry, tmp, cache,
  231. list) {
  232. if (GLAMOR_TICK_AFTER(fbo_entry->expire, glamor_priv->tick)) {
  233. break;
  234. }
  235. glamor_priv->fbo_cache_watermark -=
  236. fbo_entry->width * fbo_entry->height;
  237. xorg_list_del(&fbo_entry->list);
  238. DEBUGF("cache %p fbo %p expired %d current %d \n", cache,
  239. fbo_entry, fbo_entry->expire, glamor_priv->tick);
  240. glamor_purge_fbo(fbo_entry);
  241. }
  242. }
  243. }
  244. void
  245. glamor_init_pixmap_fbo(ScreenPtr screen)
  246. {
  247. glamor_screen_private *glamor_priv;
  248. int i, j, k;
  249. glamor_priv = glamor_get_screen_private(screen);
  250. for (i = 0; i < CACHE_FORMAT_COUNT; i++)
  251. for (j = 0; j < CACHE_BUCKET_WCOUNT; j++)
  252. for (k = 0; k < CACHE_BUCKET_HCOUNT; k++) {
  253. xorg_list_init(&glamor_priv->fbo_cache[i][j][k]);
  254. }
  255. glamor_priv->fbo_cache_watermark = 0;
  256. }
  257. void
  258. glamor_fini_pixmap_fbo(ScreenPtr screen)
  259. {
  260. struct xorg_list *cache;
  261. glamor_screen_private *glamor_priv;
  262. glamor_pixmap_fbo *fbo_entry, *tmp;
  263. int i, j, k;
  264. glamor_priv = glamor_get_screen_private(screen);
  265. for (i = 0; i < CACHE_FORMAT_COUNT; i++)
  266. for (j = 0; j < CACHE_BUCKET_WCOUNT; j++)
  267. for (k = 0; k < CACHE_BUCKET_HCOUNT; k++) {
  268. cache = &glamor_priv->fbo_cache[i][j][k];
  269. xorg_list_for_each_entry_safe_reverse(fbo_entry, tmp, cache,
  270. list) {
  271. xorg_list_del(&fbo_entry->list);
  272. glamor_purge_fbo(fbo_entry);
  273. }
  274. }
  275. }
  276. void
  277. glamor_destroy_fbo(glamor_pixmap_fbo *fbo)
  278. {
  279. xorg_list_del(&fbo->list);
  280. glamor_pixmap_fbo_cache_put(fbo);
  281. }
  282. static int
  283. _glamor_create_tex(glamor_screen_private *glamor_priv,
  284. int w, int h, GLenum format)
  285. {
  286. unsigned int tex = 0;
  287. /* With dri3, we want to allocate ARGB8888 pixmaps only.
  288. * Depending on the implementation, GL_RGBA might not
  289. * give us ARGB8888. We ask glamor_egl to use get
  290. * an ARGB8888 based texture for us. */
  291. if (glamor_priv->dri3_enabled && format == GL_RGBA) {
  292. tex = glamor_egl_create_argb8888_based_texture(glamor_priv->screen,
  293. w, h);
  294. }
  295. if (!tex) {
  296. glamor_make_current(glamor_priv);
  297. glGenTextures(1, &tex);
  298. glBindTexture(GL_TEXTURE_2D, tex);
  299. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
  300. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  301. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  302. glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0,
  303. format, GL_UNSIGNED_BYTE, NULL);
  304. }
  305. return tex;
  306. }
  307. glamor_pixmap_fbo *
  308. glamor_create_fbo(glamor_screen_private *glamor_priv,
  309. int w, int h, GLenum format, int flag)
  310. {
  311. glamor_pixmap_fbo *fbo;
  312. GLint tex = 0;
  313. int cache_flag;
  314. if (flag == GLAMOR_CREATE_FBO_NO_FBO)
  315. goto new_fbo;
  316. if (flag == GLAMOR_CREATE_PIXMAP_MAP)
  317. goto no_tex;
  318. /* Tiling from textures requires exact pixmap sizes. As we don't
  319. * know which pixmaps will be used as tiles, just allocate
  320. * everything at the requested size
  321. */
  322. cache_flag = GLAMOR_CACHE_EXACT_SIZE;
  323. fbo = glamor_pixmap_fbo_cache_get(glamor_priv, w, h, format, cache_flag);
  324. if (fbo)
  325. return fbo;
  326. new_fbo:
  327. tex = _glamor_create_tex(glamor_priv, w, h, format);
  328. no_tex:
  329. fbo = glamor_create_fbo_from_tex(glamor_priv, w, h, format, tex, flag);
  330. return fbo;
  331. }
  332. static glamor_pixmap_fbo *
  333. _glamor_create_fbo_array(glamor_screen_private *glamor_priv,
  334. int w, int h, GLenum format, int flag,
  335. int block_w, int block_h,
  336. glamor_pixmap_private *pixmap_priv, int has_fbo)
  337. {
  338. int block_wcnt;
  339. int block_hcnt;
  340. glamor_pixmap_fbo **fbo_array;
  341. BoxPtr box_array;
  342. int i, j;
  343. glamor_pixmap_private_large_t *priv;
  344. priv = &pixmap_priv->large;
  345. block_wcnt = (w + block_w - 1) / block_w;
  346. block_hcnt = (h + block_h - 1) / block_h;
  347. box_array = calloc(block_wcnt * block_hcnt, sizeof(box_array[0]));
  348. if (box_array == NULL)
  349. return NULL;
  350. fbo_array = calloc(block_wcnt * block_hcnt, sizeof(glamor_pixmap_fbo *));
  351. if (fbo_array == NULL) {
  352. free(box_array);
  353. return FALSE;
  354. }
  355. for (i = 0; i < block_hcnt; i++) {
  356. int block_y1, block_y2;
  357. int fbo_w, fbo_h;
  358. block_y1 = i * block_h;
  359. block_y2 = (block_y1 + block_h) > h ? h : (block_y1 + block_h);
  360. fbo_h = block_y2 - block_y1;
  361. for (j = 0; j < block_wcnt; j++) {
  362. box_array[i * block_wcnt + j].x1 = j * block_w;
  363. box_array[i * block_wcnt + j].y1 = block_y1;
  364. box_array[i * block_wcnt + j].x2 =
  365. (j + 1) * block_w > w ? w : (j + 1) * block_w;
  366. box_array[i * block_wcnt + j].y2 = block_y2;
  367. fbo_w =
  368. box_array[i * block_wcnt + j].x2 - box_array[i * block_wcnt +
  369. j].x1;
  370. if (!has_fbo)
  371. fbo_array[i * block_wcnt + j] = glamor_create_fbo(glamor_priv,
  372. fbo_w, fbo_h,
  373. format,
  374. GLAMOR_CREATE_PIXMAP_FIXUP);
  375. else
  376. fbo_array[i * block_wcnt + j] = priv->base.fbo;
  377. if (fbo_array[i * block_wcnt + j] == NULL)
  378. goto cleanup;
  379. }
  380. }
  381. priv->box = box_array[0];
  382. priv->box_array = box_array;
  383. priv->fbo_array = fbo_array;
  384. priv->block_wcnt = block_wcnt;
  385. priv->block_hcnt = block_hcnt;
  386. return fbo_array[0];
  387. cleanup:
  388. for (i = 0; i < block_wcnt * block_hcnt; i++)
  389. if ((fbo_array)[i])
  390. glamor_destroy_fbo((fbo_array)[i]);
  391. free(box_array);
  392. free(fbo_array);
  393. return NULL;
  394. }
  395. /* Create a fbo array to cover the w*h region, by using block_w*block_h
  396. * block.*/
  397. glamor_pixmap_fbo *
  398. glamor_create_fbo_array(glamor_screen_private *glamor_priv,
  399. int w, int h, GLenum format, int flag,
  400. int block_w, int block_h,
  401. glamor_pixmap_private *pixmap_priv)
  402. {
  403. pixmap_priv->large.block_w = block_w;
  404. pixmap_priv->large.block_h = block_h;
  405. return _glamor_create_fbo_array(glamor_priv, w, h, format, flag,
  406. block_w, block_h, pixmap_priv, 0);
  407. }
  408. glamor_pixmap_fbo *
  409. glamor_pixmap_detach_fbo(glamor_pixmap_private *pixmap_priv)
  410. {
  411. glamor_pixmap_fbo *fbo;
  412. if (pixmap_priv == NULL)
  413. return NULL;
  414. fbo = pixmap_priv->base.fbo;
  415. if (fbo == NULL)
  416. return NULL;
  417. pixmap_priv->base.fbo = NULL;
  418. return fbo;
  419. }
  420. /* The pixmap must not be attached to another fbo. */
  421. void
  422. glamor_pixmap_attach_fbo(PixmapPtr pixmap, glamor_pixmap_fbo *fbo)
  423. {
  424. glamor_pixmap_private *pixmap_priv;
  425. pixmap_priv = glamor_get_pixmap_private(pixmap);
  426. if (pixmap_priv->base.fbo)
  427. return;
  428. pixmap_priv->base.fbo = fbo;
  429. switch (pixmap_priv->type) {
  430. case GLAMOR_TEXTURE_LARGE:
  431. case GLAMOR_TEXTURE_ONLY:
  432. case GLAMOR_TEXTURE_DRM:
  433. pixmap_priv->base.gl_fbo = GLAMOR_FBO_NORMAL;
  434. if (fbo->tex != 0)
  435. pixmap_priv->base.gl_tex = 1;
  436. else {
  437. /* XXX For the Xephyr only, may be broken now. */
  438. pixmap_priv->base.gl_tex = 0;
  439. }
  440. case GLAMOR_MEMORY_MAP:
  441. pixmap->devPrivate.ptr = NULL;
  442. break;
  443. default:
  444. break;
  445. }
  446. }
  447. void
  448. glamor_pixmap_destroy_fbo(glamor_pixmap_private *priv)
  449. {
  450. glamor_pixmap_fbo *fbo;
  451. if (priv->type == GLAMOR_TEXTURE_LARGE) {
  452. int i;
  453. glamor_pixmap_private_large_t *large = &priv->large;
  454. for (i = 0; i < large->block_wcnt * large->block_hcnt; i++)
  455. glamor_destroy_fbo(large->fbo_array[i]);
  456. free(large->fbo_array);
  457. }
  458. else {
  459. fbo = glamor_pixmap_detach_fbo(priv);
  460. if (fbo)
  461. glamor_destroy_fbo(fbo);
  462. }
  463. free(priv);
  464. }
  465. Bool
  466. glamor_pixmap_ensure_fbo(PixmapPtr pixmap, GLenum format, int flag)
  467. {
  468. glamor_screen_private *glamor_priv;
  469. glamor_pixmap_private *pixmap_priv;
  470. glamor_pixmap_fbo *fbo;
  471. glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen);
  472. pixmap_priv = glamor_get_pixmap_private(pixmap);
  473. if (pixmap_priv->base.fbo == NULL) {
  474. fbo = glamor_create_fbo(glamor_priv, pixmap->drawable.width,
  475. pixmap->drawable.height, format, flag);
  476. if (fbo == NULL)
  477. return FALSE;
  478. glamor_pixmap_attach_fbo(pixmap, fbo);
  479. }
  480. else {
  481. /* We do have a fbo, but it may lack of fb or tex. */
  482. if (!pixmap_priv->base.fbo->tex)
  483. pixmap_priv->base.fbo->tex =
  484. _glamor_create_tex(glamor_priv, pixmap->drawable.width,
  485. pixmap->drawable.height, format);
  486. if (flag != GLAMOR_CREATE_FBO_NO_FBO && pixmap_priv->base.fbo->fb == 0)
  487. if (glamor_pixmap_ensure_fb(pixmap_priv->base.fbo) != 0)
  488. return FALSE;
  489. }
  490. return TRUE;
  491. }
  492. _X_EXPORT void
  493. glamor_pixmap_exchange_fbos(PixmapPtr front, PixmapPtr back)
  494. {
  495. glamor_pixmap_private *front_priv, *back_priv;
  496. glamor_pixmap_fbo *temp_fbo;
  497. front_priv = glamor_get_pixmap_private(front);
  498. back_priv = glamor_get_pixmap_private(back);
  499. temp_fbo = front_priv->base.fbo;
  500. front_priv->base.fbo = back_priv->base.fbo;
  501. back_priv->base.fbo = temp_fbo;
  502. }