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