static bool copy_format_compatible(const struct gl_context *ctx, GLenum srcFormat, GLenum dstFormat) { /* * From ARB_copy_image spec: * For the purposes of CopyImageSubData, two internal formats * are considered compatible if any of the following conditions are * met: * * the formats are the same, * * the formats are considered compatible according to the * compatibility rules used for texture views as defined in * section 3.9.X. In particular, if both internal formats are listed * in the same entry of Table 3.X.2, they are considered compatible, or * * one format is compressed and the other is uncompressed and * Table 4.X.1 lists the two formats in the same row. */ if (_mesa_texture_view_compatible_format(ctx, srcFormat, dstFormat)) { /* Also checks if formats are equal. */ return true; } else if (_mesa_is_compressed_format(ctx, srcFormat)) { return compressed_format_compatible(ctx, srcFormat, dstFormat); } else if (_mesa_is_compressed_format(ctx, dstFormat)) { return compressed_format_compatible(ctx, dstFormat, srcFormat); } return false; }
/** * glTextureView (ARB_texture_view) * If an error is found, record it with _mesa_error() * \return none. */ void GLAPIENTRY _mesa_TextureView(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) { struct gl_texture_object *texObj; struct gl_texture_object *origTexObj; struct gl_texture_image *origTexImage; GLuint newViewMinLevel, newViewMinLayer; GLuint newViewNumLevels, newViewNumLayers; GLsizei width, height, depth; mesa_format texFormat; GLboolean sizeOK, dimensionsOK; GLenum faceTarget; GET_CURRENT_CONTEXT(ctx); if (MESA_VERBOSE & (VERBOSE_API | VERBOSE_TEXTURE)) _mesa_debug(ctx, "glTextureView %d %s %d %s %d %d %d %d\n", texture, _mesa_lookup_enum_by_nr(target), origtexture, _mesa_lookup_enum_by_nr(internalformat), minlevel, numlevels, minlayer, numlayers); if (origtexture == 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glTextureView(origtexture = %u)", origtexture); return; } /* Need original texture information to validate arguments */ origTexObj = _mesa_lookup_texture(ctx, origtexture); /* If <origtexture> is not the name of a texture, INVALID_VALUE is generated. */ if (!origTexObj) { _mesa_error(ctx, GL_INVALID_VALUE, "glTextureView(origtexture = %u)", origtexture); return; } /* If <origtexture>'s TEXTURE_IMMUTABLE_FORMAT value is not TRUE, * INVALID_OPERATION is generated. */ if (!origTexObj->Immutable) { _mesa_error(ctx, GL_INVALID_OPERATION, "glTextureView(origtexture not immutable)"); return; } /* If <texture> is 0, INVALID_VALUE is generated. */ if (texture == 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glTextureView(texture = 0)"); return; } /* If <texture> is not a valid name returned by GenTextures, * the error INVALID_OPERATION is generated. */ texObj = _mesa_lookup_texture(ctx, texture); if (texObj == NULL) { _mesa_error(ctx, GL_INVALID_OPERATION, "glTextureView(texture = %u non-gen name)", texture); return; } /* If <texture> has already been bound and given a target, then * the error INVALID_OPERATION is generated. */ if (texObj->Target) { _mesa_error(ctx, GL_INVALID_OPERATION, "glTextureView(texture = %u already bound)", texture); return; } /* Check for compatible target */ if (!target_valid(ctx, origTexObj->Target, target)) { return; /* error was recorded */ } /* minlevel and minlayer are relative to the view of origtexture * If minlevel or minlayer is greater than level or layer, respectively, * of origtexture return INVALID_VALUE. */ newViewMinLevel = origTexObj->MinLevel + minlevel; newViewMinLayer = origTexObj->MinLayer + minlayer; if (newViewMinLevel >= (origTexObj->MinLevel + origTexObj->NumLevels)) { _mesa_error(ctx, GL_INVALID_VALUE, "glTextureView(new minlevel (%d) > orig minlevel (%d) + orig numlevels (%d))", newViewMinLevel, origTexObj->MinLevel, origTexObj->NumLevels); return; } if (newViewMinLayer >= (origTexObj->MinLayer + origTexObj->NumLayers)) { _mesa_error(ctx, GL_INVALID_VALUE, "glTextureView(new minlayer (%d) > orig minlayer (%d) + orig numlayers (%d))", newViewMinLayer, origTexObj->MinLayer, origTexObj->NumLayers); return; } if (!_mesa_texture_view_compatible_format(ctx, origTexObj->Image[0][0]->InternalFormat, internalformat)) { _mesa_error(ctx, GL_INVALID_OPERATION, "glTextureView(internalformat %s not compatible with origtexture %s)", _mesa_lookup_enum_by_nr(internalformat), _mesa_lookup_enum_by_nr(origTexObj->Image[0][0]->InternalFormat)); return; } texFormat = _mesa_choose_texture_format(ctx, texObj, target, 0, internalformat, GL_NONE, GL_NONE); assert(texFormat != MESA_FORMAT_NONE); if (texFormat == MESA_FORMAT_NONE) return; newViewNumLevels = MIN2(numlevels, origTexObj->NumLevels - minlevel); newViewNumLayers = MIN2(numlayers, origTexObj->NumLayers - minlayer); faceTarget = origTexObj->Target; if (faceTarget == GL_TEXTURE_CUBE_MAP) faceTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + minlayer; /* Get a reference to what will become this View's base level */ origTexImage = _mesa_select_tex_image(origTexObj, faceTarget, minlevel); width = origTexImage->Width; height = origTexImage->Height; depth = origTexImage->Depth; /* Adjust width, height, depth to be appropriate for new target */ switch (target) { case GL_TEXTURE_1D: height = 1; break; case GL_TEXTURE_3D: break; case GL_TEXTURE_1D_ARRAY: height = (GLsizei) newViewNumLayers; break; case GL_TEXTURE_2D: case GL_TEXTURE_2D_MULTISAMPLE: case GL_TEXTURE_RECTANGLE: case GL_TEXTURE_CUBE_MAP: depth = 1; break; case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_CUBE_MAP_ARRAY: case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: depth = newViewNumLayers; break; } /* If the dimensions of the original texture are larger than the maximum * supported dimensions of the new target, the error INVALID_OPERATION is * generated. For example, if the original texture has a TEXTURE_2D_ARRAY * target and its width is greater than MAX_CUBE_MAP_TEXTURE_SIZE, an error * will be generated if TextureView is called to create a TEXTURE_CUBE_MAP * view. */ dimensionsOK = _mesa_legal_texture_dimensions(ctx, target, 0, width, height, depth, 0); if (!dimensionsOK) { _mesa_error(ctx, GL_INVALID_OPERATION, "glTextureView(invalid width or height or depth)"); return; } sizeOK = ctx->Driver.TestProxyTexImage(ctx, target, 0, texFormat, width, height, depth, 0); if (!sizeOK) { _mesa_error(ctx, GL_INVALID_OPERATION, "glTextureView(invalid texture size)"); return; } /* If <target> is TEXTURE_1D, TEXTURE_2D, TEXTURE_3D, TEXTURE_RECTANGLE, * or TEXTURE_2D_MULTISAMPLE and <numlayers> does not equal 1, the error * INVALID_VALUE is generated. */ switch (target) { case GL_TEXTURE_1D: case GL_TEXTURE_2D: case GL_TEXTURE_3D: case GL_TEXTURE_RECTANGLE: case GL_TEXTURE_2D_MULTISAMPLE: if (numlayers != 1) { _mesa_error(ctx, GL_INVALID_VALUE, "glTextureView(numlayers %d != 1)", numlayers); return; } break; case GL_TEXTURE_CUBE_MAP: /* If the new texture's target is TEXTURE_CUBE_MAP, the clamped <numlayers> * must be equal to 6. */ if (newViewNumLayers != 6) { _mesa_error(ctx, GL_INVALID_VALUE, "glTextureView(clamped numlayers %d != 6)", newViewNumLayers); return; } break; case GL_TEXTURE_CUBE_MAP_ARRAY: /* If the new texture's target is TEXTURE_CUBE_MAP_ARRAY, * then <numlayers> counts layer-faces rather than layers, * and the clamped <numlayers> must be a multiple of 6. * Otherwise, the error INVALID_VALUE is generated. */ if ((newViewNumLayers % 6) != 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glTextureView(clamped numlayers %d is not a multiple of 6)", newViewNumLayers); return; } break; } /* If the new texture's target is TEXTURE_CUBE_MAP or * TEXTURE_CUBE_MAP_ARRAY, the width and height of the original texture's * levels must be equal otherwise the error INVALID_OPERATION is generated. */ if ((target == GL_TEXTURE_CUBE_MAP || target == GL_TEXTURE_CUBE_MAP_ARRAY) && (origTexImage->Width != origTexImage->Height)) { _mesa_error(ctx, GL_INVALID_OPERATION, "glTextureView(origtexture width (%d) != height (%d))", origTexImage->Width, origTexImage->Height); return; } /* When the original texture's target is TEXTURE_CUBE_MAP, the layer * parameters are interpreted in the same order as if it were a * TEXTURE_CUBE_MAP_ARRAY with 6 layer-faces. */ /* If the internal format does not exactly match the internal format of the * original texture, the contents of the memory are reinterpreted in the * same manner as for image bindings described in * section 3.9.20 (Texture Image Loads and Stores). */ /* TEXTURE_BASE_LEVEL and TEXTURE_MAX_LEVEL are interpreted * relative to the view and not relative to the original data store. */ if (!initialize_texture_fields(ctx, target, texObj, newViewNumLevels, width, height, depth, internalformat, texFormat)) { return; /* Already recorded error */ } texObj->MinLevel = newViewMinLevel; texObj->MinLayer = newViewMinLayer; texObj->NumLevels = newViewNumLevels; texObj->NumLayers = newViewNumLayers; texObj->Immutable = GL_TRUE; texObj->ImmutableLevels = origTexObj->ImmutableLevels; texObj->Target = target; if (ctx->Driver.TextureView != NULL && !ctx->Driver.TextureView(ctx, texObj, origTexObj)) { return; /* driver recorded error */ } }
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) { GET_CURRENT_CONTEXT(ctx); GLuint tmpTexNames[2] = { 0, 0 }; struct gl_texture_object *srcTexObj, *dstTexObj; struct gl_texture_image *srcTexImage, *dstTexImage; GLuint src_bw, src_bh, dst_bw, dst_bh; int i, srcNewZ, dstNewZ, Bpt; 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, srcX, srcY, srcZ, dstName, _mesa_lookup_enum_by_nr(dstTarget), dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcWidth); if (!ctx->Extensions.ARB_copy_image) { _mesa_error(ctx, GL_INVALID_OPERATION, "glCopyImageSubData(extension not available)"); return; } if (!prepare_target(ctx, srcName, &srcTarget, srcLevel, &srcTexObj, &srcTexImage, &tmpTexNames[0], "src")) goto cleanup; if (!prepare_target(ctx, dstName, &dstTarget, dstLevel, &dstTexObj, &dstTexImage, &tmpTexNames[1], "dst")) goto cleanup; _mesa_get_format_block_size(srcTexImage->TexFormat, &src_bw, &src_bh); if ((srcX % src_bw != 0) || (srcY % src_bh != 0) || (srcWidth % src_bw != 0) || (srcHeight % src_bh != 0)) { _mesa_error(ctx, GL_INVALID_VALUE, "glCopyImageSubData(unaligned src rectangle)"); goto cleanup; } _mesa_get_format_block_size(dstTexImage->TexFormat, &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; } /* Very simple sanity check. This is sufficient if one of the textures * is compressed. */ Bpt = _mesa_get_format_bytes(srcTexImage->TexFormat); if (_mesa_get_format_bytes(dstTexImage->TexFormat) != Bpt) { _mesa_error(ctx, GL_INVALID_VALUE, "glCopyImageSubData(internalFormat mismatch)"); goto cleanup; } if (!check_region_bounds(ctx, srcTexImage, srcX, srcY, srcZ, srcWidth, srcHeight, srcDepth, "src")) goto cleanup; if (!check_region_bounds(ctx, dstTexImage, dstX, dstY, dstZ, (srcWidth / src_bw) * dst_bw, (srcHeight / src_bh) * dst_bh, srcDepth, "dst")) goto cleanup; if (_mesa_is_format_compressed(srcTexImage->TexFormat)) { /* XXX: Technically, we should probaby do some more specific checking * here. However, this should be sufficient for all compressed * formats that mesa supports since it is a direct memory copy. */ } else if (_mesa_is_format_compressed(dstTexImage->TexFormat)) { } else if (_mesa_texture_view_compatible_format(ctx, srcTexImage->InternalFormat, dstTexImage->InternalFormat)) { } else { return; /* Error logged by _mesa_texture_view_compatible_format */ } for (i = 0; i < srcDepth; ++i) { 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); } cleanup: _mesa_DeleteTextures(2, tmpTexNames); }