/** * Preparation prior to glTexImage. Basically check the 'surface_based' * field and switch to a "normal" tex image if necessary. */ static void prep_teximage(struct gl_context *ctx, struct gl_texture_image *texImage, GLenum format, GLenum type) { struct gl_texture_object *texObj = texImage->TexObject; struct st_texture_object *stObj = st_texture_object(texObj); /* switch to "normal" */ if (stObj->surface_based) { const GLenum target = texObj->Target; const GLuint level = texImage->Level; gl_format texFormat; _mesa_clear_texture_object(ctx, texObj); pipe_resource_reference(&stObj->pt, NULL); /* oops, need to init this image again */ texFormat = _mesa_choose_texture_format(ctx, texObj, target, level, texImage->InternalFormat, format, type); _mesa_init_teximage_fields(ctx, texImage, texImage->Width, texImage->Height, texImage->Depth, texImage->Border, texImage->InternalFormat, texFormat); stObj->surface_based = GL_FALSE; } }
/* This function makes a texture view without bothering with all of the API * checks. Most of them are the same for CopyTexSubImage so checking would * be redundant. The one major difference is that we don't check for * whether the texture is immutable or not. However, since the view will * be created and then immediately destroyed, this should not be a problem. */ static bool make_view(struct gl_context *ctx, struct gl_texture_image *tex_image, struct gl_texture_image **view_tex_image, GLuint *view_tex_name, GLenum internal_format) { struct gl_texture_object *tex_obj = tex_image->TexObject; struct gl_texture_object *view_tex_obj; mesa_format tex_format; /* Set up the new texture object */ _mesa_GenTextures(1, view_tex_name); view_tex_obj = _mesa_lookup_texture(ctx, *view_tex_name); if (!view_tex_obj) return false; tex_format = _mesa_choose_texture_format(ctx, view_tex_obj, tex_obj->Target, 0, internal_format, GL_NONE, GL_NONE); if (!ctx->Driver.TestProxyTexImage(ctx, tex_obj->Target, 0, tex_format, tex_image->Width, tex_image->Height, tex_image->Depth, 0)) { _mesa_DeleteTextures(1, view_tex_name); *view_tex_name = 0; return false; } view_tex_obj->Target = tex_obj->Target; *view_tex_image = _mesa_get_tex_image(ctx, view_tex_obj, tex_obj->Target, 0); if (!*view_tex_image) { _mesa_DeleteTextures(1, view_tex_name); *view_tex_name = 0; return false; } _mesa_init_teximage_fields(ctx, *view_tex_image, tex_image->Width, tex_image->Height, tex_image->Depth, 0, internal_format, tex_format); view_tex_obj->MinLevel = tex_image->Level; view_tex_obj->NumLevels = 1; view_tex_obj->MinLayer = tex_obj->MinLayer; view_tex_obj->NumLayers = tex_obj->NumLayers; view_tex_obj->Immutable = tex_obj->Immutable; view_tex_obj->ImmutableLevels = tex_obj->ImmutableLevels; view_tex_obj->Target = tex_obj->Target; if (ctx->Driver.TextureView != NULL && !ctx->Driver.TextureView(ctx, view_tex_obj, tex_obj)) { _mesa_DeleteTextures(1, view_tex_name); *view_tex_name = 0; return false; /* driver recorded error */ } return true; }
/** * Helper that does the storage allocation for _mesa_TexStorage1/2/3D() * and _mesa_TextureStorage1/2/3D(). */ static void texture_storage(struct gl_context *ctx, GLuint dims, struct gl_texture_object *texObj, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, bool dsa) { GLboolean sizeOK, dimensionsOK; mesa_format texFormat; const char* suffix = dsa ? "ture" : ""; assert(texObj); if (tex_storage_error_check(ctx, texObj, dims, target, levels, internalformat, width, height, depth, dsa)) { return; /* error was recorded */ } texFormat = _mesa_choose_texture_format(ctx, texObj, target, 0, internalformat, GL_NONE, GL_NONE); /* check that width, height, depth are legal for the mipmap level */ dimensionsOK = _mesa_legal_texture_dimensions(ctx, target, 0, width, height, depth, 0); sizeOK = ctx->Driver.TestProxyTexImage(ctx, target, levels, 0, texFormat, 1, width, height, depth); if (_mesa_is_proxy_texture(target)) { if (dimensionsOK && sizeOK) { initialize_texture_fields(ctx, texObj, levels, width, height, depth, internalformat, texFormat); } else { /* clear all image fields for [levels] */ clear_texture_fields(ctx, texObj); } } else { if (!dimensionsOK) { _mesa_error(ctx, GL_INVALID_VALUE, "glTex%sStorage%uD(invalid width, height or depth)", suffix, dims); return; } if (!sizeOK) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTex%sStorage%uD(texture too large)", suffix, dims); } assert(levels > 0); assert(width > 0); assert(height > 0); assert(depth > 0); if (!initialize_texture_fields(ctx, texObj, levels, width, height, depth, internalformat, texFormat)) { return; } /* Do actual texture memory allocation */ if (!ctx->Driver.AllocTextureStorage(ctx, texObj, levels, width, height, depth)) { /* Reset the texture images' info to zeros. * Strictly speaking, we probably don't have to do this since * generating GL_OUT_OF_MEMORY can leave things in an undefined * state but this puts things in a consistent state. */ clear_texture_fields(ctx, texObj); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTex%sStorage%uD", suffix, dims); return; } _mesa_set_texture_view_state(ctx, texObj, target, levels); update_fbo_texture(ctx, texObj); } }
/** * 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 */ } }
/** * Helper used by _mesa_TexStorage1/2/3D(). */ static void texstorage(GLuint dims, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) { struct gl_texture_object *texObj; GLboolean sizeOK, dimensionsOK; gl_format texFormat; GET_CURRENT_CONTEXT(ctx); if (tex_storage_error_check(ctx, dims, target, levels, internalformat, width, height, depth)) { return; /* error was recorded */ } texObj = _mesa_get_current_tex_object(ctx, target); assert(texObj); texFormat = _mesa_choose_texture_format(ctx, texObj, target, 0, internalformat, GL_NONE, GL_NONE); assert(texFormat != MESA_FORMAT_NONE); /* check that width, height, depth are legal for the mipmap level */ dimensionsOK = _mesa_legal_texture_dimensions(ctx, target, 0, width, height, depth, 0); sizeOK = ctx->Driver.TestProxyTexImage(ctx, target, 0, texFormat, width, height, depth, 0); if (_mesa_is_proxy_texture(texObj->Target)) { if (dimensionsOK && sizeOK) { initialize_texture_fields(ctx, texObj, levels, width, height, depth, internalformat, texFormat); } else { /* clear all image fields for [levels] */ clear_texture_fields(ctx, texObj); } } else { if (!dimensionsOK) { _mesa_error(ctx, GL_INVALID_VALUE, "glTexStorage%uD(invalid width, height or depth)", dims); return; } if (!sizeOK) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexStorage%uD(texture too large)", dims); } assert(levels > 0); assert(width > 0); assert(height > 0); assert(depth > 0); if (!initialize_texture_fields(ctx, texObj, levels, width, height, depth, internalformat, texFormat)) { return; } /* Do actual texture memory allocation */ if (!ctx->Driver.AllocTextureStorage(ctx, texObj, levels, width, height, depth)) { /* Reset the texture images' info to zeros. * Strictly speaking, we probably don't have to do this since * generating GL_OUT_OF_MEMORY can leave things in an undefined * state but this puts things in a consistent state. */ clear_texture_fields(ctx, texObj); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexStorage%uD", dims); return; } texObj->Immutable = GL_TRUE; texObj->ImmutableLevels = levels; } }
/** * Do actual memory allocation for glTexStorage1/2/3D(). */ static void setup_texstorage(struct gl_context *ctx, struct gl_texture_object *texObj, GLuint dims, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth) { const GLenum target = texObj->Target; const GLuint numFaces = (target == GL_TEXTURE_CUBE_MAP) ? 6 : 1; gl_format texFormat; GLint level, levelWidth = width, levelHeight = height, levelDepth = depth; GLuint face; assert(levels > 0); assert(width > 0); assert(height > 0); assert(depth > 0); texFormat = _mesa_choose_texture_format(ctx, texObj, target, 0, internalFormat, GL_NONE, GL_NONE); /* Set up all the texture object's gl_texture_images */ for (level = 0; level < levels; level++) { for (face = 0; face < numFaces; face++) { const GLenum faceTarget = (target == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + face : target; struct gl_texture_image *texImage = _mesa_get_tex_image(ctx, texObj, faceTarget, level); if (!texImage) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage%uD", dims); return; } _mesa_init_teximage_fields(ctx, target, texImage, levelWidth, levelHeight, levelDepth, 0, internalFormat, texFormat); } next_mipmap_level_size(target, &levelWidth, &levelHeight, &levelDepth); } assert(levelWidth > 0); assert(levelHeight > 0); assert(levelDepth > 0); if (!_mesa_is_proxy_texture(texObj->Target)) { /* Do actual texture memory allocation */ if (!ctx->Driver.AllocTextureStorage(ctx, texObj, levels, width, height, depth)) { /* Reset the texture images' info to zeros. * Strictly speaking, we probably don't have to do this since * generating GL_OUT_OF_MEMORY can leave things in an undefined * state but this puts things in a consistent state. */ for (level = 0; level < levels; level++) { for (face = 0; face < numFaces; face++) { struct gl_texture_image *texImage = texObj->Image[face][level]; if (texImage) { _mesa_init_teximage_fields(ctx, target, texImage, 0, 0, 0, 0, GL_NONE, MESA_FORMAT_NONE); } } } _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexStorage%uD", dims); return; } } texObj->Immutable = GL_TRUE; }