1/* 2 * Mesa 3-D graphics library 3 * Version: 6.5 4 * 5 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 * CONNECTION WITH THE SOFTWARE OR THE USE OR 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_RGB888; 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_RGBA8888_REV; 85 else 86 rb->Format = MESA_FORMAT_RGBA8888; 87 break; 88 case GL_RGBA16: 89 case GL_RGBA16_SNORM: 90 /* for accum buffer */ 91 rb->Format = MESA_FORMAT_SIGNED_RGBA_16; 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_S8; 99 break; 100 case GL_DEPTH_COMPONENT: 101 case GL_DEPTH_COMPONENT16: 102 rb->Format = MESA_FORMAT_Z16; 103 break; 104 case GL_DEPTH_COMPONENT24: 105 rb->Format = MESA_FORMAT_X8_Z24; 106 break; 107 case GL_DEPTH_COMPONENT32: 108 rb->Format = MESA_FORMAT_Z32; 109 break; 110 case GL_DEPTH_STENCIL_EXT: 111 case GL_DEPTH24_STENCIL8_EXT: 112 rb->Format = MESA_FORMAT_Z24_S8; 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 if (srb->Buffer) { 123 free(srb->Buffer); 124 srb->Buffer = NULL; 125 } 126 127 srb->RowStride = width * bpp; 128 129 if (width > 0 && height > 0) { 130 /* allocate new buffer storage */ 131 srb->Buffer = malloc(srb->RowStride * height); 132 133 if (srb->Buffer == NULL) { 134 rb->Width = 0; 135 rb->Height = 0; 136 _mesa_error(ctx, GL_OUT_OF_MEMORY, 137 "software renderbuffer allocation (%d x %d x %d)", 138 width, height, bpp); 139 return GL_FALSE; 140 } 141 } 142 143 rb->Width = width; 144 rb->Height = height; 145 rb->_BaseFormat = _mesa_base_fbo_format(ctx, internalFormat); 146 147 if (rb->Name == 0 && 148 internalFormat == GL_RGBA16_SNORM && 149 rb->_BaseFormat == 0) { 150 /* NOTE: This is a special case just for accumulation buffers. 151 * This is a very limited use case- there's no snorm texturing or 152 * rendering going on. 153 */ 154 rb->_BaseFormat = GL_RGBA; 155 } 156 else { 157 /* the internalFormat should have been error checked long ago */ 158 ASSERT(rb->_BaseFormat); 159 } 160 161 return GL_TRUE; 162} 163 164 165/** 166 * Called via gl_renderbuffer::Delete() 167 */ 168static void 169soft_renderbuffer_delete(struct gl_context *ctx, struct gl_renderbuffer *rb) 170{ 171 struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); 172 173 free(srb->Buffer); 174 srb->Buffer = NULL; 175 _mesa_delete_renderbuffer(ctx, rb); 176} 177 178 179void 180_swrast_map_soft_renderbuffer(struct gl_context *ctx, 181 struct gl_renderbuffer *rb, 182 GLuint x, GLuint y, GLuint w, GLuint h, 183 GLbitfield mode, 184 GLubyte **out_map, 185 GLint *out_stride) 186{ 187 struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); 188 GLubyte *map = srb->Buffer; 189 int cpp = _mesa_get_format_bytes(rb->Format); 190 int stride = rb->Width * cpp; 191 192 if (!map) { 193 *out_map = NULL; 194 *out_stride = 0; 195 } 196 197 map += y * stride; 198 map += x * cpp; 199 200 *out_map = map; 201 *out_stride = stride; 202} 203 204 205void 206_swrast_unmap_soft_renderbuffer(struct gl_context *ctx, 207 struct gl_renderbuffer *rb) 208{ 209} 210 211 212 213/** 214 * Allocate a software-based renderbuffer. This is called via the 215 * ctx->Driver.NewRenderbuffer() function when the user creates a new 216 * renderbuffer. 217 * This would not be used for hardware-based renderbuffers. 218 */ 219struct gl_renderbuffer * 220_swrast_new_soft_renderbuffer(struct gl_context *ctx, GLuint name) 221{ 222 struct swrast_renderbuffer *srb = CALLOC_STRUCT(swrast_renderbuffer); 223 if (srb) { 224 _mesa_init_renderbuffer(&srb->Base, name); 225 srb->Base.AllocStorage = soft_renderbuffer_storage; 226 srb->Base.Delete = soft_renderbuffer_delete; 227 } 228 return &srb->Base; 229} 230 231 232/** 233 * Add software-based color renderbuffers to the given framebuffer. 234 * This is a helper routine for device drivers when creating a 235 * window system framebuffer (not a user-created render/framebuffer). 236 * Once this function is called, you can basically forget about this 237 * renderbuffer; core Mesa will handle all the buffer management and 238 * rendering! 239 */ 240static GLboolean 241add_color_renderbuffers(struct gl_context *ctx, struct gl_framebuffer *fb, 242 GLuint rgbBits, GLuint alphaBits, 243 GLboolean frontLeft, GLboolean backLeft, 244 GLboolean frontRight, GLboolean backRight) 245{ 246 gl_buffer_index b; 247 248 if (rgbBits > 16 || alphaBits > 16) { 249 _mesa_problem(ctx, 250 "Unsupported bit depth in add_color_renderbuffers"); 251 return GL_FALSE; 252 } 253 254 assert(MAX_COLOR_ATTACHMENTS >= 4); 255 256 for (b = BUFFER_FRONT_LEFT; b <= BUFFER_BACK_RIGHT; b++) { 257 struct gl_renderbuffer *rb; 258 259 if (b == BUFFER_FRONT_LEFT && !frontLeft) 260 continue; 261 else if (b == BUFFER_BACK_LEFT && !backLeft) 262 continue; 263 else if (b == BUFFER_FRONT_RIGHT && !frontRight) 264 continue; 265 else if (b == BUFFER_BACK_RIGHT && !backRight) 266 continue; 267 268 assert(fb->Attachment[b].Renderbuffer == NULL); 269 270 rb = ctx->Driver.NewRenderbuffer(ctx, 0); 271 if (!rb) { 272 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating color buffer"); 273 return GL_FALSE; 274 } 275 276 rb->InternalFormat = GL_RGBA; 277 278 rb->AllocStorage = soft_renderbuffer_storage; 279 _mesa_add_renderbuffer(fb, b, rb); 280 } 281 282 return GL_TRUE; 283} 284 285 286/** 287 * Add a software-based depth renderbuffer to the given framebuffer. 288 * This is a helper routine for device drivers when creating a 289 * window system framebuffer (not a user-created render/framebuffer). 290 * Once this function is called, you can basically forget about this 291 * renderbuffer; core Mesa will handle all the buffer management and 292 * rendering! 293 */ 294static GLboolean 295add_depth_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb, 296 GLuint depthBits) 297{ 298 struct gl_renderbuffer *rb; 299 300 if (depthBits > 32) { 301 _mesa_problem(ctx, 302 "Unsupported depthBits in add_depth_renderbuffer"); 303 return GL_FALSE; 304 } 305 306 assert(fb->Attachment[BUFFER_DEPTH].Renderbuffer == NULL); 307 308 rb = _swrast_new_soft_renderbuffer(ctx, 0); 309 if (!rb) { 310 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating depth buffer"); 311 return GL_FALSE; 312 } 313 314 if (depthBits <= 16) { 315 rb->InternalFormat = GL_DEPTH_COMPONENT16; 316 } 317 else if (depthBits <= 24) { 318 rb->InternalFormat = GL_DEPTH_COMPONENT24; 319 } 320 else { 321 rb->InternalFormat = GL_DEPTH_COMPONENT32; 322 } 323 324 rb->AllocStorage = soft_renderbuffer_storage; 325 _mesa_add_renderbuffer(fb, BUFFER_DEPTH, rb); 326 327 return GL_TRUE; 328} 329 330 331/** 332 * Add a software-based stencil renderbuffer to the given framebuffer. 333 * This is a helper routine for device drivers when creating a 334 * window system framebuffer (not a user-created render/framebuffer). 335 * Once this function is called, you can basically forget about this 336 * renderbuffer; core Mesa will handle all the buffer management and 337 * rendering! 338 */ 339static GLboolean 340add_stencil_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb, 341 GLuint stencilBits) 342{ 343 struct gl_renderbuffer *rb; 344 345 if (stencilBits > 16) { 346 _mesa_problem(ctx, 347 "Unsupported stencilBits in add_stencil_renderbuffer"); 348 return GL_FALSE; 349 } 350 351 assert(fb->Attachment[BUFFER_STENCIL].Renderbuffer == NULL); 352 353 rb = _swrast_new_soft_renderbuffer(ctx, 0); 354 if (!rb) { 355 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating stencil buffer"); 356 return GL_FALSE; 357 } 358 359 assert(stencilBits <= 8); 360 rb->InternalFormat = GL_STENCIL_INDEX8; 361 362 rb->AllocStorage = soft_renderbuffer_storage; 363 _mesa_add_renderbuffer(fb, BUFFER_STENCIL, rb); 364 365 return GL_TRUE; 366} 367 368 369static GLboolean 370add_depth_stencil_renderbuffer(struct gl_context *ctx, 371 struct gl_framebuffer *fb) 372{ 373 struct gl_renderbuffer *rb; 374 375 assert(fb->Attachment[BUFFER_DEPTH].Renderbuffer == NULL); 376 assert(fb->Attachment[BUFFER_STENCIL].Renderbuffer == NULL); 377 378 rb = _swrast_new_soft_renderbuffer(ctx, 0); 379 if (!rb) { 380 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating depth+stencil buffer"); 381 return GL_FALSE; 382 } 383 384 rb->InternalFormat = GL_DEPTH_STENCIL; 385 386 rb->AllocStorage = soft_renderbuffer_storage; 387 _mesa_add_renderbuffer(fb, BUFFER_DEPTH, rb); 388 _mesa_add_renderbuffer(fb, BUFFER_STENCIL, rb); 389 390 return GL_TRUE; 391} 392 393 394/** 395 * Add a software-based accumulation renderbuffer to the given framebuffer. 396 * This is a helper routine for device drivers when creating a 397 * window system framebuffer (not a user-created render/framebuffer). 398 * Once this function is called, you can basically forget about this 399 * renderbuffer; core Mesa will handle all the buffer management and 400 * rendering! 401 */ 402static GLboolean 403add_accum_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb, 404 GLuint redBits, GLuint greenBits, 405 GLuint blueBits, GLuint alphaBits) 406{ 407 struct gl_renderbuffer *rb; 408 409 if (redBits > 16 || greenBits > 16 || blueBits > 16 || alphaBits > 16) { 410 _mesa_problem(ctx, 411 "Unsupported accumBits in add_accum_renderbuffer"); 412 return GL_FALSE; 413 } 414 415 assert(fb->Attachment[BUFFER_ACCUM].Renderbuffer == NULL); 416 417 rb = _swrast_new_soft_renderbuffer(ctx, 0); 418 if (!rb) { 419 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating accum buffer"); 420 return GL_FALSE; 421 } 422 423 rb->InternalFormat = GL_RGBA16_SNORM; 424 rb->AllocStorage = soft_renderbuffer_storage; 425 _mesa_add_renderbuffer(fb, BUFFER_ACCUM, rb); 426 427 return GL_TRUE; 428} 429 430 431 432/** 433 * Add a software-based aux renderbuffer to the given framebuffer. 434 * This is a helper routine for device drivers when creating a 435 * window system framebuffer (not a user-created render/framebuffer). 436 * Once this function is called, you can basically forget about this 437 * renderbuffer; core Mesa will handle all the buffer management and 438 * rendering! 439 * 440 * NOTE: color-index aux buffers not supported. 441 */ 442static GLboolean 443add_aux_renderbuffers(struct gl_context *ctx, struct gl_framebuffer *fb, 444 GLuint colorBits, GLuint numBuffers) 445{ 446 GLuint i; 447 448 if (colorBits > 16) { 449 _mesa_problem(ctx, 450 "Unsupported colorBits in add_aux_renderbuffers"); 451 return GL_FALSE; 452 } 453 454 assert(numBuffers <= MAX_AUX_BUFFERS); 455 456 for (i = 0; i < numBuffers; i++) { 457 struct gl_renderbuffer *rb = _swrast_new_soft_renderbuffer(ctx, 0); 458 459 assert(fb->Attachment[BUFFER_AUX0 + i].Renderbuffer == NULL); 460 461 if (!rb) { 462 _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating aux buffer"); 463 return GL_FALSE; 464 } 465 466 assert (colorBits <= 8); 467 rb->InternalFormat = GL_RGBA; 468 469 rb->AllocStorage = soft_renderbuffer_storage; 470 _mesa_add_renderbuffer(fb, BUFFER_AUX0 + i, rb); 471 } 472 return GL_TRUE; 473} 474 475 476/** 477 * Create/attach software-based renderbuffers to the given framebuffer. 478 * This is a helper routine for device drivers. Drivers can just as well 479 * call the individual _mesa_add_*_renderbuffer() routines directly. 480 */ 481void 482_swrast_add_soft_renderbuffers(struct gl_framebuffer *fb, 483 GLboolean color, 484 GLboolean depth, 485 GLboolean stencil, 486 GLboolean accum, 487 GLboolean alpha, 488 GLboolean aux) 489{ 490 GLboolean frontLeft = GL_TRUE; 491 GLboolean backLeft = fb->Visual.doubleBufferMode; 492 GLboolean frontRight = fb->Visual.stereoMode; 493 GLboolean backRight = fb->Visual.stereoMode && fb->Visual.doubleBufferMode; 494 495 if (color) { 496 assert(fb->Visual.redBits == fb->Visual.greenBits); 497 assert(fb->Visual.redBits == fb->Visual.blueBits); 498 add_color_renderbuffers(NULL, fb, 499 fb->Visual.redBits, 500 fb->Visual.alphaBits, 501 frontLeft, backLeft, 502 frontRight, backRight); 503 } 504 505#if 0 506 /* This is pretty much for debugging purposes only since there's a perf 507 * hit for using combined depth/stencil in swrast. 508 */ 509 if (depth && fb->Visual.depthBits == 24 && 510 stencil && fb->Visual.stencilBits == 8) { 511 /* use combined depth/stencil buffer */ 512 add_depth_stencil_renderbuffer(NULL, fb); 513 } 514 else 515#else 516 (void) add_depth_stencil_renderbuffer; 517#endif 518 { 519 if (depth) { 520 assert(fb->Visual.depthBits > 0); 521 add_depth_renderbuffer(NULL, fb, fb->Visual.depthBits); 522 } 523 524 if (stencil) { 525 assert(fb->Visual.stencilBits > 0); 526 add_stencil_renderbuffer(NULL, fb, fb->Visual.stencilBits); 527 } 528 } 529 530 if (accum) { 531 assert(fb->Visual.accumRedBits > 0); 532 assert(fb->Visual.accumGreenBits > 0); 533 assert(fb->Visual.accumBlueBits > 0); 534 add_accum_renderbuffer(NULL, fb, 535 fb->Visual.accumRedBits, 536 fb->Visual.accumGreenBits, 537 fb->Visual.accumBlueBits, 538 fb->Visual.accumAlphaBits); 539 } 540 541 if (aux) { 542 assert(fb->Visual.numAuxBuffers > 0); 543 add_aux_renderbuffers(NULL, fb, fb->Visual.redBits, 544 fb->Visual.numAuxBuffers); 545 } 546 547#if 0 548 if (multisample) { 549 /* maybe someday */ 550 } 551#endif 552} 553 554 555 556static void 557map_attachment(struct gl_context *ctx, 558 struct gl_framebuffer *fb, 559 gl_buffer_index buffer) 560{ 561 struct gl_texture_object *texObj = fb->Attachment[buffer].Texture; 562 struct gl_renderbuffer *rb = fb->Attachment[buffer].Renderbuffer; 563 struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); 564 565 if (texObj) { 566 /* map texture image (render to texture) */ 567 const GLuint level = fb->Attachment[buffer].TextureLevel; 568 const GLuint face = fb->Attachment[buffer].CubeMapFace; 569 const GLuint slice = fb->Attachment[buffer].Zoffset; 570 struct gl_texture_image *texImage = texObj->Image[face][level]; 571 if (texImage) { 572 ctx->Driver.MapTextureImage(ctx, texImage, slice, 573 0, 0, texImage->Width, texImage->Height, 574 GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, 575 &srb->Map, &srb->RowStride); 576 } 577 } 578 else if (rb) { 579 /* Map ordinary renderbuffer */ 580 ctx->Driver.MapRenderbuffer(ctx, rb, 581 0, 0, rb->Width, rb->Height, 582 GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, 583 &srb->Map, &srb->RowStride); 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 map_attachment(ctx, fb, fb->_ColorDrawBufferIndexes[buf]); 665 find_renderbuffer_colortype(fb->_ColorDrawBuffers[buf]); 666 } 667} 668 669 670/** 671 * Unmap renderbuffers after rendering. 672 */ 673void 674_swrast_unmap_renderbuffers(struct gl_context *ctx) 675{ 676 struct gl_framebuffer *fb = ctx->DrawBuffer; 677 struct gl_renderbuffer *depthRb, *stencilRb; 678 GLuint buf; 679 680 depthRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; 681 if (depthRb) { 682 /* map depth buffer */ 683 unmap_attachment(ctx, fb, BUFFER_DEPTH); 684 } 685 686 stencilRb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; 687 if (stencilRb && stencilRb != depthRb) { 688 /* map stencil buffer */ 689 unmap_attachment(ctx, fb, BUFFER_STENCIL); 690 } 691 692 for (buf = 0; buf < fb->_NumColorDrawBuffers; buf++) { 693 unmap_attachment(ctx, fb, fb->_ColorDrawBufferIndexes[buf]); 694 } 695} 696