1/* 2 * Mesa 3-D graphics library 3 * 4 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included 14 * in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 * OR 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 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25 26/** 27 * Functions for allocating/managing software-based renderbuffers. 28 * Also, routines for reading/writing software-based renderbuffer data as 29 * ubytes, ushorts, uints, etc. 30 */ 31 32 33#include "main/glheader.h" 34#include "main/imports.h" 35#include "main/context.h" 36#include "main/fbobject.h" 37#include "main/formats.h" 38#include "main/mtypes.h" 39#include "main/renderbuffer.h" 40#include "swrast/s_context.h" 41#include "swrast/s_renderbuffer.h" 42 43 44/** 45 * This is a software fallback for the gl_renderbuffer->AllocStorage 46 * function. 47 * Device drivers will typically override this function for the buffers 48 * which it manages (typically color buffers, Z and stencil). 49 * Other buffers (like software accumulation and aux buffers) which the driver 50 * doesn't manage can be handled with this function. 51 * 52 * This one multi-purpose function can allocate stencil, depth, accum, color 53 * or color-index buffers! 54 */ 55static GLboolean 56soft_renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb, 57 GLenum internalFormat, 58 GLuint width, GLuint height) 59{ 60 struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); 61 GLuint bpp; 62 63 switch (internalFormat) { 64 case GL_RGB: 65 case GL_R3_G3_B2: 66 case GL_RGB4: 67 case GL_RGB5: 68 case GL_RGB8: 69 case GL_RGB10: 70 case GL_RGB12: 71 case GL_RGB16: 72 rb->Format = MESA_FORMAT_BGR_UNORM8; 73 break; 74 case GL_RGBA: 75 case GL_RGBA2: 76 case GL_RGBA4: 77 case GL_RGB5_A1: 78 case GL_RGBA8: 79#if 1 80 case GL_RGB10_A2: 81 case GL_RGBA12: 82#endif 83 if (_mesa_little_endian()) 84 rb->Format = MESA_FORMAT_R8G8B8A8_UNORM; 85 else 86 rb->Format = MESA_FORMAT_A8B8G8R8_UNORM; 87 break; 88 case GL_RGBA16: 89 case GL_RGBA16_SNORM: 90 /* for accum buffer */ 91 rb->Format = MESA_FORMAT_RGBA_SNORM16; 92 break; 93 case GL_STENCIL_INDEX: 94 case GL_STENCIL_INDEX1_EXT: 95 case GL_STENCIL_INDEX4_EXT: 96 case GL_STENCIL_INDEX8_EXT: 97 case GL_STENCIL_INDEX16_EXT: 98 rb->Format = MESA_FORMAT_S_UINT8; 99 break; 100 case GL_DEPTH_COMPONENT: 101 case GL_DEPTH_COMPONENT16: 102 rb->Format = MESA_FORMAT_Z_UNORM16; 103 break; 104 case GL_DEPTH_COMPONENT24: 105 rb->Format = MESA_FORMAT_Z24_UNORM_X8_UINT; 106 break; 107 case GL_DEPTH_COMPONENT32: 108 rb->Format = MESA_FORMAT_Z_UNORM32; 109 break; 110 case GL_DEPTH_STENCIL_EXT: 111 case GL_DEPTH24_STENCIL8_EXT: 112 rb->Format = MESA_FORMAT_S8_UINT_Z24_UNORM; 113 break; 114 default: 115 /* unsupported format */ 116 return GL_FALSE; 117 } 118 119 bpp = _mesa_get_format_bytes(rb->Format); 120 121 /* free old buffer storage */ 122 free(srb->Buffer); 123 srb->Buffer = NULL; 124 125 srb->RowStride = width * bpp; 126 127 if (width > 0 && height > 0) { 128 /* allocate new buffer storage */ 129 srb->Buffer = malloc(srb->RowStride * height); 130 131 if (srb->Buffer == NULL) { 132 rb->Width = 0; 133 rb->Height = 0; 134 _mesa_error(ctx, GL_OUT_OF_MEMORY, 135 "software renderbuffer allocation (%d x %d x %d)", 136 width, height, bpp); 137 return GL_FALSE; 138 } 139 } 140 141 rb->Width = width; 142 rb->Height = height; 143 rb->_BaseFormat = _mesa_base_fbo_format(ctx, internalFormat); 144 145 if (rb->Name == 0 && 146 internalFormat == GL_RGBA16_SNORM && 147 rb->_BaseFormat == 0) { 148 /* NOTE: This is a special case just for accumulation buffers. 149 * This is a very limited use case- there's no snorm texturing or 150 * rendering going on. 151 */ 152 rb->_BaseFormat = GL_RGBA; 153 } 154 else { 155 /* the internalFormat should have been error checked long ago */ 156 assert(rb->_BaseFormat); 157 } 158 159 return GL_TRUE; 160} 161 162 163/** 164 * Called via gl_renderbuffer::Delete() 165 */ 166static void 167soft_renderbuffer_delete(struct gl_context *ctx, struct gl_renderbuffer *rb) 168{ 169 struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); 170 171 free(srb->Buffer); 172 srb->Buffer = NULL; 173 _mesa_delete_renderbuffer(ctx, rb); 174} 175 176 177void 178_swrast_map_soft_renderbuffer(struct gl_context *ctx, 179 struct gl_renderbuffer *rb, 180 GLuint x, GLuint y, GLuint w, GLuint h, 181 GLbitfield mode, 182 GLubyte **out_map, 183 GLint *out_stride, 184 bool flip_y) 185{ 186 struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); 187 GLubyte *map = srb->Buffer; 188 int cpp = _mesa_get_format_bytes(rb->Format); 189 int stride = rb->Width * cpp; 190 191 if (!map) { 192 *out_map = NULL; 193 *out_stride = 0; 194 } 195 196 map += y * stride; 197 map += x * cpp; 198 199 *out_map = map; 200 *out_stride = stride; 201} 202 203 204void 205_swrast_unmap_soft_renderbuffer(struct gl_context *ctx, 206 struct gl_renderbuffer *rb) 207{ 208} 209 210 211 212/** 213 * Allocate a software-based renderbuffer. This is called via the 214 * ctx->Driver.NewRenderbuffer() function when the user creates a new 215 * renderbuffer. 216 * This would not be used for hardware-based renderbuffers. 217 */ 218struct gl_renderbuffer * 219_swrast_new_soft_renderbuffer(struct gl_context *ctx, GLuint name) 220{ 221 struct swrast_renderbuffer *srb = CALLOC_STRUCT(swrast_renderbuffer); 222 if (srb) { 223 _mesa_init_renderbuffer(&srb->Base, name); 224 srb->Base.AllocStorage = soft_renderbuffer_storage; 225 srb->Base.Delete = soft_renderbuffer_delete; 226 } 227 return &srb->Base; 228} 229 230 231/** 232 * Add software-based color renderbuffers to the given framebuffer. 233 * This is a helper routine for device drivers when creating a 234 * window system framebuffer (not a user-created render/framebuffer). 235 * Once this function is called, you can basically forget about this 236 * renderbuffer; core Mesa will handle all the buffer management and 237 * rendering! 238 */ 239static GLboolean 240add_color_renderbuffers(struct gl_context *ctx, struct gl_framebuffer *fb, 241 GLuint rgbBits, GLuint alphaBits, 242 GLboolean frontLeft, GLboolean backLeft, 243 GLboolean frontRight, GLboolean backRight) 244{ 245 gl_buffer_index b; 246 247 if (rgbBits > 16 || alphaBits > 16) { 248 _mesa_problem(ctx, 249 "Unsupported bit depth in add_color_renderbuffers"); 250 return GL_FALSE; 251 } 252 253 assert(MAX_COLOR_ATTACHMENTS >= 4); 254 255 for (b = BUFFER_FRONT_LEFT; b <= BUFFER_BACK_RIGHT; b++) { 256 struct gl_renderbuffer *rb; 257 258 if (b == BUFFER_FRONT_LEFT && !frontLeft) 259 continue; 260 else if (b == BUFFER_BACK_LEFT && !backLeft) 261 continue; 262 else if (b == BUFFER_FRONT_RIGHT && !frontRight) 263 continue; 264 else if (b == BUFFER_BACK_RIGHT && !backRight) 265 continue; 266 267 assert(fb->Attachment[b].Renderbuffer == NULL); 268 269 rb = ctx->Driver.NewRenderbuffer(ctx, 0); 270 if (!rb) { 271 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating color buffer"); 272 return GL_FALSE; 273 } 274 275 rb->InternalFormat = GL_RGBA; 276 277 rb->AllocStorage = soft_renderbuffer_storage; 278 _mesa_attach_and_own_rb(fb, b, rb); 279 } 280 281 return GL_TRUE; 282} 283 284 285/** 286 * Add a software-based depth renderbuffer to the given framebuffer. 287 * This is a helper routine for device drivers when creating a 288 * window system framebuffer (not a user-created render/framebuffer). 289 * Once this function is called, you can basically forget about this 290 * renderbuffer; core Mesa will handle all the buffer management and 291 * rendering! 292 */ 293static GLboolean 294add_depth_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb, 295 GLuint depthBits) 296{ 297 struct gl_renderbuffer *rb; 298 299 if (depthBits > 32) { 300 _mesa_problem(ctx, 301 "Unsupported depthBits in add_depth_renderbuffer"); 302 return GL_FALSE; 303 } 304 305 assert(fb->Attachment[BUFFER_DEPTH].Renderbuffer == NULL); 306 307 rb = _swrast_new_soft_renderbuffer(ctx, 0); 308 if (!rb) { 309 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating depth buffer"); 310 return GL_FALSE; 311 } 312 313 if (depthBits <= 16) { 314 rb->InternalFormat = GL_DEPTH_COMPONENT16; 315 } 316 else if (depthBits <= 24) { 317 rb->InternalFormat = GL_DEPTH_COMPONENT24; 318 } 319 else { 320 rb->InternalFormat = GL_DEPTH_COMPONENT32; 321 } 322 323 rb->AllocStorage = soft_renderbuffer_storage; 324 _mesa_attach_and_own_rb(fb, BUFFER_DEPTH, rb); 325 326 return GL_TRUE; 327} 328 329 330/** 331 * Add a software-based stencil renderbuffer to the given framebuffer. 332 * This is a helper routine for device drivers when creating a 333 * window system framebuffer (not a user-created render/framebuffer). 334 * Once this function is called, you can basically forget about this 335 * renderbuffer; core Mesa will handle all the buffer management and 336 * rendering! 337 */ 338static GLboolean 339add_stencil_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb, 340 GLuint stencilBits) 341{ 342 struct gl_renderbuffer *rb; 343 344 if (stencilBits > 16) { 345 _mesa_problem(ctx, 346 "Unsupported stencilBits in add_stencil_renderbuffer"); 347 return GL_FALSE; 348 } 349 350 assert(fb->Attachment[BUFFER_STENCIL].Renderbuffer == NULL); 351 352 rb = _swrast_new_soft_renderbuffer(ctx, 0); 353 if (!rb) { 354 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating stencil buffer"); 355 return GL_FALSE; 356 } 357 358 assert(stencilBits <= 8); 359 rb->InternalFormat = GL_STENCIL_INDEX8; 360 361 rb->AllocStorage = soft_renderbuffer_storage; 362 _mesa_attach_and_own_rb(fb, BUFFER_STENCIL, rb); 363 364 return GL_TRUE; 365} 366 367 368static GLboolean 369add_depth_stencil_renderbuffer(struct gl_context *ctx, 370 struct gl_framebuffer *fb) 371{ 372 struct gl_renderbuffer *rb; 373 374 assert(fb->Attachment[BUFFER_DEPTH].Renderbuffer == NULL); 375 assert(fb->Attachment[BUFFER_STENCIL].Renderbuffer == NULL); 376 377 rb = _swrast_new_soft_renderbuffer(ctx, 0); 378 if (!rb) { 379 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating depth+stencil buffer"); 380 return GL_FALSE; 381 } 382 383 rb->InternalFormat = GL_DEPTH_STENCIL; 384 385 rb->AllocStorage = soft_renderbuffer_storage; 386 _mesa_attach_and_own_rb(fb, BUFFER_DEPTH, rb); 387 _mesa_attach_and_reference_rb(fb, BUFFER_STENCIL, rb); 388 389 return GL_TRUE; 390} 391 392 393/** 394 * Add a software-based accumulation renderbuffer to the given framebuffer. 395 * This is a helper routine for device drivers when creating a 396 * window system framebuffer (not a user-created render/framebuffer). 397 * Once this function is called, you can basically forget about this 398 * renderbuffer; core Mesa will handle all the buffer management and 399 * rendering! 400 */ 401static GLboolean 402add_accum_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb, 403 GLuint redBits, GLuint greenBits, 404 GLuint blueBits, GLuint alphaBits) 405{ 406 struct gl_renderbuffer *rb; 407 408 if (redBits > 16 || greenBits > 16 || blueBits > 16 || alphaBits > 16) { 409 _mesa_problem(ctx, 410 "Unsupported accumBits in add_accum_renderbuffer"); 411 return GL_FALSE; 412 } 413 414 assert(fb->Attachment[BUFFER_ACCUM].Renderbuffer == NULL); 415 416 rb = _swrast_new_soft_renderbuffer(ctx, 0); 417 if (!rb) { 418 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating accum buffer"); 419 return GL_FALSE; 420 } 421 422 rb->InternalFormat = GL_RGBA16_SNORM; 423 rb->AllocStorage = soft_renderbuffer_storage; 424 _mesa_attach_and_own_rb(fb, BUFFER_ACCUM, rb); 425 426 return GL_TRUE; 427} 428 429 430 431/** 432 * Add a software-based aux renderbuffer to the given framebuffer. 433 * This is a helper routine for device drivers when creating a 434 * window system framebuffer (not a user-created render/framebuffer). 435 * Once this function is called, you can basically forget about this 436 * renderbuffer; core Mesa will handle all the buffer management and 437 * rendering! 438 * 439 * NOTE: color-index aux buffers not supported. 440 */ 441static GLboolean 442add_aux_renderbuffers(struct gl_context *ctx, struct gl_framebuffer *fb, 443 GLuint colorBits, GLuint numBuffers) 444{ 445 GLuint i; 446 447 if (colorBits > 16) { 448 _mesa_problem(ctx, 449 "Unsupported colorBits in add_aux_renderbuffers"); 450 return GL_FALSE; 451 } 452 453 assert(numBuffers <= MAX_AUX_BUFFERS); 454 455 for (i = 0; i < numBuffers; i++) { 456 struct gl_renderbuffer *rb = _swrast_new_soft_renderbuffer(ctx, 0); 457 458 assert(fb->Attachment[BUFFER_AUX0 + i].Renderbuffer == NULL); 459 460 if (!rb) { 461 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating aux buffer"); 462 return GL_FALSE; 463 } 464 465 assert (colorBits <= 8); 466 rb->InternalFormat = GL_RGBA; 467 468 rb->AllocStorage = soft_renderbuffer_storage; 469 _mesa_attach_and_own_rb(fb, BUFFER_AUX0 + i, rb); 470 } 471 return GL_TRUE; 472} 473 474 475/** 476 * Create/attach software-based renderbuffers to the given framebuffer. 477 * This is a helper routine for device drivers. Drivers can just as well 478 * call the individual _mesa_add_*_renderbuffer() routines directly. 479 */ 480void 481_swrast_add_soft_renderbuffers(struct gl_framebuffer *fb, 482 GLboolean color, 483 GLboolean depth, 484 GLboolean stencil, 485 GLboolean accum, 486 GLboolean alpha, 487 GLboolean aux) 488{ 489 GLboolean frontLeft = GL_TRUE; 490 GLboolean backLeft = fb->Visual.doubleBufferMode; 491 GLboolean frontRight = fb->Visual.stereoMode; 492 GLboolean backRight = fb->Visual.stereoMode && fb->Visual.doubleBufferMode; 493 494 if (color) { 495 assert(fb->Visual.redBits == fb->Visual.greenBits); 496 assert(fb->Visual.redBits == fb->Visual.blueBits); 497 add_color_renderbuffers(NULL, fb, 498 fb->Visual.redBits, 499 fb->Visual.alphaBits, 500 frontLeft, backLeft, 501 frontRight, backRight); 502 } 503 504#if 0 505 /* This is pretty much for debugging purposes only since there's a perf 506 * hit for using combined depth/stencil in swrast. 507 */ 508 if (depth && fb->Visual.depthBits == 24 && 509 stencil && fb->Visual.stencilBits == 8) { 510 /* use combined depth/stencil buffer */ 511 add_depth_stencil_renderbuffer(NULL, fb); 512 } 513 else 514#else 515 (void) add_depth_stencil_renderbuffer; 516#endif 517 { 518 if (depth) { 519 assert(fb->Visual.depthBits > 0); 520 add_depth_renderbuffer(NULL, fb, fb->Visual.depthBits); 521 } 522 523 if (stencil) { 524 assert(fb->Visual.stencilBits > 0); 525 add_stencil_renderbuffer(NULL, fb, fb->Visual.stencilBits); 526 } 527 } 528 529 if (accum) { 530 assert(fb->Visual.accumRedBits > 0); 531 assert(fb->Visual.accumGreenBits > 0); 532 assert(fb->Visual.accumBlueBits > 0); 533 add_accum_renderbuffer(NULL, fb, 534 fb->Visual.accumRedBits, 535 fb->Visual.accumGreenBits, 536 fb->Visual.accumBlueBits, 537 fb->Visual.accumAlphaBits); 538 } 539 540 if (aux) { 541 assert(fb->Visual.numAuxBuffers > 0); 542 add_aux_renderbuffers(NULL, fb, fb->Visual.redBits, 543 fb->Visual.numAuxBuffers); 544 } 545 546#if 0 547 if (multisample) { 548 /* maybe someday */ 549 } 550#endif 551} 552 553 554 555static void 556map_attachment(struct gl_context *ctx, 557 struct gl_framebuffer *fb, 558 gl_buffer_index buffer) 559{ 560 struct gl_texture_object *texObj = fb->Attachment[buffer].Texture; 561 struct gl_renderbuffer *rb = fb->Attachment[buffer].Renderbuffer; 562 struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); 563 564 if (texObj) { 565 /* map texture image (render to texture) */ 566 const GLuint level = fb->Attachment[buffer].TextureLevel; 567 const GLuint face = fb->Attachment[buffer].CubeMapFace; 568 const GLuint slice = fb->Attachment[buffer].Zoffset; 569 struct gl_texture_image *texImage = texObj->Image[face][level]; 570 if (texImage) { 571 ctx->Driver.MapTextureImage(ctx, texImage, slice, 572 0, 0, texImage->Width, texImage->Height, 573 GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, 574 &srb->Map, &srb->RowStride); 575 } 576 } 577 else if (rb) { 578 /* Map ordinary renderbuffer */ 579 ctx->Driver.MapRenderbuffer(ctx, rb, 580 0, 0, rb->Width, rb->Height, 581 GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, 582 &srb->Map, &srb->RowStride, 583 fb->FlipY); 584 } 585 586 assert(srb->Map); 587} 588 589 590static void 591unmap_attachment(struct gl_context *ctx, 592 struct gl_framebuffer *fb, 593 gl_buffer_index buffer) 594{ 595 struct gl_texture_object *texObj = fb->Attachment[buffer].Texture; 596 struct gl_renderbuffer *rb = fb->Attachment[buffer].Renderbuffer; 597 struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); 598 599 if (texObj) { 600 /* unmap texture image (render to texture) */ 601 const GLuint level = fb->Attachment[buffer].TextureLevel; 602 const GLuint face = fb->Attachment[buffer].CubeMapFace; 603 const GLuint slice = fb->Attachment[buffer].Zoffset; 604 struct gl_texture_image *texImage = texObj->Image[face][level]; 605 if (texImage) { 606 ctx->Driver.UnmapTextureImage(ctx, texImage, slice); 607 } 608 } 609 else if (rb) { 610 /* unmap ordinary renderbuffer */ 611 ctx->Driver.UnmapRenderbuffer(ctx, rb); 612 } 613 614 srb->Map = NULL; 615} 616 617 618/** 619 * Determine what type to use (ubyte vs. float) for span colors for the 620 * given renderbuffer. 621 * See also _swrast_write_rgba_span(). 622 */ 623static void 624find_renderbuffer_colortype(struct gl_renderbuffer *rb) 625{ 626 struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); 627 GLuint rbMaxBits = _mesa_get_format_max_bits(rb->Format); 628 GLenum rbDatatype = _mesa_get_format_datatype(rb->Format); 629 630 if (rbDatatype == GL_UNSIGNED_NORMALIZED && rbMaxBits <= 8) { 631 /* the buffer's values fit in GLubyte values */ 632 srb->ColorType = GL_UNSIGNED_BYTE; 633 } 634 else { 635 /* use floats otherwise */ 636 srb->ColorType = GL_FLOAT; 637 } 638} 639 640 641/** 642 * Map the renderbuffers we'll use for tri/line/point rendering. 643 */ 644void 645_swrast_map_renderbuffers(struct gl_context *ctx) 646{ 647 struct gl_framebuffer *fb = ctx->DrawBuffer; 648 struct gl_renderbuffer *depthRb, *stencilRb; 649 GLuint buf; 650 651 depthRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; 652 if (depthRb) { 653 /* map depth buffer */ 654 map_attachment(ctx, fb, BUFFER_DEPTH); 655 } 656 657 stencilRb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; 658 if (stencilRb && stencilRb != depthRb) { 659 /* map stencil buffer */ 660 map_attachment(ctx, fb, BUFFER_STENCIL); 661 } 662 663 for (buf = 0; buf < fb->_NumColorDrawBuffers; buf++) { 664 if (fb->_ColorDrawBufferIndexes[buf] != BUFFER_NONE) { 665 map_attachment(ctx, fb, fb->_ColorDrawBufferIndexes[buf]); 666 find_renderbuffer_colortype(fb->_ColorDrawBuffers[buf]); 667 } 668 } 669} 670 671 672/** 673 * Unmap renderbuffers after rendering. 674 */ 675void 676_swrast_unmap_renderbuffers(struct gl_context *ctx) 677{ 678 struct gl_framebuffer *fb = ctx->DrawBuffer; 679 struct gl_renderbuffer *depthRb, *stencilRb; 680 GLuint buf; 681 682 depthRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; 683 if (depthRb) { 684 /* map depth buffer */ 685 unmap_attachment(ctx, fb, BUFFER_DEPTH); 686 } 687 688 stencilRb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; 689 if (stencilRb && stencilRb != depthRb) { 690 /* map stencil buffer */ 691 unmap_attachment(ctx, fb, BUFFER_STENCIL); 692 } 693 694 for (buf = 0; buf < fb->_NumColorDrawBuffers; buf++) { 695 if (fb->_ColorDrawBufferIndexes[buf] != BUFFER_NONE) { 696 unmap_attachment(ctx, fb, fb->_ColorDrawBufferIndexes[buf]); 697 } 698 } 699} 700