1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 1999-2007 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 #include "main/glheader.h"
27 #include "main/context.h"
28 #include "main/condrender.h"
29 #include "main/macros.h"
30 #include "main/blit.h"
31 #include "main/pixeltransfer.h"
32
33
34 #include "s_context.h"
35 #include "s_depth.h"
36 #include "s_span.h"
37 #include "s_stencil.h"
38 #include "s_zoom.h"
39
40
41
42 /**
43 * Determine if there's overlap in an image copy.
44 * This test also compensates for the fact that copies are done from
45 * bottom to top and overlaps can sometimes be handled correctly
46 * without making a temporary image copy.
47 * \return GL_TRUE if the regions overlap, GL_FALSE otherwise.
48 */
49 static GLboolean
regions_overlap(GLint srcx,GLint srcy,GLint dstx,GLint dsty,GLint width,GLint height,GLfloat zoomX,GLfloat zoomY)50 regions_overlap(GLint srcx, GLint srcy,
51 GLint dstx, GLint dsty,
52 GLint width, GLint height,
53 GLfloat zoomX, GLfloat zoomY)
54 {
55 if (zoomX == 1.0F && zoomY == 1.0F) {
56 return _mesa_regions_overlap(srcx, srcy, srcx + width, srcy + height,
57 dstx, dsty, dstx + width, dsty + height);
58 }
59 else {
60 /* add one pixel of slop when zooming, just to be safe */
61 if (srcx > (dstx + ((zoomX > 0.0F) ? (width * zoomX + 1.0F) : 0.0F))) {
62 /* src is completely right of dest */
63 return GL_FALSE;
64 }
65 else if (srcx + width + 1.0F < dstx + ((zoomX > 0.0F) ? 0.0F : (width * zoomX))) {
66 /* src is completely left of dest */
67 return GL_FALSE;
68 }
69 else if ((srcy < dsty) && (srcy + height < dsty + (height * zoomY))) {
70 /* src is completely below dest */
71 return GL_FALSE;
72 }
73 else if ((srcy > dsty) && (srcy + height > dsty + (height * zoomY))) {
74 /* src is completely above dest */
75 return GL_FALSE;
76 }
77 else {
78 return GL_TRUE;
79 }
80 }
81 }
82
83
84 /**
85 * RGBA copypixels
86 */
87 static void
copy_rgba_pixels(struct gl_context * ctx,GLint srcx,GLint srcy,GLint width,GLint height,GLint destx,GLint desty)88 copy_rgba_pixels(struct gl_context *ctx, GLint srcx, GLint srcy,
89 GLint width, GLint height, GLint destx, GLint desty)
90 {
91 GLfloat *tmpImage, *p;
92 GLint sy, dy, stepy, row;
93 const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
94 GLint overlapping;
95 GLuint transferOps = ctx->_ImageTransferState;
96 SWspan span;
97
98 if (!ctx->ReadBuffer->_ColorReadBuffer) {
99 /* no readbuffer - OK */
100 return;
101 }
102
103 if (ctx->DrawBuffer == ctx->ReadBuffer) {
104 overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
105 ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
106 }
107 else {
108 overlapping = GL_FALSE;
109 }
110
111 /* Determine if copy should be done bottom-to-top or top-to-bottom */
112 if (!overlapping && srcy < desty) {
113 /* top-down max-to-min */
114 sy = srcy + height - 1;
115 dy = desty + height - 1;
116 stepy = -1;
117 }
118 else {
119 /* bottom-up min-to-max */
120 sy = srcy;
121 dy = desty;
122 stepy = 1;
123 }
124
125 INIT_SPAN(span, GL_BITMAP);
126 _swrast_span_default_attribs(ctx, &span);
127 span.arrayMask = SPAN_RGBA;
128 span.arrayAttribs = VARYING_BIT_COL0; /* we'll fill in COL0 attrib values */
129
130 if (overlapping) {
131 tmpImage = malloc(width * height * sizeof(GLfloat) * 4);
132 if (!tmpImage) {
133 _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
134 return;
135 }
136 /* read the source image as RGBA/float */
137 p = tmpImage;
138 for (row = 0; row < height; row++) {
139 _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer,
140 width, srcx, sy + row, p );
141 p += width * 4;
142 }
143 p = tmpImage;
144 }
145 else {
146 tmpImage = NULL; /* silence compiler warnings */
147 p = NULL;
148 }
149
150 assert(width < SWRAST_MAX_WIDTH);
151
152 for (row = 0; row < height; row++, sy += stepy, dy += stepy) {
153 GLvoid *rgba = span.array->attribs[VARYING_SLOT_COL0];
154
155 /* Get row/span of source pixels */
156 if (overlapping) {
157 /* get from buffered image */
158 memcpy(rgba, p, width * sizeof(GLfloat) * 4);
159 p += width * 4;
160 }
161 else {
162 /* get from framebuffer */
163 _swrast_read_rgba_span( ctx, ctx->ReadBuffer->_ColorReadBuffer,
164 width, srcx, sy, rgba );
165 }
166
167 if (transferOps) {
168 _mesa_apply_rgba_transfer_ops(ctx, transferOps, width,
169 (GLfloat (*)[4]) rgba);
170 }
171
172 /* Write color span */
173 span.x = destx;
174 span.y = dy;
175 span.end = width;
176 span.array->ChanType = GL_FLOAT;
177 if (zoom) {
178 _swrast_write_zoomed_rgba_span(ctx, destx, desty, &span, rgba);
179 }
180 else {
181 _swrast_write_rgba_span(ctx, &span);
182 }
183 }
184
185 span.array->ChanType = CHAN_TYPE; /* restore */
186
187 if (overlapping)
188 free(tmpImage);
189 }
190
191
192 /**
193 * Convert floating point Z values to integer Z values with pixel transfer's
194 * Z scale and bias.
195 */
196 static void
scale_and_bias_z(struct gl_context * ctx,GLuint width,const GLfloat depth[],GLuint z[])197 scale_and_bias_z(struct gl_context *ctx, GLuint width,
198 const GLfloat depth[], GLuint z[])
199 {
200 const GLuint depthMax = ctx->DrawBuffer->_DepthMax;
201 GLuint i;
202
203 if (depthMax <= 0xffffff &&
204 ctx->Pixel.DepthScale == 1.0F &&
205 ctx->Pixel.DepthBias == 0.0F) {
206 /* no scale or bias and no clamping and no worry of overflow */
207 const GLfloat depthMaxF = ctx->DrawBuffer->_DepthMaxF;
208 for (i = 0; i < width; i++) {
209 z[i] = (GLuint) (depth[i] * depthMaxF);
210 }
211 }
212 else {
213 /* need to be careful with overflow */
214 const GLdouble depthMaxF = ctx->DrawBuffer->_DepthMaxF;
215 for (i = 0; i < width; i++) {
216 GLdouble d = depth[i] * ctx->Pixel.DepthScale + ctx->Pixel.DepthBias;
217 d = SATURATE(d) * depthMaxF;
218 if (d >= depthMaxF)
219 z[i] = depthMax;
220 else
221 z[i] = (GLuint) d;
222 }
223 }
224 }
225
226
227
228 /*
229 * TODO: Optimize!!!!
230 */
231 static void
copy_depth_pixels(struct gl_context * ctx,GLint srcx,GLint srcy,GLint width,GLint height,GLint destx,GLint desty)232 copy_depth_pixels( struct gl_context *ctx, GLint srcx, GLint srcy,
233 GLint width, GLint height,
234 GLint destx, GLint desty )
235 {
236 struct gl_framebuffer *fb = ctx->ReadBuffer;
237 struct gl_renderbuffer *readRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer;
238 GLfloat *p, *tmpImage, *depth;
239 GLint sy, dy, stepy;
240 GLint j;
241 const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
242 GLint overlapping;
243 SWspan span;
244
245 if (!readRb) {
246 /* no readbuffer - OK */
247 return;
248 }
249
250 INIT_SPAN(span, GL_BITMAP);
251 _swrast_span_default_attribs(ctx, &span);
252 span.arrayMask = SPAN_Z;
253
254 if (ctx->DrawBuffer == ctx->ReadBuffer) {
255 overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
256 ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
257 }
258 else {
259 overlapping = GL_FALSE;
260 }
261
262 /* Determine if copy should be bottom-to-top or top-to-bottom */
263 if (!overlapping && srcy < desty) {
264 /* top-down max-to-min */
265 sy = srcy + height - 1;
266 dy = desty + height - 1;
267 stepy = -1;
268 }
269 else {
270 /* bottom-up min-to-max */
271 sy = srcy;
272 dy = desty;
273 stepy = 1;
274 }
275
276 if (overlapping) {
277 GLint ssy = sy;
278 tmpImage = malloc(width * height * sizeof(GLfloat));
279 if (!tmpImage) {
280 _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
281 return;
282 }
283 p = tmpImage;
284 for (j = 0; j < height; j++, ssy += stepy) {
285 _swrast_read_depth_span_float(ctx, readRb, width, srcx, ssy, p);
286 p += width;
287 }
288 p = tmpImage;
289 }
290 else {
291 tmpImage = NULL; /* silence compiler warning */
292 p = NULL;
293 }
294
295 depth = malloc(width * sizeof(GLfloat));
296 if (!depth) {
297 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels()");
298 goto end;
299 }
300
301 for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
302 /* get depth values */
303 if (overlapping) {
304 memcpy(depth, p, width * sizeof(GLfloat));
305 p += width;
306 }
307 else {
308 _swrast_read_depth_span_float(ctx, readRb, width, srcx, sy, depth);
309 }
310
311 /* apply scale and bias */
312 scale_and_bias_z(ctx, width, depth, span.array->z);
313
314 /* write depth values */
315 span.x = destx;
316 span.y = dy;
317 span.end = width;
318 if (zoom)
319 _swrast_write_zoomed_depth_span(ctx, destx, desty, &span);
320 else
321 _swrast_write_rgba_span(ctx, &span);
322 }
323
324 free(depth);
325
326 end:
327 if (overlapping)
328 free(tmpImage);
329 }
330
331
332
333 static void
copy_stencil_pixels(struct gl_context * ctx,GLint srcx,GLint srcy,GLint width,GLint height,GLint destx,GLint desty)334 copy_stencil_pixels( struct gl_context *ctx, GLint srcx, GLint srcy,
335 GLint width, GLint height,
336 GLint destx, GLint desty )
337 {
338 struct gl_framebuffer *fb = ctx->ReadBuffer;
339 struct gl_renderbuffer *rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer;
340 GLint sy, dy, stepy;
341 GLint j;
342 GLubyte *p, *tmpImage, *stencil;
343 const GLboolean zoom = ctx->Pixel.ZoomX != 1.0F || ctx->Pixel.ZoomY != 1.0F;
344 GLint overlapping;
345
346 if (!rb) {
347 /* no readbuffer - OK */
348 return;
349 }
350
351 if (ctx->DrawBuffer == ctx->ReadBuffer) {
352 overlapping = regions_overlap(srcx, srcy, destx, desty, width, height,
353 ctx->Pixel.ZoomX, ctx->Pixel.ZoomY);
354 }
355 else {
356 overlapping = GL_FALSE;
357 }
358
359 /* Determine if copy should be bottom-to-top or top-to-bottom */
360 if (!overlapping && srcy < desty) {
361 /* top-down max-to-min */
362 sy = srcy + height - 1;
363 dy = desty + height - 1;
364 stepy = -1;
365 }
366 else {
367 /* bottom-up min-to-max */
368 sy = srcy;
369 dy = desty;
370 stepy = 1;
371 }
372
373 if (overlapping) {
374 GLint ssy = sy;
375 tmpImage = malloc(width * height * sizeof(GLubyte));
376 if (!tmpImage) {
377 _mesa_error( ctx, GL_OUT_OF_MEMORY, "glCopyPixels" );
378 return;
379 }
380 p = tmpImage;
381 for (j = 0; j < height; j++, ssy += stepy) {
382 _swrast_read_stencil_span( ctx, rb, width, srcx, ssy, p );
383 p += width;
384 }
385 p = tmpImage;
386 }
387 else {
388 tmpImage = NULL; /* silence compiler warning */
389 p = NULL;
390 }
391
392 stencil = malloc(width * sizeof(GLubyte));
393 if (!stencil) {
394 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels()");
395 goto end;
396 }
397
398 for (j = 0; j < height; j++, sy += stepy, dy += stepy) {
399 /* Get stencil values */
400 if (overlapping) {
401 memcpy(stencil, p, width * sizeof(GLubyte));
402 p += width;
403 }
404 else {
405 _swrast_read_stencil_span( ctx, rb, width, srcx, sy, stencil );
406 }
407
408 _mesa_apply_stencil_transfer_ops(ctx, width, stencil);
409
410 /* Write stencil values */
411 if (zoom) {
412 _swrast_write_zoomed_stencil_span(ctx, destx, desty, width,
413 destx, dy, stencil);
414 }
415 else {
416 _swrast_write_stencil_span( ctx, width, destx, dy, stencil );
417 }
418 }
419
420 free(stencil);
421
422 end:
423 if (overlapping)
424 free(tmpImage);
425 }
426
427
428 /**
429 * Try to do a fast 1:1 blit with memcpy.
430 * \return GL_TRUE if successful, GL_FALSE otherwise.
431 */
432 GLboolean
swrast_fast_copy_pixels(struct gl_context * ctx,struct gl_framebuffer * srcFb,struct gl_framebuffer * dstFb,GLint srcX,GLint srcY,GLsizei width,GLsizei height,GLint dstX,GLint dstY,GLenum type)433 swrast_fast_copy_pixels(struct gl_context *ctx,
434 struct gl_framebuffer *srcFb,
435 struct gl_framebuffer *dstFb,
436 GLint srcX, GLint srcY, GLsizei width, GLsizei height,
437 GLint dstX, GLint dstY, GLenum type)
438 {
439 struct gl_renderbuffer *srcRb, *dstRb;
440 GLint row;
441 GLuint pixelBytes, widthInBytes;
442 GLubyte *srcMap, *dstMap;
443 GLint srcRowStride, dstRowStride;
444
445 if (type == GL_COLOR) {
446 if (dstFb->_NumColorDrawBuffers != 1)
447 return GL_FALSE;
448 srcRb = srcFb->_ColorReadBuffer;
449 dstRb = dstFb->_ColorDrawBuffers[0];
450 }
451 else if (type == GL_STENCIL) {
452 srcRb = srcFb->Attachment[BUFFER_STENCIL].Renderbuffer;
453 dstRb = dstFb->Attachment[BUFFER_STENCIL].Renderbuffer;
454 }
455 else if (type == GL_DEPTH) {
456 srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer;
457 dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer;
458 }
459 else {
460 assert(type == GL_DEPTH_STENCIL_EXT);
461 /* XXX correct? */
462 srcRb = srcFb->Attachment[BUFFER_DEPTH].Renderbuffer;
463 dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer;
464 }
465
466 /* src and dst renderbuffers must be same format */
467 if (!srcRb || !dstRb || srcRb->Format != dstRb->Format) {
468 return GL_FALSE;
469 }
470
471 if (type == GL_STENCIL || type == GL_DEPTH_COMPONENT) {
472 /* can't handle packed depth+stencil here */
473 if (_mesa_is_format_packed_depth_stencil(srcRb->Format) ||
474 _mesa_is_format_packed_depth_stencil(dstRb->Format))
475 return GL_FALSE;
476 }
477 else if (type == GL_DEPTH_STENCIL) {
478 /* can't handle separate depth/stencil buffers */
479 if (srcRb != srcFb->Attachment[BUFFER_STENCIL].Renderbuffer ||
480 dstRb != dstFb->Attachment[BUFFER_STENCIL].Renderbuffer)
481 return GL_FALSE;
482 }
483
484 /* clipping not supported */
485 if (srcX < 0 || srcX + width > (GLint) srcFb->Width ||
486 srcY < 0 || srcY + height > (GLint) srcFb->Height ||
487 dstX < dstFb->_Xmin || dstX + width > dstFb->_Xmax ||
488 dstY < dstFb->_Ymin || dstY + height > dstFb->_Ymax) {
489 return GL_FALSE;
490 }
491
492 pixelBytes = _mesa_get_format_bytes(srcRb->Format);
493 widthInBytes = width * pixelBytes;
494
495 if (srcRb == dstRb) {
496 /* map whole buffer for read/write */
497 /* XXX we could be clever and just map the union region of the
498 * source and dest rects.
499 */
500 GLubyte *map;
501 GLint rowStride;
502
503 ctx->Driver.MapRenderbuffer(ctx, srcRb, 0, 0,
504 srcRb->Width, srcRb->Height,
505 GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
506 &map, &rowStride, srcFb->FlipY);
507 if (!map) {
508 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
509 return GL_TRUE; /* don't retry with slow path */
510 }
511
512 srcMap = map + srcY * rowStride + srcX * pixelBytes;
513 dstMap = map + dstY * rowStride + dstX * pixelBytes;
514
515 /* this handles overlapping copies */
516 if (srcY < dstY) {
517 /* copy in reverse (top->down) order */
518 srcMap += rowStride * (height - 1);
519 dstMap += rowStride * (height - 1);
520 srcRowStride = -rowStride;
521 dstRowStride = -rowStride;
522 }
523 else {
524 /* copy in normal (bottom->up) order */
525 srcRowStride = rowStride;
526 dstRowStride = rowStride;
527 }
528 }
529 else {
530 /* different src/dst buffers */
531 ctx->Driver.MapRenderbuffer(ctx, srcRb, srcX, srcY,
532 width, height,
533 GL_MAP_READ_BIT, &srcMap, &srcRowStride,
534 srcFb->FlipY);
535 if (!srcMap) {
536 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
537 return GL_TRUE; /* don't retry with slow path */
538 }
539 ctx->Driver.MapRenderbuffer(ctx, dstRb, dstX, dstY,
540 width, height,
541 GL_MAP_WRITE_BIT, &dstMap, &dstRowStride,
542 dstFb->FlipY);
543 if (!dstMap) {
544 ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
545 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
546 return GL_TRUE; /* don't retry with slow path */
547 }
548 }
549
550 for (row = 0; row < height; row++) {
551 /* memmove() in case of overlap */
552 memmove(dstMap, srcMap, widthInBytes);
553 dstMap += dstRowStride;
554 srcMap += srcRowStride;
555 }
556
557 ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
558 if (dstRb != srcRb) {
559 ctx->Driver.UnmapRenderbuffer(ctx, dstRb);
560 }
561
562 return GL_TRUE;
563 }
564
565
566 /**
567 * Find/map the renderbuffer that we'll be reading from.
568 * The swrast_render_start() function only maps the drawing buffers,
569 * not the read buffer.
570 */
571 static struct gl_renderbuffer *
map_readbuffer(struct gl_context * ctx,GLenum type)572 map_readbuffer(struct gl_context *ctx, GLenum type)
573 {
574 struct gl_framebuffer *fb = ctx->ReadBuffer;
575 struct gl_renderbuffer *rb;
576 struct swrast_renderbuffer *srb;
577
578 switch (type) {
579 case GL_COLOR:
580 rb = fb->Attachment[fb->_ColorReadBufferIndex].Renderbuffer;
581 break;
582 case GL_DEPTH:
583 case GL_DEPTH_STENCIL:
584 rb = fb->Attachment[BUFFER_DEPTH].Renderbuffer;
585 break;
586 case GL_STENCIL:
587 rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer;
588 break;
589 default:
590 return NULL;
591 }
592
593 srb = swrast_renderbuffer(rb);
594
595 if (!srb || srb->Map) {
596 /* no buffer, or buffer is mapped already, we're done */
597 return NULL;
598 }
599
600 ctx->Driver.MapRenderbuffer(ctx, rb,
601 0, 0, rb->Width, rb->Height,
602 GL_MAP_READ_BIT,
603 &srb->Map, &srb->RowStride,
604 fb->FlipY);
605
606 return rb;
607 }
608
609
610 /**
611 * Do software-based glCopyPixels.
612 * By time we get here, all parameters will have been error-checked.
613 */
614 void
_swrast_CopyPixels(struct gl_context * ctx,GLint srcx,GLint srcy,GLsizei width,GLsizei height,GLint destx,GLint desty,GLenum type)615 _swrast_CopyPixels(struct gl_context *ctx,
616 GLint srcx, GLint srcy, GLsizei width, GLsizei height,
617 GLint destx, GLint desty, GLenum type)
618 {
619 SWcontext *swrast = SWRAST_CONTEXT(ctx);
620 struct gl_renderbuffer *rb;
621
622 if (!_mesa_check_conditional_render(ctx))
623 return; /* don't copy */
624
625 if (swrast->NewState)
626 _swrast_validate_derived( ctx );
627
628 if (!(SWRAST_CONTEXT(ctx)->_RasterMask != 0x0 ||
629 ctx->Pixel.ZoomX != 1.0F ||
630 ctx->Pixel.ZoomY != 1.0F ||
631 ctx->_ImageTransferState) &&
632 swrast_fast_copy_pixels(ctx, ctx->ReadBuffer, ctx->DrawBuffer,
633 srcx, srcy, width, height, destx, desty,
634 type)) {
635 /* all done */
636 return;
637 }
638
639 swrast_render_start(ctx);
640 rb = map_readbuffer(ctx, type);
641
642 switch (type) {
643 case GL_COLOR:
644 copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty );
645 break;
646 case GL_DEPTH:
647 copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty );
648 break;
649 case GL_STENCIL:
650 copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty );
651 break;
652 case GL_DEPTH_STENCIL_EXT:
653 /* Copy buffers separately (if the fast copy path wasn't taken) */
654 copy_depth_pixels(ctx, srcx, srcy, width, height, destx, desty);
655 copy_stencil_pixels(ctx, srcx, srcy, width, height, destx, desty);
656 break;
657 default:
658 _mesa_problem(ctx, "unexpected type in _swrast_CopyPixels");
659 }
660
661 swrast_render_finish(ctx);
662
663 if (rb) {
664 struct swrast_renderbuffer *srb = swrast_renderbuffer(rb);
665 ctx->Driver.UnmapRenderbuffer(ctx, rb);
666 srb->Map = NULL;
667 }
668 }
669