X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Fcopyimage.c;h=3373c6e8cbbcedec952ef23d5aa221365f1f3c7e;hb=HEAD;hp=cf25159e8808aac0995889b3f4410857071c175e;hpb=1456da91c8a14f77dd347981e2bc95e0644e0fd2;p=mesa.git diff --git a/src/mesa/main/copyimage.c b/src/mesa/main/copyimage.c index cf25159e880..3373c6e8cbb 100644 --- a/src/mesa/main/copyimage.c +++ b/src/mesa/main/copyimage.c @@ -57,20 +57,23 @@ enum mesa_block_class { * \return true if success, false if error */ 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) +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, + bool is_arb_version) { + const char *suffix = is_arb_version ? "" : "NV"; + if (name == 0) { _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sName = %d)", dbg_prefix, name); + "glCopyImageSubData%s(%sName = %d)", suffix, dbg_prefix, name); return false; } @@ -101,7 +104,7 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum target, case GL_TEXTURE_BUFFER: default: _mesa_error(ctx, GL_INVALID_ENUM, - "glCopyImageSubData(%sTarget = %s)", dbg_prefix, + "glCopyImageSubData%s(%sTarget = %s)", suffix, dbg_prefix, _mesa_enum_to_string(target)); return false; } @@ -111,19 +114,19 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum target, if (!rb) { _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sName = %u)", dbg_prefix, name); + "glCopyImageSubData%s(%sName = %u)", suffix, dbg_prefix, name); return false; } if (!rb->Name) { _mesa_error(ctx, GL_INVALID_OPERATION, - "glCopyImageSubData(%sName incomplete)", dbg_prefix); + "glCopyImageSubData%s(%sName incomplete)", suffix, dbg_prefix); return false; } if (level != 0) { _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level); + "glCopyImageSubData%s(%sLevel = %u)", suffix, dbg_prefix, level); return false; } @@ -145,15 +148,70 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum target, * to the corresponding target parameter." */ _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sName = %u)", dbg_prefix, name); + "glCopyImageSubData%s(%sName = %u)", suffix, dbg_prefix, name); return false; } + /* 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); - if (!texObj->_BaseComplete || - (level != 0 && !texObj->_MipmapComplete)) { + 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); + "glCopyImageSubData%s(%sName incomplete)", suffix, dbg_prefix); return false; } @@ -165,14 +223,14 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum target, * of the object." */ _mesa_error(ctx, GL_INVALID_ENUM, - "glCopyImageSubData(%sTarget = %s)", dbg_prefix, + "glCopyImageSubData%s(%sTarget = %s)", suffix, dbg_prefix, _mesa_enum_to_string(target)); return false; } if (level < 0 || level >= MAX_TEXTURE_LEVELS) { _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sLevel = %d)", dbg_prefix, level); + "glCopyImageSubData%s(%sLevel = %d)", suffix, dbg_prefix, level); return false; } @@ -199,7 +257,7 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum target, if (!*tex_image) { _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level); + "glCopyImageSubData%s(%sLevel = %u)", suffix, dbg_prefix, level); return false; } @@ -214,6 +272,30 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum target, 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 @@ -226,21 +308,23 @@ check_region_bounds(struct gl_context *ctx, 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) + const char *dbg_prefix, + bool is_arb_version) { int surfWidth, surfHeight, surfDepth; + const char *suffix = is_arb_version ? "" : "NV"; if (width < 0 || height < 0 || depth < 0) { _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sWidth, %sHeight, or %sDepth is negative)", - dbg_prefix, dbg_prefix, dbg_prefix); + "glCopyImageSubData%s(%sWidth, %sHeight, or %sDepth is negative)", + suffix, dbg_prefix, dbg_prefix, dbg_prefix); return false; } if (x < 0 || y < 0 || z < 0) { _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sX, %sY, or %sZ is negative)", - dbg_prefix, dbg_prefix, dbg_prefix); + "glCopyImageSubData%s(%sX, %sY, or %sZ is negative)", + suffix, dbg_prefix, dbg_prefix, dbg_prefix); return false; } @@ -254,8 +338,8 @@ check_region_bounds(struct gl_context *ctx, if (x + width > surfWidth) { _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sX or %sWidth exceeds image bounds)", - dbg_prefix, dbg_prefix); + "glCopyImageSubData%s(%sX or %sWidth exceeds image bounds)", + suffix, dbg_prefix, dbg_prefix); return false; } @@ -274,8 +358,8 @@ check_region_bounds(struct gl_context *ctx, if (y + height > surfHeight) { _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sY or %sHeight exceeds image bounds)", - dbg_prefix, dbg_prefix); + "glCopyImageSubData%s(%sY or %sHeight exceeds image bounds)", + suffix, dbg_prefix, dbg_prefix); return false; } @@ -300,8 +384,8 @@ check_region_bounds(struct gl_context *ctx, if (z < 0 || z + depth > surfDepth) { _mesa_error(ctx, GL_INVALID_VALUE, - "glCopyImageSubData(%sZ or %sDepth exceeds image bounds)", - dbg_prefix, dbg_prefix); + "glCopyImageSubData%s(%sZ or %sDepth exceeds image bounds)", + suffix, dbg_prefix, dbg_prefix); return false; } @@ -450,6 +534,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, @@ -466,7 +615,6 @@ _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLuint src_bw, src_bh, dst_bw, dst_bh; GLuint src_num_samples, dst_num_samples; int dstWidth, dstHeight, dstDepth; - int i; if (MESA_VERBOSE & VERBOSE_API) _mesa_debug(ctx, "glCopyImageSubData(%u, %s, %d, %d, %d, %d, " @@ -484,14 +632,16 @@ _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel, return; } - if (!prepare_target(ctx, srcName, srcTarget, srcLevel, srcZ, srcDepth, - &srcTexImage, &srcRenderbuffer, &srcFormat, - &srcIntFormat, &src_w, &src_h, &src_num_samples, "src")) + if (!prepare_target_err(ctx, srcName, srcTarget, srcLevel, srcZ, srcDepth, + &srcTexImage, &srcRenderbuffer, &srcFormat, + &srcIntFormat, &src_w, &src_h, &src_num_samples, + "src",true)) return; - if (!prepare_target(ctx, dstName, dstTarget, dstLevel, dstZ, srcDepth, - &dstTexImage, &dstRenderbuffer, &dstFormat, - &dstIntFormat, &dst_w, &dst_h, &dst_num_samples, "dst")) + if (!prepare_target_err(ctx, dstName, dstTarget, dstLevel, dstZ, srcDepth, + &dstTexImage, &dstRenderbuffer, &dstFormat, + &dstIntFormat, &dst_w, &dst_h, &dst_num_samples, + "dst",true)) return; _mesa_get_format_block_size(srcFormat, &src_bw, &src_bh); @@ -553,12 +703,12 @@ _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel, if (!check_region_bounds(ctx, srcTarget, srcTexImage, srcRenderbuffer, srcX, srcY, srcZ, srcWidth, srcHeight, srcDepth, - "src")) + "src", true)) return; if (!check_region_bounds(ctx, dstTarget, dstTexImage, dstRenderbuffer, dstX, dstY, dstZ, dstWidth, dstHeight, dstDepth, - "dst")) + "dst", true)) return; /* Section 18.3.2 (Copying Between Images) of the OpenGL 4.5 Core Profile @@ -580,34 +730,150 @@ _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel, return; } - /* loop over 2D slices/faces/layers */ - for (i = 0; i < srcDepth; ++i) { - int newSrcZ = srcZ + i; - int newDstZ = dstZ + i; + copy_image_subdata(ctx, srcTexImage, srcRenderbuffer, srcX, srcY, srcZ, + srcLevel, dstTexImage, dstRenderbuffer, dstX, dstY, dstZ, + dstLevel, srcWidth, srcHeight, srcDepth); +} - 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; - } +void GLAPIENTRY +_mesa_CopyImageSubDataNV_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; - 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; - } + GET_CURRENT_CONTEXT(ctx); - ctx->Driver.CopyImageSubData(ctx, - srcTexImage, srcRenderbuffer, - srcX, srcY, newSrcZ, - dstTexImage, dstRenderbuffer, - dstX, dstY, newDstZ, - srcWidth, srcHeight); + 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_CopyImageSubDataNV(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) +{ + GET_CURRENT_CONTEXT(ctx); + 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; + GLuint src_num_samples, dst_num_samples; + + if (MESA_VERBOSE & VERBOSE_API) + _mesa_debug(ctx, "glCopyImageSubDataNV(%u, %s, %d, %d, %d, %d, " + "%u, %s, %d, %d, %d, %d, " + "%d, %d, %d)\n", + srcName, _mesa_enum_to_string(srcTarget), srcLevel, + srcX, srcY, srcZ, + dstName, _mesa_enum_to_string(dstTarget), dstLevel, + dstX, dstY, dstZ, + srcWidth, srcHeight, srcDepth); + + if (!ctx->Extensions.NV_copy_image) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glCopyImageSubDataNV(extension not available)"); + return; + } + + if (!prepare_target_err(ctx, srcName, srcTarget, srcLevel, srcZ, srcDepth, + &srcTexImage, &srcRenderbuffer, &srcFormat, + &srcIntFormat, &src_w, &src_h, &src_num_samples, + "src", false)) + return; + + if (!prepare_target_err(ctx, dstName, dstTarget, dstLevel, dstZ, srcDepth, + &dstTexImage, &dstRenderbuffer, &dstFormat, + &dstIntFormat, &dst_w, &dst_h, &dst_num_samples, + "dst", false)) + return; + + /* + * The NV_copy_image spec says: + * + * INVALID_OPERATION is generated if either object is a texture + * and the texture is not consistent, or if the source and destination + * internal formats or number of samples do not match. + * + * In the absence of any definition of texture consistency the texture + * completeness check, which is affected in the prepare_target_err function, + * is used instead in keeping with the ARB version. + * The check related to the internal format here is different from the ARB + * version which adds the ability to copy between images which have + * different formats where the formats are compatible for texture views. + */ + if (srcIntFormat != dstIntFormat) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glCopyImageSubDataNV(internalFormat mismatch)"); + return; + } + + if (src_num_samples != dst_num_samples) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glCopyImageSubDataNV(number of samples mismatch)"); + return; } + + /* + * The NV_copy_image spec says: + * + * INVALID_VALUE is generated if the image format is compressed + * and the dimensions of the subregion fail to meet the alignment + * constraints of the format. + * + * The check here is identical to the ARB version. + */ + _mesa_get_format_block_size(srcFormat, &src_bw, &src_bh); + if ((srcX % src_bw != 0) || (srcY % 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, + "glCopyImageSubDataNV(unaligned src rectangle)"); + return; + } + + _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, + "glCopyImageSubDataNV(unaligned dst rectangle)"); + return; + } + + /* + * The NV_copy_image spec says: + * + * INVALID_VALUE is generated if the dimensions of the either subregion + * exceeds the boundaries of the corresponding image object. + * + * The check here is similar to the ARB version except for the fact that + * block sizes are not considered owing to the fact that copying across + * compressed and uncompressed formats is not supported. + */ + if (!check_region_bounds(ctx, srcTarget, srcTexImage, srcRenderbuffer, + srcX, srcY, srcZ, srcWidth, srcHeight, srcDepth, + "src", false)) + return; + + if (!check_region_bounds(ctx, dstTarget, dstTexImage, dstRenderbuffer, + dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth, + "dst", false)) + return; + + copy_image_subdata(ctx, srcTexImage, srcRenderbuffer, srcX, srcY, srcZ, + srcLevel, dstTexImage, dstRenderbuffer, dstX, dstY, dstZ, + dstLevel, srcWidth, srcHeight, srcDepth); }