mesa/copyimage: make sure number of samples match.
[mesa.git] / src / mesa / main / copyimage.c
1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 2014 Intel Corporation. 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 * Authors:
25 * Jason Ekstrand <jason.ekstrand@intel.com>
26 */
27
28 #include "context.h"
29 #include "glheader.h"
30 #include "errors.h"
31 #include "enums.h"
32 #include "copyimage.h"
33 #include "teximage.h"
34 #include "texobj.h"
35 #include "fbobject.h"
36 #include "textureview.h"
37 #include "glformats.h"
38
39 enum mesa_block_class {
40 BLOCK_CLASS_128_BITS,
41 BLOCK_CLASS_64_BITS
42 };
43
44 /**
45 * Prepare the source or destination resource. This involves error
46 * checking and returning the relevant gl_texture_image or gl_renderbuffer.
47 * Note that one of the resulting tex_image or renderbuffer pointers will be
48 * NULL and the other will be non-null.
49 *
50 * \param name the texture or renderbuffer name
51 * \param target One of GL_TEXTURE_x target or GL_RENDERBUFFER
52 * \param level mipmap level
53 * \param z src or dest Z
54 * \param depth number of slices/faces/layers to copy
55 * \param tex_image returns a pointer to a texture image
56 * \param renderbuffer returns a pointer to a renderbuffer
57 * \return true if success, false if error
58 */
59 static bool
60 prepare_target(struct gl_context *ctx, GLuint name, GLenum target,
61 int level, int z, int depth,
62 struct gl_texture_image **tex_image,
63 struct gl_renderbuffer **renderbuffer,
64 mesa_format *format,
65 GLenum *internalFormat,
66 GLuint *width,
67 GLuint *height,
68 const char *dbg_prefix)
69 {
70 if (name == 0) {
71 _mesa_error(ctx, GL_INVALID_VALUE,
72 "glCopyImageSubData(%sName = %d)", dbg_prefix, name);
73 return false;
74 }
75
76 /*
77 * INVALID_ENUM is generated
78 * * if either <srcTarget> or <dstTarget>
79 * - is not RENDERBUFFER or a valid non-proxy texture target
80 * - is TEXTURE_BUFFER, or
81 * - is one of the cubemap face selectors described in table 3.17,
82 */
83 switch (target) {
84 case GL_RENDERBUFFER:
85 /* Not a texture target, but valid */
86 case GL_TEXTURE_1D:
87 case GL_TEXTURE_1D_ARRAY:
88 case GL_TEXTURE_2D:
89 case GL_TEXTURE_3D:
90 case GL_TEXTURE_CUBE_MAP:
91 case GL_TEXTURE_RECTANGLE:
92 case GL_TEXTURE_2D_ARRAY:
93 case GL_TEXTURE_CUBE_MAP_ARRAY:
94 case GL_TEXTURE_2D_MULTISAMPLE:
95 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
96 /* These are all valid */
97 break;
98 case GL_TEXTURE_EXTERNAL_OES:
99 /* Only exists in ES */
100 case GL_TEXTURE_BUFFER:
101 default:
102 _mesa_error(ctx, GL_INVALID_ENUM,
103 "glCopyImageSubData(%sTarget = %s)", dbg_prefix,
104 _mesa_enum_to_string(target));
105 return false;
106 }
107
108 if (target == GL_RENDERBUFFER) {
109 struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, name);
110
111 if (!rb) {
112 _mesa_error(ctx, GL_INVALID_VALUE,
113 "glCopyImageSubData(%sName = %u)", dbg_prefix, name);
114 return false;
115 }
116
117 if (!rb->Name) {
118 _mesa_error(ctx, GL_INVALID_OPERATION,
119 "glCopyImageSubData(%sName incomplete)", dbg_prefix);
120 return false;
121 }
122
123 if (level != 0) {
124 _mesa_error(ctx, GL_INVALID_VALUE,
125 "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level);
126 return false;
127 }
128
129 *renderbuffer = rb;
130 *format = rb->Format;
131 *internalFormat = rb->InternalFormat;
132 *width = rb->Width;
133 *height = rb->Height;
134 *tex_image = NULL;
135 } else {
136 struct gl_texture_object *texObj = _mesa_lookup_texture(ctx, name);
137
138 if (!texObj) {
139 _mesa_error(ctx, GL_INVALID_VALUE,
140 "glCopyImageSubData(%sName = %u)", dbg_prefix, name);
141 return false;
142 }
143
144 _mesa_test_texobj_completeness(ctx, texObj);
145 if (!texObj->_BaseComplete ||
146 (level != 0 && !texObj->_MipmapComplete)) {
147 _mesa_error(ctx, GL_INVALID_OPERATION,
148 "glCopyImageSubData(%sName incomplete)", dbg_prefix);
149 return false;
150 }
151
152 /* Note that target will not be a cube face name */
153 if (texObj->Target != target) {
154 /*
155 * From GL_ARB_copy_image specification:
156 * "INVALID_VALUE is generated if either <srcName> or <dstName> does
157 * not correspond to a valid renderbuffer or texture object according
158 * to the corresponding target parameter."
159 */
160 _mesa_error(ctx, GL_INVALID_VALUE,
161 "glCopyImageSubData(%sTarget = %s)", dbg_prefix,
162 _mesa_enum_to_string(target));
163 return false;
164 }
165
166 if (level < 0 || level >= MAX_TEXTURE_LEVELS) {
167 _mesa_error(ctx, GL_INVALID_VALUE,
168 "glCopyImageSubData(%sLevel = %d)", dbg_prefix, level);
169 return false;
170 }
171
172 if (target == GL_TEXTURE_CUBE_MAP) {
173 int i;
174
175 assert(z < MAX_FACES); /* should have been caught earlier */
176
177 /* make sure all the cube faces are present */
178 for (i = 0; i < depth; i++) {
179 if (!texObj->Image[z+i][level]) {
180 /* missing cube face */
181 _mesa_error(ctx, GL_INVALID_OPERATION,
182 "glCopyImageSubData(missing cube face)");
183 return false;
184 }
185 }
186
187 *tex_image = texObj->Image[z][level];
188 }
189 else {
190 *tex_image = _mesa_select_tex_image(texObj, target, level);
191 }
192
193 if (!*tex_image) {
194 _mesa_error(ctx, GL_INVALID_VALUE,
195 "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level);
196 return false;
197 }
198
199 *renderbuffer = NULL;
200 *format = (*tex_image)->TexFormat;
201 *internalFormat = (*tex_image)->InternalFormat;
202 *width = (*tex_image)->Width;
203 *height = (*tex_image)->Height;
204 }
205
206 return true;
207 }
208
209
210 /**
211 * Check that the x,y,z,width,height,region is within the texture image
212 * dimensions.
213 * \return true if bounds OK, false if regions is out of bounds
214 */
215 static bool
216 check_region_bounds(struct gl_context *ctx,
217 GLenum target,
218 const struct gl_texture_image *tex_image,
219 const struct gl_renderbuffer *renderbuffer,
220 int x, int y, int z, int width, int height, int depth,
221 const char *dbg_prefix)
222 {
223 int surfWidth, surfHeight, surfDepth;
224
225 if (width < 0 || height < 0 || depth < 0) {
226 _mesa_error(ctx, GL_INVALID_VALUE,
227 "glCopyImageSubData(%sWidth, %sHeight, or %sDepth is negative)",
228 dbg_prefix, dbg_prefix, dbg_prefix);
229 return false;
230 }
231
232 if (x < 0 || y < 0 || z < 0) {
233 _mesa_error(ctx, GL_INVALID_VALUE,
234 "glCopyImageSubData(%sX, %sY, or %sZ is negative)",
235 dbg_prefix, dbg_prefix, dbg_prefix);
236 return false;
237 }
238
239 /* Check X direction */
240 if (target == GL_RENDERBUFFER) {
241 surfWidth = renderbuffer->Width;
242 }
243 else {
244 surfWidth = tex_image->Width;
245 }
246
247 if (x + width > surfWidth) {
248 _mesa_error(ctx, GL_INVALID_VALUE,
249 "glCopyImageSubData(%sX or %sWidth exceeds image bounds)",
250 dbg_prefix, dbg_prefix);
251 return false;
252 }
253
254 /* Check Y direction */
255 switch (target) {
256 case GL_RENDERBUFFER:
257 surfHeight = renderbuffer->Height;
258 break;
259 case GL_TEXTURE_1D:
260 case GL_TEXTURE_1D_ARRAY:
261 surfHeight = 1;
262 break;
263 default:
264 surfHeight = tex_image->Height;
265 }
266
267 if (y + height > surfHeight) {
268 _mesa_error(ctx, GL_INVALID_VALUE,
269 "glCopyImageSubData(%sY or %sHeight exceeds image bounds)",
270 dbg_prefix, dbg_prefix);
271 return false;
272 }
273
274 /* Check Z direction */
275 switch (target) {
276 case GL_RENDERBUFFER:
277 case GL_TEXTURE_1D:
278 case GL_TEXTURE_2D:
279 case GL_TEXTURE_2D_MULTISAMPLE:
280 case GL_TEXTURE_RECTANGLE:
281 surfDepth = 1;
282 break;
283 case GL_TEXTURE_CUBE_MAP:
284 surfDepth = 6;
285 break;
286 case GL_TEXTURE_1D_ARRAY:
287 surfDepth = tex_image->Height;
288 break;
289 default:
290 surfDepth = tex_image->Depth;
291 }
292
293 if (z < 0 || z + depth > surfDepth) {
294 _mesa_error(ctx, GL_INVALID_VALUE,
295 "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)",
296 dbg_prefix, dbg_prefix);
297 return false;
298 }
299
300 return true;
301 }
302
303 static bool
304 compressed_format_compatible(const struct gl_context *ctx,
305 GLenum compressedFormat, GLenum otherFormat)
306 {
307 enum mesa_block_class compressedClass, otherClass;
308
309 /* Two view-incompatible compressed formats are never compatible. */
310 if (_mesa_is_compressed_format(ctx, otherFormat)) {
311 return false;
312 }
313
314 /*
315 * From ARB_copy_image spec:
316 * Table 4.X.1 (Compatible internal formats for copying between
317 * compressed and uncompressed internal formats)
318 * ---------------------------------------------------------------------
319 * | Texel / | Uncompressed | |
320 * | Block | internal format | Compressed internal format |
321 * | size | | |
322 * ---------------------------------------------------------------------
323 * | 128-bit | RGBA32UI, | COMPRESSED_RGBA_S3TC_DXT3_EXT, |
324 * | | RGBA32I, | COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,|
325 * | | RGBA32F | COMPRESSED_RGBA_S3TC_DXT5_EXT, |
326 * | | | COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT,|
327 * | | | COMPRESSED_RG_RGTC2, |
328 * | | | COMPRESSED_SIGNED_RG_RGTC2, |
329 * | | | COMPRESSED_RGBA_BPTC_UNORM, |
330 * | | | COMPRESSED_SRGB_ALPHA_BPTC_UNORM, |
331 * | | | COMPRESSED_RGB_BPTC_SIGNED_FLOAT, |
332 * | | | COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT |
333 * ---------------------------------------------------------------------
334 * | 64-bit | RGBA16F, RG32F, | COMPRESSED_RGB_S3TC_DXT1_EXT, |
335 * | | RGBA16UI, RG32UI, | COMPRESSED_SRGB_S3TC_DXT1_EXT, |
336 * | | RGBA16I, RG32I, | COMPRESSED_RGBA_S3TC_DXT1_EXT, |
337 * | | RGBA16, | COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,|
338 * | | RGBA16_SNORM | COMPRESSED_RED_RGTC1, |
339 * | | | COMPRESSED_SIGNED_RED_RGTC1 |
340 * ---------------------------------------------------------------------
341 */
342
343 switch (compressedFormat) {
344 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
345 case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
346 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
347 case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
348 case GL_COMPRESSED_RG_RGTC2:
349 case GL_COMPRESSED_SIGNED_RG_RGTC2:
350 case GL_COMPRESSED_RGBA_BPTC_UNORM:
351 case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
352 case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
353 case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
354 compressedClass = BLOCK_CLASS_128_BITS;
355 break;
356 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
357 case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
358 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
359 case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
360 case GL_COMPRESSED_RED_RGTC1:
361 case GL_COMPRESSED_SIGNED_RED_RGTC1:
362 compressedClass = BLOCK_CLASS_64_BITS;
363 break;
364 case GL_COMPRESSED_RGBA8_ETC2_EAC:
365 case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
366 case GL_COMPRESSED_RG11_EAC:
367 case GL_COMPRESSED_SIGNED_RG11_EAC:
368 if (_mesa_is_gles(ctx))
369 compressedClass = BLOCK_CLASS_128_BITS;
370 else
371 return false;
372 break;
373 case GL_COMPRESSED_RGB8_ETC2:
374 case GL_COMPRESSED_SRGB8_ETC2:
375 case GL_COMPRESSED_R11_EAC:
376 case GL_COMPRESSED_SIGNED_R11_EAC:
377 case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
378 case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
379 if (_mesa_is_gles(ctx))
380 compressedClass = BLOCK_CLASS_64_BITS;
381 else
382 return false;
383 break;
384 default:
385 if (_mesa_is_gles(ctx) && _mesa_is_astc_format(compressedFormat))
386 compressedClass = BLOCK_CLASS_128_BITS;
387 else
388 return false;
389 break;
390 }
391
392 switch (otherFormat) {
393 case GL_RGBA32UI:
394 case GL_RGBA32I:
395 case GL_RGBA32F:
396 otherClass = BLOCK_CLASS_128_BITS;
397 break;
398 case GL_RGBA16F:
399 case GL_RG32F:
400 case GL_RGBA16UI:
401 case GL_RG32UI:
402 case GL_RGBA16I:
403 case GL_RG32I:
404 case GL_RGBA16:
405 case GL_RGBA16_SNORM:
406 otherClass = BLOCK_CLASS_64_BITS;
407 break;
408 default:
409 return false;
410 }
411
412 return compressedClass == otherClass;
413 }
414
415 static bool
416 copy_format_compatible(const struct gl_context *ctx,
417 GLenum srcFormat, GLenum dstFormat)
418 {
419 /*
420 * From ARB_copy_image spec:
421 * For the purposes of CopyImageSubData, two internal formats
422 * are considered compatible if any of the following conditions are
423 * met:
424 * * the formats are the same,
425 * * the formats are considered compatible according to the
426 * compatibility rules used for texture views as defined in
427 * section 3.9.X. In particular, if both internal formats are listed
428 * in the same entry of Table 3.X.2, they are considered compatible, or
429 * * one format is compressed and the other is uncompressed and
430 * Table 4.X.1 lists the two formats in the same row.
431 */
432
433 if (_mesa_texture_view_compatible_format(ctx, srcFormat, dstFormat)) {
434 /* Also checks if formats are equal. */
435 return true;
436 } else if (_mesa_is_compressed_format(ctx, srcFormat)) {
437 return compressed_format_compatible(ctx, srcFormat, dstFormat);
438 } else if (_mesa_is_compressed_format(ctx, dstFormat)) {
439 return compressed_format_compatible(ctx, dstFormat, srcFormat);
440 }
441
442 return false;
443 }
444
445 void GLAPIENTRY
446 _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel,
447 GLint srcX, GLint srcY, GLint srcZ,
448 GLuint dstName, GLenum dstTarget, GLint dstLevel,
449 GLint dstX, GLint dstY, GLint dstZ,
450 GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth)
451 {
452 GET_CURRENT_CONTEXT(ctx);
453 struct gl_texture_image *srcTexImage, *dstTexImage;
454 struct gl_renderbuffer *srcRenderbuffer, *dstRenderbuffer;
455 mesa_format srcFormat, dstFormat;
456 GLenum srcIntFormat, dstIntFormat;
457 GLuint src_w, src_h, dst_w, dst_h;
458 GLuint src_bw, src_bh, dst_bw, dst_bh;
459 int dstWidth, dstHeight, dstDepth;
460 int i;
461
462 if (MESA_VERBOSE & VERBOSE_API)
463 _mesa_debug(ctx, "glCopyImageSubData(%u, %s, %d, %d, %d, %d, "
464 "%u, %s, %d, %d, %d, %d, "
465 "%d, %d, %d)\n",
466 srcName, _mesa_enum_to_string(srcTarget), srcLevel,
467 srcX, srcY, srcZ,
468 dstName, _mesa_enum_to_string(dstTarget), dstLevel,
469 dstX, dstY, dstZ,
470 srcWidth, srcHeight, srcDepth);
471
472 if (!ctx->Extensions.ARB_copy_image) {
473 _mesa_error(ctx, GL_INVALID_OPERATION,
474 "glCopyImageSubData(extension not available)");
475 return;
476 }
477
478 if (!prepare_target(ctx, srcName, srcTarget, srcLevel, srcZ, srcDepth,
479 &srcTexImage, &srcRenderbuffer, &srcFormat,
480 &srcIntFormat, &src_w, &src_h, "src"))
481 return;
482
483 if (!prepare_target(ctx, dstName, dstTarget, dstLevel, dstZ, srcDepth,
484 &dstTexImage, &dstRenderbuffer, &dstFormat,
485 &dstIntFormat, &dst_w, &dst_h, "dst"))
486 return;
487
488 _mesa_get_format_block_size(srcFormat, &src_bw, &src_bh);
489
490 /* Section 18.3.2 (Copying Between Images) of the OpenGL 4.5 Core Profile
491 * spec says:
492 *
493 * An INVALID_VALUE error is generated if the dimensions of either
494 * subregion exceeds the boundaries of the corresponding image object,
495 * or if the image format is compressed and the dimensions of the
496 * subregion fail to meet the alignment constraints of the format.
497 *
498 * and Section 8.7 (Compressed Texture Images) says:
499 *
500 * An INVALID_OPERATION error is generated if any of the following
501 * conditions occurs:
502 *
503 * * width is not a multiple of four, and width + xoffset is not
504 * equal to the value of TEXTURE_WIDTH.
505 * * height is not a multiple of four, and height + yoffset is not
506 * equal to the value of TEXTURE_HEIGHT.
507 *
508 * so we take that to mean that you can copy the "last" block of a
509 * compressed texture image even if it's smaller than the minimum block
510 * dimensions.
511 */
512 if ((srcX % src_bw != 0) || (srcY % src_bh != 0) ||
513 (srcWidth % src_bw != 0 && (srcX + srcWidth) != src_w) ||
514 (srcHeight % src_bh != 0 && (srcY + srcHeight) != src_h)) {
515 _mesa_error(ctx, GL_INVALID_VALUE,
516 "glCopyImageSubData(unaligned src rectangle)");
517 return;
518 }
519
520 _mesa_get_format_block_size(dstFormat, &dst_bw, &dst_bh);
521 if ((dstX % dst_bw != 0) || (dstY % dst_bh != 0)) {
522 _mesa_error(ctx, GL_INVALID_VALUE,
523 "glCopyImageSubData(unaligned dst rectangle)");
524 return;
525 }
526
527 /* From the GL_ARB_copy_image spec:
528 *
529 * "The dimensions are always specified in texels, even for compressed
530 * texture formats. But it should be noted that if only one of the
531 * source and destination textures is compressed then the number of
532 * texels touched in the compressed image will be a factor of the
533 * block size larger than in the uncompressed image."
534 *
535 * So, if copying from compressed to uncompressed, the dest region is
536 * shrunk by the src block size factor. If copying from uncompressed
537 * to compressed, the dest region is grown by the dest block size factor.
538 * Note that we're passed the _source_ width, height, depth and those
539 * dimensions are never changed.
540 */
541 dstWidth = srcWidth * dst_bw / src_bw;
542 dstHeight = srcHeight * dst_bh / src_bh;
543 dstDepth = srcDepth;
544
545 if (!check_region_bounds(ctx, srcTarget, srcTexImage, srcRenderbuffer,
546 srcX, srcY, srcZ, srcWidth, srcHeight, srcDepth,
547 "src"))
548 return;
549
550 if (!check_region_bounds(ctx, dstTarget, dstTexImage, dstRenderbuffer,
551 dstX, dstY, dstZ, dstWidth, dstHeight, dstDepth,
552 "dst"))
553 return;
554
555 /* Section 18.3.2 (Copying Between Images) of the OpenGL 4.5 Core Profile
556 * spec says:
557 *
558 * An INVALID_OPERATION error is generated if either object is a texture
559 * and the texture is not complete, if the source and destination internal
560 * formats are not compatible, or if the number of samples do not match.
561 */
562 if (!copy_format_compatible(ctx, srcIntFormat, dstIntFormat)) {
563 _mesa_error(ctx, GL_INVALID_OPERATION,
564 "glCopyImageSubData(internalFormat mismatch)");
565 return;
566 }
567
568 if (srcTexImage && dstTexImage &&
569 srcTexImage->NumSamples != dstTexImage->NumSamples) {
570 _mesa_error(ctx, GL_INVALID_OPERATION,
571 "glCopyImageSubData(number of samples mismatch)");
572 return;
573 }
574
575 /* loop over 2D slices/faces/layers */
576 for (i = 0; i < srcDepth; ++i) {
577 int newSrcZ = srcZ + i;
578 int newDstZ = dstZ + i;
579
580 if (srcTexImage &&
581 srcTexImage->TexObject->Target == GL_TEXTURE_CUBE_MAP) {
582 /* need to update srcTexImage pointer for the cube face */
583 assert(srcZ + i < MAX_FACES);
584 srcTexImage = srcTexImage->TexObject->Image[srcZ + i][srcLevel];
585 assert(srcTexImage);
586 newSrcZ = 0;
587 }
588
589 if (dstTexImage &&
590 dstTexImage->TexObject->Target == GL_TEXTURE_CUBE_MAP) {
591 /* need to update dstTexImage pointer for the cube face */
592 assert(dstZ + i < MAX_FACES);
593 dstTexImage = dstTexImage->TexObject->Image[dstZ + i][dstLevel];
594 assert(dstTexImage);
595 newDstZ = 0;
596 }
597
598 ctx->Driver.CopyImageSubData(ctx,
599 srcTexImage, srcRenderbuffer,
600 srcX, srcY, newSrcZ,
601 dstTexImage, dstRenderbuffer,
602 dstX, dstY, newDstZ,
603 srcWidth, srcHeight);
604 }
605 }