X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Fcopyimage.c;h=10777cfdf0570e877ef89e6c22cbd40103f16b6f;hb=7ec308d978019dd9aa0e7a756b5919e34431196d;hp=e8732c6175b63b35457e34080c869fa1084a11e7;hpb=a62edcce4eb4c800d972817a20ee874bf2a2c3ef;p=mesa.git diff --git a/src/mesa/main/copyimage.c b/src/mesa/main/copyimage.c index e8732c6175b..10777cfdf05 100644 --- a/src/mesa/main/copyimage.c +++ b/src/mesa/main/copyimage.c @@ -25,6 +25,7 @@ * Jason Ekstrand */ +#include "context.h" #include "glheader.h" #include "errors.h" #include "enums.h" @@ -41,23 +42,31 @@ enum mesa_block_class { }; /** - * Prepare the source or destination resource, including: - * - Error checking - * - Creating texture wrappers for renderbuffers + * Prepare the source or destination resource. This involves error + * checking and returning the relevant gl_texture_image or gl_renderbuffer. + * Note that one of the resulting tex_image or renderbuffer pointers will be + * NULL and the other will be non-null. + * * \param name the texture or renderbuffer name - * \param target GL_TEXTURE target or GL_RENDERBUFFER. For the later, will - * be changed to a compatible GL_TEXTURE target. + * \param target One of GL_TEXTURE_x target or GL_RENDERBUFFER * \param level mipmap level - * \param tex_obj returns a pointer to a texture object + * \param z src or dest Z + * \param depth number of slices/faces/layers to copy * \param tex_image returns a pointer to a texture image - * \param tmp_tex returns temporary texture object name + * \param renderbuffer returns a pointer to a renderbuffer * \return true if success, false if error */ static bool -prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level, - struct gl_texture_object **tex_obj, - struct gl_texture_image **tex_image, GLuint *tmp_tex, - const char *dbg_prefix) +prepare_target_err(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) { if (name == 0) { _mesa_error(ctx, GL_INVALID_VALUE, @@ -72,7 +81,7 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level, * - is TEXTURE_BUFFER, or * - is one of the cubemap face selectors described in table 3.17, */ - switch (*target) { + switch (target) { case GL_RENDERBUFFER: /* Not a texture target, but valid */ case GL_TEXTURE_1D: @@ -93,12 +102,13 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level, default: _mesa_error(ctx, GL_INVALID_ENUM, "glCopyImageSubData(%sTarget = %s)", dbg_prefix, - _mesa_lookup_enum_by_nr(*target)); + _mesa_enum_to_string(target)); return false; } - if (*target == GL_RENDERBUFFER) { + if (target == GL_RENDERBUFFER) { struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, name); + if (!rb) { _mesa_error(ctx, GL_INVALID_VALUE, "glCopyImageSubData(%sName = %u)", dbg_prefix, name); @@ -117,49 +127,101 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level, return false; } - if (rb->NumSamples > 1) - *target = GL_TEXTURE_2D_MULTISAMPLE; - else - *target = GL_TEXTURE_2D; - - *tmp_tex = 0; - _mesa_GenTextures(1, tmp_tex); - if (*tmp_tex == 0) - return false; /* Error already set by GenTextures */ - - _mesa_BindTexture(*target, *tmp_tex); - *tex_obj = _mesa_lookup_texture(ctx, *tmp_tex); - *tex_image = _mesa_get_tex_image(ctx, *tex_obj, *target, 0); - - if (!ctx->Driver.BindRenderbufferTexImage(ctx, rb, *tex_image)) { - _mesa_problem(ctx, "Failed to create texture from renderbuffer"); - return false; - } - - if (ctx->Driver.FinishRenderTexture && !rb->NeedsFinishRenderTexture) { - rb->NeedsFinishRenderTexture = true; - ctx->Driver.FinishRenderTexture(ctx, rb); - } + *renderbuffer = rb; + *format = rb->Format; + *internalFormat = rb->InternalFormat; + *width = rb->Width; + *height = rb->Height; + *num_samples = rb->NumSamples; + *tex_image = NULL; } else { - *tex_obj = _mesa_lookup_texture(ctx, name); - if (!*tex_obj) { + struct gl_texture_object *texObj = _mesa_lookup_texture(ctx, name); + + if (!texObj) { + /* + * From GL_ARB_copy_image specification: + * "INVALID_VALUE is generated if either or does + * not correspond to a valid renderbuffer or texture object according + * to the corresponding target parameter." + */ _mesa_error(ctx, GL_INVALID_VALUE, "glCopyImageSubData(%sName = %u)", dbg_prefix, name); return false; } - _mesa_test_texobj_completeness(ctx, *tex_obj); - if (!(*tex_obj)->_BaseComplete || - (level != 0 && !(*tex_obj)->_MipmapComplete)) { + /* The ARB_copy_image specification says: + * + * "INVALID_OPERATION is generated if either object is a texture and + * the texture is not complete (as defined in section 3.9.14)" + * + * The cited section says: + * + * "Using the preceding definitions, a texture is complete unless any + * of the following conditions hold true: [...] + * + * * The minification filter requires a mipmap (is neither NEAREST + * nor LINEAR), and the texture is not mipmap complete." + * + * This imposes the bizarre restriction that glCopyImageSubData requires + * mipmap completion based on the sampler minification filter, even + * though the call fundamentally ignores the sampler. Additionally, it + * doesn't work with texture units, so it can't consider any bound + * separate sampler objects. It appears that you're supposed to use + * the sampler object which is built-in to the texture object. + * + * dEQP and the Android CTS mandate this behavior, and the Khronos + * GL and ES working groups both affirmed that this is unfortunate but + * correct. See https://cvs.khronos.org/bugzilla/show_bug.cgi?id=16224. + * + * Integer textures with filtering cause another completeness snag: + * + * "Any of: + * – The internal format of the texture is integer (see table 8.12). + * – The internal format is STENCIL_INDEX. + * – The internal format is DEPTH_STENCIL, and the value of + * DEPTH_STENCIL_TEXTURE_MODE for the texture is STENCIL_INDEX. + * and either the magnification filter is not NEAREST, or the + * minification filter is neither NEAREST nor + * NEAREST_MIPMAP_NEAREST." + * + * However, applications in the wild (such as "Total War: WARHAMMER") + * appear to call glCopyImageSubData with integer textures and the + * default mipmap filters of GL_LINEAR and GL_NEAREST_MIPMAP_LINEAR, + * which would be considered incomplete, but expect this to work. In + * fact, until VK-GL-CTS commit fef80039ff875a51806b54d151c5f2d0c12da, + * the GL 4.5 CTS contained three tests which did the exact same thing + * by accident, and all conformant implementations allowed it. + * + * A proposal was made to amend the spec to say "is not complete (as + * defined in section , but ignoring format-based completeness + * rules)" to allow this case. It makes some sense, given that + * glCopyImageSubData copies raw data without considering format. + * While the official edits have not yet been made, the OpenGL + * working group agreed with the idea of allowing this behavior. + * + * To ignore formats, we check texObj->_MipmapComplete directly + * rather than calling _mesa_is_texture_complete(). + */ + _mesa_test_texobj_completeness(ctx, texObj); + const bool texture_complete_aside_from_formats = + _mesa_is_mipmap_filter(&texObj->Sampler) ? texObj->_MipmapComplete + : texObj->_BaseComplete; + if (!texture_complete_aside_from_formats) { _mesa_error(ctx, GL_INVALID_OPERATION, "glCopyImageSubData(%sName incomplete)", dbg_prefix); return false; } - if ((*tex_obj)->Target != *target) { + /* Note that target will not be a cube face name */ + if (texObj->Target != target) { + /* + * From GL_ARB_copy_image_specification: + * "INVALID_ENUM is generated if the target does not match the type + * of the object." + */ _mesa_error(ctx, GL_INVALID_ENUM, "glCopyImageSubData(%sTarget = %s)", dbg_prefix, - _mesa_lookup_enum_by_nr(*target)); + _mesa_enum_to_string(target)); return false; } @@ -169,17 +231,68 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level, return false; } - *tex_image = _mesa_select_tex_image(*tex_obj, *target, level); + if (target == GL_TEXTURE_CUBE_MAP) { + int i; + + assert(z < MAX_FACES); /* should have been caught earlier */ + + /* make sure all the cube faces are present */ + for (i = 0; i < depth; i++) { + if (!texObj->Image[z+i][level]) { + /* missing cube face */ + _mesa_error(ctx, GL_INVALID_VALUE, + "glCopyImageSubData(missing cube face)"); + return false; + } + } + + *tex_image = texObj->Image[z][level]; + } + else { + *tex_image = _mesa_select_tex_image(texObj, target, level); + } + if (!*tex_image) { _mesa_error(ctx, GL_INVALID_VALUE, "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level); return false; } + + *renderbuffer = NULL; + *format = (*tex_image)->TexFormat; + *internalFormat = (*tex_image)->InternalFormat; + *width = (*tex_image)->Width; + *height = (*tex_image)->Height; + *num_samples = (*tex_image)->NumSamples; } return true; } +static void +prepare_target(struct gl_context *ctx, GLuint name, GLenum target, + int level, int z, + struct gl_texture_image **texImage, + struct gl_renderbuffer **renderbuffer) +{ + if (target == GL_RENDERBUFFER) { + struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, name); + + *renderbuffer = rb; + *texImage = NULL; + } else { + struct gl_texture_object *texObj = _mesa_lookup_texture(ctx, name); + + if (target == GL_TEXTURE_CUBE_MAP) { + *texImage = texObj->Image[z][level]; + } + else { + *texImage = _mesa_select_tex_image(texObj, target, level); + } + + *renderbuffer = NULL; + } +} /** * Check that the x,y,z,width,height,region is within the texture image @@ -188,10 +301,14 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level, */ 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) { + int surfWidth, surfHeight, surfDepth; + if (width < 0 || height < 0 || depth < 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glCopyImageSubData(%sWidth, %sHeight, or %sDepth is negative)", @@ -207,7 +324,14 @@ check_region_bounds(struct gl_context *ctx, } /* Check X direction */ - if (x + width > tex_image->Width) { + if (target == GL_RENDERBUFFER) { + surfWidth = renderbuffer->Width; + } + else { + surfWidth = tex_image->Width; + } + + if (x + width > surfWidth) { _mesa_error(ctx, GL_INVALID_VALUE, "glCopyImageSubData(%sX or %sWidth exceeds image bounds)", dbg_prefix, dbg_prefix); @@ -215,66 +339,49 @@ check_region_bounds(struct gl_context *ctx, } /* Check Y direction */ - switch (tex_image->TexObject->Target) { + switch (target) { + case GL_RENDERBUFFER: + surfHeight = renderbuffer->Height; + break; case GL_TEXTURE_1D: case GL_TEXTURE_1D_ARRAY: - if (y != 0 || height != 1) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sY or %sHeight exceeds image bounds)", - dbg_prefix, dbg_prefix); - return false; - } + surfHeight = 1; break; default: - if (y + height > tex_image->Height) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sY or %sHeight exceeds image bounds)", - dbg_prefix, dbg_prefix); - return false; - } - break; + surfHeight = tex_image->Height; + } + + if (y + height > surfHeight) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glCopyImageSubData(%sY or %sHeight exceeds image bounds)", + dbg_prefix, dbg_prefix); + return false; } /* Check Z direction */ - switch (tex_image->TexObject->Target) { + switch (target) { + case GL_RENDERBUFFER: case GL_TEXTURE_1D: case GL_TEXTURE_2D: case GL_TEXTURE_2D_MULTISAMPLE: case GL_TEXTURE_RECTANGLE: - if (z != 0 || depth != 1) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)", - dbg_prefix, dbg_prefix); - return false; - } + surfDepth = 1; break; case GL_TEXTURE_CUBE_MAP: - if (z < 0 || z + depth > 6) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)", - dbg_prefix, dbg_prefix); - return false; - } + surfDepth = 6; break; case GL_TEXTURE_1D_ARRAY: - if (z < 0 || z + depth > tex_image->Height) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)", - dbg_prefix, dbg_prefix); - return false; - } - break; - case GL_TEXTURE_CUBE_MAP_ARRAY: - case GL_TEXTURE_2D_ARRAY: - case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: - case GL_TEXTURE_3D: - if (z < 0 || z + depth > tex_image->Depth) { - _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)", - dbg_prefix, dbg_prefix); - return false; - } + surfDepth = tex_image->Height; break; + default: + surfDepth = tex_image->Depth; + } + + if (z < 0 || z + depth > surfDepth) { + _mesa_error(ctx, GL_INVALID_VALUE, + "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)", + dbg_prefix, dbg_prefix); + return false; } return true; @@ -341,8 +448,32 @@ compressed_format_compatible(const struct gl_context *ctx, case GL_COMPRESSED_SIGNED_RED_RGTC1: compressedClass = BLOCK_CLASS_64_BITS; break; + case GL_COMPRESSED_RGBA8_ETC2_EAC: + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + case GL_COMPRESSED_RG11_EAC: + case GL_COMPRESSED_SIGNED_RG11_EAC: + if (_mesa_is_gles(ctx)) + compressedClass = BLOCK_CLASS_128_BITS; + else + return false; + break; + case GL_COMPRESSED_RGB8_ETC2: + case GL_COMPRESSED_SRGB8_ETC2: + case GL_COMPRESSED_R11_EAC: + case GL_COMPRESSED_SIGNED_R11_EAC: + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + if (_mesa_is_gles(ctx)) + compressedClass = BLOCK_CLASS_64_BITS; + else + return false; + break; default: - return false; + if (_mesa_is_gles(ctx) && _mesa_is_astc_format(compressedFormat)) + compressedClass = BLOCK_CLASS_128_BITS; + else + return false; + break; } switch (otherFormat) { @@ -398,6 +529,71 @@ copy_format_compatible(const struct gl_context *ctx, return false; } +static void +copy_image_subdata(struct gl_context *ctx, + struct gl_texture_image *srcTexImage, + struct gl_renderbuffer *srcRenderbuffer, + int srcX, int srcY, int srcZ, int srcLevel, + struct gl_texture_image *dstTexImage, + struct gl_renderbuffer *dstRenderbuffer, + int dstX, int dstY, int dstZ, int dstLevel, + int srcWidth, int srcHeight, int srcDepth) +{ + /* loop over 2D slices/faces/layers */ + for (int i = 0; i < srcDepth; ++i) { + int newSrcZ = srcZ + i; + int newDstZ = dstZ + i; + + if (srcTexImage && + srcTexImage->TexObject->Target == GL_TEXTURE_CUBE_MAP) { + /* need to update srcTexImage pointer for the cube face */ + assert(srcZ + i < MAX_FACES); + srcTexImage = srcTexImage->TexObject->Image[srcZ + i][srcLevel]; + assert(srcTexImage); + newSrcZ = 0; + } + + if (dstTexImage && + dstTexImage->TexObject->Target == GL_TEXTURE_CUBE_MAP) { + /* need to update dstTexImage pointer for the cube face */ + assert(dstZ + i < MAX_FACES); + dstTexImage = dstTexImage->TexObject->Image[dstZ + i][dstLevel]; + assert(dstTexImage); + newDstZ = 0; + } + + ctx->Driver.CopyImageSubData(ctx, + srcTexImage, srcRenderbuffer, + srcX, srcY, newSrcZ, + dstTexImage, dstRenderbuffer, + dstX, dstY, newDstZ, + srcWidth, srcHeight); + } +} + +void GLAPIENTRY +_mesa_CopyImageSubData_no_error(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) +{ + struct gl_texture_image *srcTexImage, *dstTexImage; + struct gl_renderbuffer *srcRenderbuffer, *dstRenderbuffer; + + GET_CURRENT_CONTEXT(ctx); + + prepare_target(ctx, srcName, srcTarget, srcLevel, srcZ, &srcTexImage, + &srcRenderbuffer); + + prepare_target(ctx, dstName, dstTarget, dstLevel, dstZ, &dstTexImage, + &dstRenderbuffer); + + copy_image_subdata(ctx, srcTexImage, srcRenderbuffer, srcX, srcY, srcZ, + srcLevel, dstTexImage, dstRenderbuffer, dstX, dstY, dstZ, + dstLevel, srcWidth, srcHeight, srcDepth); +} + void GLAPIENTRY _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, @@ -406,21 +602,24 @@ _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) { GET_CURRENT_CONTEXT(ctx); - GLuint tmpTexNames[2] = { 0, 0 }; - struct gl_texture_object *srcTexObj, *dstTexObj; struct gl_texture_image *srcTexImage, *dstTexImage; + struct gl_renderbuffer *srcRenderbuffer, *dstRenderbuffer; + mesa_format srcFormat, dstFormat; + GLenum srcIntFormat, dstIntFormat; + GLuint src_w, src_h, dst_w, dst_h; GLuint src_bw, src_bh, dst_bw, dst_bh; - int i; + GLuint src_num_samples, dst_num_samples; + int dstWidth, dstHeight, dstDepth; if (MESA_VERBOSE & VERBOSE_API) _mesa_debug(ctx, "glCopyImageSubData(%u, %s, %d, %d, %d, %d, " "%u, %s, %d, %d, %d, %d, " "%d, %d, %d)\n", - srcName, _mesa_lookup_enum_by_nr(srcTarget), srcLevel, + srcName, _mesa_enum_to_string(srcTarget), srcLevel, srcX, srcY, srcZ, - dstName, _mesa_lookup_enum_by_nr(dstTarget), dstLevel, + dstName, _mesa_enum_to_string(dstTarget), dstLevel, dstX, dstY, dstZ, - srcWidth, srcHeight, srcWidth); + srcWidth, srcHeight, srcDepth); if (!ctx->Extensions.ARB_copy_image) { _mesa_error(ctx, GL_INVALID_OPERATION, @@ -428,67 +627,105 @@ _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel, return; } - if (!prepare_target(ctx, srcName, &srcTarget, srcLevel, - &srcTexObj, &srcTexImage, &tmpTexNames[0], "src")) - goto cleanup; + if (!prepare_target_err(ctx, srcName, srcTarget, srcLevel, srcZ, srcDepth, + &srcTexImage, &srcRenderbuffer, &srcFormat, + &srcIntFormat, &src_w, &src_h, &src_num_samples, + "src")) + return; - if (!prepare_target(ctx, dstName, &dstTarget, dstLevel, - &dstTexObj, &dstTexImage, &tmpTexNames[1], "dst")) - goto cleanup; + if (!prepare_target_err(ctx, dstName, dstTarget, dstLevel, dstZ, srcDepth, + &dstTexImage, &dstRenderbuffer, &dstFormat, + &dstIntFormat, &dst_w, &dst_h, &dst_num_samples, + "dst")) + return; - _mesa_get_format_block_size(srcTexImage->TexFormat, &src_bw, &src_bh); + _mesa_get_format_block_size(srcFormat, &src_bw, &src_bh); + + /* Section 18.3.2 (Copying Between Images) of the OpenGL 4.5 Core Profile + * spec says: + * + * An INVALID_VALUE error is generated if the dimensions of either + * subregion exceeds the boundaries of the corresponding image object, + * or if the image format is compressed and the dimensions of the + * subregion fail to meet the alignment constraints of the format. + * + * and Section 8.7 (Compressed Texture Images) says: + * + * An INVALID_OPERATION error is generated if any of the following + * conditions occurs: + * + * * width is not a multiple of four, and width + xoffset is not + * equal to the value of TEXTURE_WIDTH. + * * height is not a multiple of four, and height + yoffset is not + * equal to the value of TEXTURE_HEIGHT. + * + * so we take that to mean that you can copy the "last" block of a + * compressed texture image even if it's smaller than the minimum block + * dimensions. + */ if ((srcX % src_bw != 0) || (srcY % src_bh != 0) || - (srcWidth % src_bw != 0) || (srcHeight % src_bh != 0)) { + (srcWidth % src_bw != 0 && (srcX + srcWidth) != src_w) || + (srcHeight % src_bh != 0 && (srcY + srcHeight) != src_h)) { _mesa_error(ctx, GL_INVALID_VALUE, "glCopyImageSubData(unaligned src rectangle)"); - goto cleanup; + return; } - _mesa_get_format_block_size(dstTexImage->TexFormat, &dst_bw, &dst_bh); + _mesa_get_format_block_size(dstFormat, &dst_bw, &dst_bh); if ((dstX % dst_bw != 0) || (dstY % dst_bh != 0)) { _mesa_error(ctx, GL_INVALID_VALUE, "glCopyImageSubData(unaligned dst rectangle)"); - goto cleanup; + return; } - if (!check_region_bounds(ctx, srcTexImage, srcX, srcY, srcZ, - srcWidth, srcHeight, srcDepth, "src")) - goto cleanup; + /* From the GL_ARB_copy_image spec: + * + * "The dimensions are always specified in texels, even for compressed + * texture formats. But it should be noted that if only one of the + * source and destination textures is compressed then the number of + * texels touched in the compressed image will be a factor of the + * block size larger than in the uncompressed image." + * + * So, if copying from compressed to uncompressed, the dest region is + * shrunk by the src block size factor. If copying from uncompressed + * to compressed, the dest region is grown by the dest block size factor. + * Note that we're passed the _source_ width, height, depth and those + * dimensions are never changed. + */ + dstWidth = srcWidth * dst_bw / src_bw; + dstHeight = srcHeight * dst_bh / src_bh; + dstDepth = srcDepth; - if (!check_region_bounds(ctx, dstTexImage, dstX, dstY, dstZ, - (srcWidth / src_bw) * dst_bw, - (srcHeight / src_bh) * dst_bh, srcDepth, "dst")) - goto cleanup; + if (!check_region_bounds(ctx, srcTarget, srcTexImage, srcRenderbuffer, + srcX, srcY, srcZ, srcWidth, srcHeight, srcDepth, + "src")) + return; + + if (!check_region_bounds(ctx, dstTarget, dstTexImage, dstRenderbuffer, + dstX, dstY, dstZ, dstWidth, dstHeight, dstDepth, + "dst")) + return; - if (!copy_format_compatible(ctx, srcTexImage->InternalFormat, - dstTexImage->InternalFormat)) { + /* Section 18.3.2 (Copying Between Images) of the OpenGL 4.5 Core Profile + * spec says: + * + * An INVALID_OPERATION error is generated if either object is a texture + * and the texture is not complete, if the source and destination internal + * formats are not compatible, or if the number of samples do not match. + */ + if (!copy_format_compatible(ctx, srcIntFormat, dstIntFormat)) { _mesa_error(ctx, GL_INVALID_OPERATION, "glCopyImageSubData(internalFormat mismatch)"); - goto cleanup; + return; } - for (i = 0; i < srcDepth; ++i) { - int srcNewZ, dstNewZ; - - if (srcTexObj->Target == GL_TEXTURE_CUBE_MAP) { - srcTexImage = srcTexObj->Image[i + srcZ][srcLevel]; - srcNewZ = 0; - } else { - srcNewZ = srcZ + i; - } - - if (dstTexObj->Target == GL_TEXTURE_CUBE_MAP) { - dstTexImage = dstTexObj->Image[i + dstZ][dstLevel]; - dstNewZ = 0; - } else { - dstNewZ = dstZ + i; - } - - ctx->Driver.CopyImageSubData(ctx, srcTexImage, srcX, srcY, srcNewZ, - dstTexImage, dstX, dstY, dstNewZ, - srcWidth, srcHeight); + if (src_num_samples != dst_num_samples) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glCopyImageSubData(number of samples mismatch)"); + return; } -cleanup: - _mesa_DeleteTextures(2, tmpTexNames); + copy_image_subdata(ctx, srcTexImage, srcRenderbuffer, srcX, srcY, srcZ, + srcLevel, dstTexImage, dstRenderbuffer, dstX, dstY, dstZ, + dstLevel, srcWidth, srcHeight, srcDepth); }