/*
* Mesa 3-D graphics library
- * Version: 7.1
*
* Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
- * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
*/
-#include "mfeatures.h"
+#include <stdio.h>
#include "bufferobj.h"
-#include "colortab.h"
#include "context.h"
#include "enums.h"
#include "fbobject.h"
#include "hash.h"
#include "imports.h"
#include "macros.h"
+#include "shaderimage.h"
#include "teximage.h"
#include "texobj.h"
#include "texstate.h"
/** \name Internal functions */
/*@{*/
+/**
+ * This function checks for all valid combinations of Min and Mag filters for
+ * Float types, when extensions like OES_texture_float and
+ * OES_texture_float_linear are supported. OES_texture_float mentions support
+ * for NEAREST, NEAREST_MIPMAP_NEAREST magnification and minification filters.
+ * Mag filters like LINEAR and min filters like NEAREST_MIPMAP_LINEAR,
+ * LINEAR_MIPMAP_NEAREST and LINEAR_MIPMAP_LINEAR are only valid in case
+ * OES_texture_float_linear is supported.
+ *
+ * Returns true in case the filter is valid for given Float type else false.
+ */
+static bool
+valid_filter_for_float(const struct gl_context *ctx,
+ const struct gl_texture_object *obj)
+{
+ switch (obj->Sampler.MagFilter) {
+ case GL_LINEAR:
+ if (obj->_IsHalfFloat && !ctx->Extensions.OES_texture_half_float_linear) {
+ return false;
+ } else if (obj->_IsFloat && !ctx->Extensions.OES_texture_float_linear) {
+ return false;
+ }
+ case GL_NEAREST:
+ case GL_NEAREST_MIPMAP_NEAREST:
+ break;
+ default:
+ unreachable("Invalid mag filter");
+ }
+
+ switch (obj->Sampler.MinFilter) {
+ case GL_LINEAR:
+ case GL_NEAREST_MIPMAP_LINEAR:
+ case GL_LINEAR_MIPMAP_NEAREST:
+ case GL_LINEAR_MIPMAP_LINEAR:
+ if (obj->_IsHalfFloat && !ctx->Extensions.OES_texture_half_float_linear) {
+ return false;
+ } else if (obj->_IsFloat && !ctx->Extensions.OES_texture_float_linear) {
+ return false;
+ }
+ case GL_NEAREST:
+ case GL_NEAREST_MIPMAP_NEAREST:
+ break;
+ default:
+ unreachable("Invalid min filter");
+ }
+
+ return true;
+}
/**
* Return the gl_texture_object for a given ID.
_mesa_HashLookup(ctx->Shared->TexObjects, id);
}
+/**
+ * Wrapper around _mesa_lookup_texture that throws GL_INVALID_OPERATION if id
+ * is not in the hash table. After calling _mesa_error, it returns NULL.
+ */
+struct gl_texture_object *
+_mesa_lookup_texture_err(struct gl_context *ctx, GLuint id, const char* func)
+{
+ struct gl_texture_object *texObj;
+
+ texObj = _mesa_lookup_texture(ctx, id); /* Returns NULL if not found. */
+
+ if (!texObj)
+ _mesa_error(ctx, GL_INVALID_OPERATION, "%s(texture)", func);
+
+ return texObj;
+}
+
+void
+_mesa_begin_texture_lookups(struct gl_context *ctx)
+{
+ _mesa_HashLockMutex(ctx->Shared->TexObjects);
+}
+
+
+void
+_mesa_end_texture_lookups(struct gl_context *ctx)
+{
+ _mesa_HashUnlockMutex(ctx->Shared->TexObjects);
+}
+
+
+struct gl_texture_object *
+_mesa_lookup_texture_locked(struct gl_context *ctx, GLuint id)
+{
+ return (struct gl_texture_object *)
+ _mesa_HashLookupLocked(ctx->Shared->TexObjects, id);
+}
+
+/**
+ * Return a pointer to the current texture object for the given target
+ * on the current texture unit.
+ * Note: all <target> error checking should have been done by this point.
+ */
+struct gl_texture_object *
+_mesa_get_current_tex_object(struct gl_context *ctx, GLenum target)
+{
+ struct gl_texture_unit *texUnit = _mesa_get_current_tex_unit(ctx);
+ const GLboolean arrayTex = ctx->Extensions.EXT_texture_array;
+
+ switch (target) {
+ case GL_TEXTURE_1D:
+ return texUnit->CurrentTex[TEXTURE_1D_INDEX];
+ case GL_PROXY_TEXTURE_1D:
+ return ctx->Texture.ProxyTex[TEXTURE_1D_INDEX];
+ case GL_TEXTURE_2D:
+ return texUnit->CurrentTex[TEXTURE_2D_INDEX];
+ case GL_PROXY_TEXTURE_2D:
+ return ctx->Texture.ProxyTex[TEXTURE_2D_INDEX];
+ case GL_TEXTURE_3D:
+ return texUnit->CurrentTex[TEXTURE_3D_INDEX];
+ case GL_PROXY_TEXTURE_3D:
+ return ctx->Texture.ProxyTex[TEXTURE_3D_INDEX];
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB:
+ case GL_TEXTURE_CUBE_MAP_ARB:
+ return ctx->Extensions.ARB_texture_cube_map
+ ? texUnit->CurrentTex[TEXTURE_CUBE_INDEX] : NULL;
+ case GL_PROXY_TEXTURE_CUBE_MAP_ARB:
+ return ctx->Extensions.ARB_texture_cube_map
+ ? ctx->Texture.ProxyTex[TEXTURE_CUBE_INDEX] : NULL;
+ case GL_TEXTURE_CUBE_MAP_ARRAY:
+ return ctx->Extensions.ARB_texture_cube_map_array
+ ? texUnit->CurrentTex[TEXTURE_CUBE_ARRAY_INDEX] : NULL;
+ case GL_PROXY_TEXTURE_CUBE_MAP_ARRAY:
+ return ctx->Extensions.ARB_texture_cube_map_array
+ ? ctx->Texture.ProxyTex[TEXTURE_CUBE_ARRAY_INDEX] : NULL;
+ case GL_TEXTURE_RECTANGLE_NV:
+ return ctx->Extensions.NV_texture_rectangle
+ ? texUnit->CurrentTex[TEXTURE_RECT_INDEX] : NULL;
+ case GL_PROXY_TEXTURE_RECTANGLE_NV:
+ return ctx->Extensions.NV_texture_rectangle
+ ? ctx->Texture.ProxyTex[TEXTURE_RECT_INDEX] : NULL;
+ case GL_TEXTURE_1D_ARRAY_EXT:
+ return arrayTex ? texUnit->CurrentTex[TEXTURE_1D_ARRAY_INDEX] : NULL;
+ case GL_PROXY_TEXTURE_1D_ARRAY_EXT:
+ return arrayTex ? ctx->Texture.ProxyTex[TEXTURE_1D_ARRAY_INDEX] : NULL;
+ case GL_TEXTURE_2D_ARRAY_EXT:
+ return arrayTex ? texUnit->CurrentTex[TEXTURE_2D_ARRAY_INDEX] : NULL;
+ case GL_PROXY_TEXTURE_2D_ARRAY_EXT:
+ return arrayTex ? ctx->Texture.ProxyTex[TEXTURE_2D_ARRAY_INDEX] : NULL;
+ case GL_TEXTURE_BUFFER:
+ return ctx->API == API_OPENGL_CORE &&
+ ctx->Extensions.ARB_texture_buffer_object ?
+ texUnit->CurrentTex[TEXTURE_BUFFER_INDEX] : NULL;
+ case GL_TEXTURE_EXTERNAL_OES:
+ return _mesa_is_gles(ctx) && ctx->Extensions.OES_EGL_image_external
+ ? texUnit->CurrentTex[TEXTURE_EXTERNAL_INDEX] : NULL;
+ case GL_TEXTURE_2D_MULTISAMPLE:
+ return ctx->Extensions.ARB_texture_multisample
+ ? texUnit->CurrentTex[TEXTURE_2D_MULTISAMPLE_INDEX] : NULL;
+ case GL_PROXY_TEXTURE_2D_MULTISAMPLE:
+ return ctx->Extensions.ARB_texture_multisample
+ ? ctx->Texture.ProxyTex[TEXTURE_2D_MULTISAMPLE_INDEX] : NULL;
+ case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+ return ctx->Extensions.ARB_texture_multisample
+ ? texUnit->CurrentTex[TEXTURE_2D_MULTISAMPLE_ARRAY_INDEX] : NULL;
+ case GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY:
+ return ctx->Extensions.ARB_texture_multisample
+ ? ctx->Texture.ProxyTex[TEXTURE_2D_MULTISAMPLE_ARRAY_INDEX] : NULL;
+ default:
+ _mesa_problem(NULL, "bad target in _mesa_get_current_tex_object()");
+ return NULL;
+ }
+}
/**
*
* Called via ctx->Driver.NewTextureObject, unless overridden by a device
* driver.
- *
+ *
* \param shared the shared GL state structure to contain the texture object
* \param name integer name for the texture object
* \param target either GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D,
struct gl_texture_object *obj;
(void) ctx;
obj = MALLOC_STRUCT(gl_texture_object);
- _mesa_initialize_texture_object(obj, name, target);
+ _mesa_initialize_texture_object(ctx, obj, name, target);
return obj;
}
* \param target the texture target
*/
void
-_mesa_initialize_texture_object( struct gl_texture_object *obj,
+_mesa_initialize_texture_object( struct gl_context *ctx,
+ struct gl_texture_object *obj,
GLuint name, GLenum target )
{
- ASSERT(target == 0 ||
+ assert(target == 0 ||
target == GL_TEXTURE_1D ||
target == GL_TEXTURE_2D ||
target == GL_TEXTURE_3D ||
target == GL_TEXTURE_1D_ARRAY_EXT ||
target == GL_TEXTURE_2D_ARRAY_EXT ||
target == GL_TEXTURE_EXTERNAL_OES ||
- target == GL_TEXTURE_BUFFER);
+ target == GL_TEXTURE_CUBE_MAP_ARRAY ||
+ target == GL_TEXTURE_BUFFER ||
+ target == GL_TEXTURE_2D_MULTISAMPLE ||
+ target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY);
memset(obj, 0, sizeof(*obj));
/* init the non-zero fields */
- _glthread_INIT_MUTEX(obj->Mutex);
+ mtx_init(&obj->Mutex, mtx_plain);
obj->RefCount = 1;
obj->Name = name;
obj->Target = target;
obj->Sampler.MaxAnisotropy = 1.0;
obj->Sampler.CompareMode = GL_NONE; /* ARB_shadow */
obj->Sampler.CompareFunc = GL_LEQUAL; /* ARB_shadow */
- obj->Sampler.CompareFailValue = 0.0F; /* ARB_shadow_ambient */
- obj->Sampler.DepthMode = GL_LUMINANCE; /* ARB_depth_texture */
+ obj->DepthMode = ctx->API == API_OPENGL_CORE ? GL_RED : GL_LUMINANCE;
+ obj->StencilSampling = false;
obj->Sampler.CubeMapSeamless = GL_FALSE;
obj->Swizzle[0] = GL_RED;
obj->Swizzle[1] = GL_GREEN;
obj->Swizzle[3] = GL_ALPHA;
obj->_Swizzle = SWIZZLE_NOOP;
obj->Sampler.sRGBDecode = GL_DECODE_EXT;
+ obj->BufferObjectFormat = GL_R8;
+ obj->_BufferObjectFormat = MESA_FORMAT_R_UNORM8;
+ obj->ImageFormatCompatibilityType = GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE;
}
finish_texture_init(struct gl_context *ctx, GLenum target,
struct gl_texture_object *obj)
{
+ GLenum filter = GL_LINEAR;
assert(obj->Target == 0);
- if (target == GL_TEXTURE_RECTANGLE_NV ||
- target == GL_TEXTURE_EXTERNAL_OES) {
- /* have to init wrap and filter state here - kind of klunky */
- obj->Sampler.WrapS = GL_CLAMP_TO_EDGE;
- obj->Sampler.WrapT = GL_CLAMP_TO_EDGE;
- obj->Sampler.WrapR = GL_CLAMP_TO_EDGE;
- obj->Sampler.MinFilter = GL_LINEAR;
- if (ctx->Driver.TexParameter) {
- static const GLfloat fparam_wrap[1] = {(GLfloat) GL_CLAMP_TO_EDGE};
- static const GLfloat fparam_filter[1] = {(GLfloat) GL_LINEAR};
- ctx->Driver.TexParameter(ctx, target, obj, GL_TEXTURE_WRAP_S, fparam_wrap);
- ctx->Driver.TexParameter(ctx, target, obj, GL_TEXTURE_WRAP_T, fparam_wrap);
- ctx->Driver.TexParameter(ctx, target, obj, GL_TEXTURE_WRAP_R, fparam_wrap);
- ctx->Driver.TexParameter(ctx, target, obj, GL_TEXTURE_MIN_FILTER, fparam_filter);
- }
+ switch (target) {
+ case GL_TEXTURE_2D_MULTISAMPLE:
+ case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+ filter = GL_NEAREST;
+ /* fallthrough */
+
+ case GL_TEXTURE_RECTANGLE_NV:
+ case GL_TEXTURE_EXTERNAL_OES:
+ /* have to init wrap and filter state here - kind of klunky */
+ obj->Sampler.WrapS = GL_CLAMP_TO_EDGE;
+ obj->Sampler.WrapT = GL_CLAMP_TO_EDGE;
+ obj->Sampler.WrapR = GL_CLAMP_TO_EDGE;
+ obj->Sampler.MinFilter = filter;
+ obj->Sampler.MagFilter = filter;
+ if (ctx->Driver.TexParameter) {
+ static const GLfloat fparam_wrap[1] = {(GLfloat) GL_CLAMP_TO_EDGE};
+ const GLfloat fparam_filter[1] = {(GLfloat) filter};
+ ctx->Driver.TexParameter(ctx, obj, GL_TEXTURE_WRAP_S, fparam_wrap);
+ ctx->Driver.TexParameter(ctx, obj, GL_TEXTURE_WRAP_T, fparam_wrap);
+ ctx->Driver.TexParameter(ctx, obj, GL_TEXTURE_WRAP_R, fparam_wrap);
+ ctx->Driver.TexParameter(ctx, obj,
+ GL_TEXTURE_MIN_FILTER, fparam_filter);
+ ctx->Driver.TexParameter(ctx, obj,
+ GL_TEXTURE_MAG_FILTER, fparam_filter);
+ }
+ break;
+
+ default:
+ /* nothing needs done */
+ break;
}
}
_mesa_reference_buffer_object(ctx, &texObj->BufferObject, NULL);
/* destroy the mutex -- it may have allocated memory (eg on bsd) */
- _glthread_DESTROY_MUTEX(texObj->Mutex);
+ mtx_destroy(&texObj->Mutex);
+
+ free(texObj->Label);
/* free this object */
free(texObj);
}
-
/**
* Copy texture object state from one texture object to another.
* Use for glPush/PopAttrib.
const struct gl_texture_object *src )
{
dest->Target = src->Target;
+ dest->TargetIndex = src->TargetIndex;
dest->Name = src->Name;
dest->Priority = src->Priority;
dest->Sampler.BorderColor.f[0] = src->Sampler.BorderColor.f[0];
dest->Sampler.MaxAnisotropy = src->Sampler.MaxAnisotropy;
dest->Sampler.CompareMode = src->Sampler.CompareMode;
dest->Sampler.CompareFunc = src->Sampler.CompareFunc;
- dest->Sampler.CompareFailValue = src->Sampler.CompareFailValue;
dest->Sampler.CubeMapSeamless = src->Sampler.CubeMapSeamless;
- dest->Sampler.DepthMode = src->Sampler.DepthMode;
+ dest->DepthMode = src->DepthMode;
+ dest->StencilSampling = src->StencilSampling;
dest->Sampler.sRGBDecode = src->Sampler.sRGBDecode;
dest->_MaxLevel = src->_MaxLevel;
dest->_MaxLambda = src->_MaxLambda;
dest->GenerateMipmap = src->GenerateMipmap;
- dest->_Complete = src->_Complete;
+ dest->_BaseComplete = src->_BaseComplete;
+ dest->_MipmapComplete = src->_MipmapComplete;
COPY_4V(dest->Swizzle, src->Swizzle);
dest->_Swizzle = src->_Swizzle;
+ dest->_IsHalfFloat = src->_IsHalfFloat;
+ dest->_IsFloat = src->_IsFloat;
dest->RequiredTextureImageUnits = src->RequiredTextureImageUnits;
}
case GL_TEXTURE_2D_ARRAY_EXT:
case GL_TEXTURE_BUFFER:
case GL_TEXTURE_EXTERNAL_OES:
+ case GL_TEXTURE_CUBE_MAP_ARRAY:
+ case GL_TEXTURE_2D_MULTISAMPLE:
+ case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
return GL_TRUE;
case 0x99:
_mesa_problem(NULL, "invalid reference to a deleted texture object");
GLboolean deleteFlag = GL_FALSE;
struct gl_texture_object *oldTex = *ptr;
- ASSERT(valid_texture_object(oldTex));
+ assert(valid_texture_object(oldTex));
(void) valid_texture_object; /* silence warning in release builds */
- _glthread_LOCK_MUTEX(oldTex->Mutex);
- ASSERT(oldTex->RefCount > 0);
+ mtx_lock(&oldTex->Mutex);
+ assert(oldTex->RefCount > 0);
oldTex->RefCount--;
deleteFlag = (oldTex->RefCount == 0);
- _glthread_UNLOCK_MUTEX(oldTex->Mutex);
+ mtx_unlock(&oldTex->Mutex);
if (deleteFlag) {
+ /* Passing in the context drastically changes the driver code for
+ * framebuffer deletion.
+ */
GET_CURRENT_CONTEXT(ctx);
if (ctx)
ctx->Driver.DeleteTexture(ctx, oldTex);
if (tex) {
/* reference new texture */
- ASSERT(valid_texture_object(tex));
- _glthread_LOCK_MUTEX(tex->Mutex);
+ assert(valid_texture_object(tex));
+ mtx_lock(&tex->Mutex);
if (tex->RefCount == 0) {
/* this texture's being deleted (look just above) */
/* Not sure this can every really happen. Warn if it does. */
tex->RefCount++;
*ptr = tex;
}
- _glthread_UNLOCK_MUTEX(tex->Mutex);
+ mtx_unlock(&tex->Mutex);
}
}
+enum base_mipmap { BASE, MIPMAP };
+
/**
- * Mark a texture object as incomplete.
+ * Mark a texture object as incomplete. There are actually three kinds of
+ * (in)completeness:
+ * 1. "base incomplete": the base level of the texture is invalid so no
+ * texturing is possible.
+ * 2. "mipmap incomplete": a non-base level of the texture is invalid so
+ * mipmap filtering isn't possible, but non-mipmap filtering is.
+ * 3. "texture incompleteness": some combination of texture state and
+ * sampler state renders the texture incomplete.
+ *
* \param t texture object
+ * \param bm either BASE or MIPMAP to indicate what's incomplete
* \param fmt... string describing why it's incomplete (for debugging).
*/
static void
-incomplete(struct gl_texture_object *t, const char *fmt, ...)
+incomplete(struct gl_texture_object *t, enum base_mipmap bm,
+ const char *fmt, ...)
{
-#if 0
- va_list args;
- char s[100];
+ if (MESA_DEBUG_FLAGS & DEBUG_INCOMPLETE_TEXTURE) {
+ va_list args;
+ char s[100];
- va_start(args, fmt);
- vsnprintf(s, sizeof(s), fmt, args);
- va_end(args);
+ va_start(args, fmt);
+ vsnprintf(s, sizeof(s), fmt, args);
+ va_end(args);
+
+ _mesa_debug(NULL, "Texture Obj %d incomplete because: %s\n", t->Name, s);
+ }
- printf("Texture Obj %d incomplete because: %s\n", t->Name, s);
-#endif
- t->_Complete = GL_FALSE;
+ if (bm == BASE)
+ t->_BaseComplete = GL_FALSE;
+ t->_MipmapComplete = GL_FALSE;
}
struct gl_texture_object *t )
{
const GLint baseLevel = t->BaseLevel;
- GLint maxLog2 = 0, maxLevels = 0;
+ const struct gl_texture_image *baseImage;
+ GLint maxLevels = 0;
- t->_Complete = GL_TRUE; /* be optimistic */
+ /* We'll set these to FALSE if tests fail below */
+ t->_BaseComplete = GL_TRUE;
+ t->_MipmapComplete = GL_TRUE;
+
+ if (t->Target == GL_TEXTURE_BUFFER) {
+ /* Buffer textures are always considered complete. The obvious case where
+ * they would be incomplete (no BO attached) is actually specced to be
+ * undefined rendering results.
+ */
+ return;
+ }
/* Detect cases where the application set the base level to an invalid
* value.
*/
if ((baseLevel < 0) || (baseLevel >= MAX_TEXTURE_LEVELS)) {
- incomplete(t, "base level = %d is invalid", baseLevel);
+ incomplete(t, BASE, "base level = %d is invalid", baseLevel);
return;
}
+ if (t->MaxLevel < baseLevel) {
+ incomplete(t, MIPMAP, "MAX_LEVEL (%d) < BASE_LEVEL (%d)",
+ t->MaxLevel, baseLevel);
+ return;
+ }
+
+ baseImage = t->Image[0][baseLevel];
+
/* Always need the base level image */
- if (!t->Image[0][baseLevel]) {
- incomplete(t, "Image[baseLevel=%d] == NULL", baseLevel);
+ if (!baseImage) {
+ incomplete(t, BASE, "Image[baseLevel=%d] == NULL", baseLevel);
return;
}
/* Check width/height/depth for zero */
- if (t->Image[0][baseLevel]->Width == 0 ||
- t->Image[0][baseLevel]->Height == 0 ||
- t->Image[0][baseLevel]->Depth == 0) {
- incomplete(t, "texture width = 0");
+ if (baseImage->Width == 0 ||
+ baseImage->Height == 0 ||
+ baseImage->Depth == 0) {
+ incomplete(t, BASE, "texture width or height or depth = 0");
return;
}
- /* Compute _MaxLevel */
- if ((t->Target == GL_TEXTURE_1D) ||
- (t->Target == GL_TEXTURE_1D_ARRAY_EXT)) {
- maxLog2 = t->Image[0][baseLevel]->WidthLog2;
- maxLevels = ctx->Const.MaxTextureLevels;
+ /* Check if the texture values are integer */
+ {
+ GLenum datatype = _mesa_get_format_datatype(baseImage->TexFormat);
+ t->_IsIntegerFormat = datatype == GL_INT || datatype == GL_UNSIGNED_INT;
}
- else if ((t->Target == GL_TEXTURE_2D) ||
- (t->Target == GL_TEXTURE_2D_ARRAY_EXT)) {
- maxLog2 = MAX2(t->Image[0][baseLevel]->WidthLog2,
- t->Image[0][baseLevel]->HeightLog2);
- maxLevels = ctx->Const.MaxTextureLevels;
+
+ /* Check if the texture type is Float or HalfFloatOES and ensure Min and Mag
+ * filters are supported in this case.
+ */
+ if (_mesa_is_gles(ctx) && !valid_filter_for_float(ctx, t)) {
+ incomplete(t, BASE, "Filter is not supported with Float types.");
+ return;
}
- else if (t->Target == GL_TEXTURE_3D) {
- GLint max = MAX2(t->Image[0][baseLevel]->WidthLog2,
- t->Image[0][baseLevel]->HeightLog2);
- maxLog2 = MAX2(max, (GLint)(t->Image[0][baseLevel]->DepthLog2));
+
+ /* Compute _MaxLevel (the maximum mipmap level we'll sample from given the
+ * mipmap image sizes and GL_TEXTURE_MAX_LEVEL state).
+ */
+ switch (t->Target) {
+ case GL_TEXTURE_1D:
+ case GL_TEXTURE_1D_ARRAY_EXT:
+ maxLevels = ctx->Const.MaxTextureLevels;
+ break;
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_2D_ARRAY_EXT:
+ maxLevels = ctx->Const.MaxTextureLevels;
+ break;
+ case GL_TEXTURE_3D:
maxLevels = ctx->Const.Max3DTextureLevels;
- }
- else if (t->Target == GL_TEXTURE_CUBE_MAP_ARB) {
- maxLog2 = MAX2(t->Image[0][baseLevel]->WidthLog2,
- t->Image[0][baseLevel]->HeightLog2);
+ break;
+ case GL_TEXTURE_CUBE_MAP_ARB:
+ case GL_TEXTURE_CUBE_MAP_ARRAY:
maxLevels = ctx->Const.MaxCubeTextureLevels;
- }
- else if (t->Target == GL_TEXTURE_RECTANGLE_NV ||
- t->Target == GL_TEXTURE_EXTERNAL_OES) {
- maxLog2 = 0; /* not applicable */
+ break;
+ case GL_TEXTURE_RECTANGLE_NV:
+ case GL_TEXTURE_BUFFER:
+ case GL_TEXTURE_EXTERNAL_OES:
+ case GL_TEXTURE_2D_MULTISAMPLE:
+ case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
maxLevels = 1; /* no mipmapping */
- }
- else {
+ break;
+ default:
_mesa_problem(ctx, "Bad t->Target in _mesa_test_texobj_completeness");
return;
}
- ASSERT(maxLevels > 0);
+ assert(maxLevels > 0);
- if (t->MaxLevel < t->BaseLevel) {
- incomplete(t, "MAX_LEVEL (%d) < BASE_LEVEL (%d)",
- t->MaxLevel, t->BaseLevel);
- return;
- }
+ t->_MaxLevel = MIN3(t->MaxLevel,
+ /* 'p' in the GL spec */
+ (int) (baseLevel + baseImage->MaxNumLevels - 1),
+ /* 'q' in the GL spec */
+ maxLevels - 1);
- t->_MaxLevel = baseLevel + maxLog2;
- t->_MaxLevel = MIN2(t->_MaxLevel, t->MaxLevel);
- t->_MaxLevel = MIN2(t->_MaxLevel, maxLevels - 1);
+ if (t->Immutable) {
+ /* Adjust max level for views: the data store may have more levels than
+ * the view exposes.
+ */
+ t->_MaxLevel = MIN2(t->_MaxLevel, t->NumLevels - 1);
+ }
- /* Compute _MaxLambda = q - b (see the 1.2 spec) used during mipmapping */
- t->_MaxLambda = (GLfloat) (t->_MaxLevel - t->BaseLevel);
+ /* Compute _MaxLambda = q - p in the spec used during mipmapping */
+ t->_MaxLambda = (GLfloat) (t->_MaxLevel - baseLevel);
if (t->Immutable) {
/* This texture object was created with glTexStorage1/2/3D() so we
}
if (t->Target == GL_TEXTURE_CUBE_MAP_ARB) {
- /* make sure that all six cube map level 0 images are the same size */
- const GLuint w = t->Image[0][baseLevel]->Width2;
- const GLuint h = t->Image[0][baseLevel]->Height2;
+ /* Make sure that all six cube map level 0 images are the same size.
+ * Note: we know that the image's width==height (we enforce that
+ * at glTexImage time) so we only need to test the width here.
+ */
GLuint face;
+ assert(baseImage->Width2 == baseImage->Height);
for (face = 1; face < 6; face++) {
+ assert(t->Image[face][baseLevel] == NULL ||
+ t->Image[face][baseLevel]->Width2 ==
+ t->Image[face][baseLevel]->Height2);
if (t->Image[face][baseLevel] == NULL ||
- t->Image[face][baseLevel]->Width2 != w ||
- t->Image[face][baseLevel]->Height2 != h) {
- incomplete(t, "Cube face missing or mismatched size");
+ t->Image[face][baseLevel]->Width2 != baseImage->Width2) {
+ incomplete(t, BASE, "Cube face missing or mismatched size");
return;
}
}
}
- /* extra checking for mipmaps */
- if (t->Sampler.MinFilter != GL_NEAREST && t->Sampler.MinFilter != GL_LINEAR) {
- /*
- * Mipmapping: determine if we have a complete set of mipmaps
- */
+ /*
+ * Do mipmap consistency checking.
+ * Note: we don't care about the current texture sampler state here.
+ * To determine texture completeness we'll either look at _BaseComplete
+ * or _MipmapComplete depending on the current minification filter mode.
+ */
+ {
GLint i;
- GLint minLevel = baseLevel;
- GLint maxLevel = t->_MaxLevel;
+ const GLint minLevel = baseLevel;
+ const GLint maxLevel = t->_MaxLevel;
+ const GLuint numFaces = _mesa_num_tex_faces(t->Target);
+ GLuint width, height, depth, face;
if (minLevel > maxLevel) {
- incomplete(t, "minLevel > maxLevel");
+ incomplete(t, MIPMAP, "minLevel > maxLevel");
return;
}
- /* Test dimension-independent attributes */
- for (i = minLevel; i <= maxLevel; i++) {
- if (t->Image[0][i]) {
- if (t->Image[0][i]->TexFormat != t->Image[0][baseLevel]->TexFormat) {
- incomplete(t, "Format[i] != Format[baseLevel]");
- return;
- }
- if (t->Image[0][i]->Border != t->Image[0][baseLevel]->Border) {
- incomplete(t, "Border[i] != Border[baseLevel]");
- return;
- }
- }
- }
+ /* Get the base image's dimensions */
+ width = baseImage->Width2;
+ height = baseImage->Height2;
+ depth = baseImage->Depth2;
- /* Test things which depend on number of texture image dimensions */
- if ((t->Target == GL_TEXTURE_1D) ||
- (t->Target == GL_TEXTURE_1D_ARRAY_EXT)) {
- /* Test 1-D mipmaps */
- GLuint width = t->Image[0][baseLevel]->Width2;
- for (i = baseLevel + 1; i < maxLevels; i++) {
- if (width > 1) {
- width /= 2;
- }
- if (i >= minLevel && i <= maxLevel) {
- const struct gl_texture_image *img = t->Image[0][i];
- if (!img) {
- incomplete(t, "1D Image[%d] is missing", i);
- return;
- }
- if (img->Width2 != width ) {
- incomplete(t, "1D Image[%d] bad width %u", i, img->Width2);
- return;
- }
- }
- if (width == 1) {
- return; /* found smallest needed mipmap, all done! */
- }
+ /* Note: this loop will be a no-op for RECT, BUFFER, EXTERNAL,
+ * MULTISAMPLE and MULTISAMPLE_ARRAY textures
+ */
+ for (i = baseLevel + 1; i < maxLevels; i++) {
+ /* Compute the expected size of image at level[i] */
+ if (width > 1) {
+ width /= 2;
}
- }
- else if ((t->Target == GL_TEXTURE_2D) ||
- (t->Target == GL_TEXTURE_2D_ARRAY_EXT)) {
- /* Test 2-D mipmaps */
- GLuint width = t->Image[0][baseLevel]->Width2;
- GLuint height = t->Image[0][baseLevel]->Height2;
- for (i = baseLevel + 1; i < maxLevels; i++) {
- if (width > 1) {
- width /= 2;
- }
- if (height > 1) {
- height /= 2;
- }
+ if (height > 1 && t->Target != GL_TEXTURE_1D_ARRAY) {
+ height /= 2;
+ }
+ if (depth > 1 && t->Target != GL_TEXTURE_2D_ARRAY
+ && t->Target != GL_TEXTURE_CUBE_MAP_ARRAY) {
+ depth /= 2;
+ }
+
+ /* loop over cube faces (or single face otherwise) */
+ for (face = 0; face < numFaces; face++) {
if (i >= minLevel && i <= maxLevel) {
- const struct gl_texture_image *img = t->Image[0][i];
+ const struct gl_texture_image *img = t->Image[face][i];
+
if (!img) {
- incomplete(t, "2D Image[%d of %d] is missing", i, maxLevel);
- return;
- }
- if (img->Width2 != width) {
- incomplete(t, "2D Image[%d] bad width %u", i, img->Width2);
- return;
- }
- if (img->Height2 != height) {
- incomplete(t, "2D Image[i] bad height %u", i, img->Height2);
+ incomplete(t, MIPMAP, "TexImage[%d] is missing", i);
return;
}
- if (width==1 && height==1) {
- return; /* found smallest needed mipmap, all done! */
- }
- }
- }
- }
- else if (t->Target == GL_TEXTURE_3D) {
- /* Test 3-D mipmaps */
- GLuint width = t->Image[0][baseLevel]->Width2;
- GLuint height = t->Image[0][baseLevel]->Height2;
- GLuint depth = t->Image[0][baseLevel]->Depth2;
- for (i = baseLevel + 1; i < maxLevels; i++) {
- if (width > 1) {
- width /= 2;
- }
- if (height > 1) {
- height /= 2;
- }
- if (depth > 1) {
- depth /= 2;
- }
- if (i >= minLevel && i <= maxLevel) {
- const struct gl_texture_image *img = t->Image[0][i];
- if (!img) {
- incomplete(t, "3D Image[%d] is missing", i);
+ if (img->TexFormat != baseImage->TexFormat) {
+ incomplete(t, MIPMAP, "Format[i] != Format[baseLevel]");
return;
}
- if (img->_BaseFormat == GL_DEPTH_COMPONENT) {
- incomplete(t, "GL_DEPTH_COMPONENT only works with 1/2D tex");
+ if (img->Border != baseImage->Border) {
+ incomplete(t, MIPMAP, "Border[i] != Border[baseLevel]");
return;
}
if (img->Width2 != width) {
- incomplete(t, "3D Image[%d] bad width %u", i, img->Width2);
+ incomplete(t, MIPMAP, "TexImage[%d] bad width %u", i,
+ img->Width2);
return;
}
if (img->Height2 != height) {
- incomplete(t, "3D Image[%d] bad height %u", i, img->Height2);
+ incomplete(t, MIPMAP, "TexImage[%d] bad height %u", i,
+ img->Height2);
return;
}
if (img->Depth2 != depth) {
- incomplete(t, "3D Image[%d] bad depth %u", i, img->Depth2);
+ incomplete(t, MIPMAP, "TexImage[%d] bad depth %u", i,
+ img->Depth2);
return;
}
- }
- if (width == 1 && height == 1 && depth == 1) {
- return; /* found smallest needed mipmap, all done! */
- }
- }
- }
- else if (t->Target == GL_TEXTURE_CUBE_MAP_ARB) {
- /* make sure 6 cube faces are consistant */
- GLuint width = t->Image[0][baseLevel]->Width2;
- GLuint height = t->Image[0][baseLevel]->Height2;
- for (i = baseLevel + 1; i < maxLevels; i++) {
- if (width > 1) {
- width /= 2;
- }
- if (height > 1) {
- height /= 2;
- }
- if (i >= minLevel && i <= maxLevel) {
- GLuint face;
- for (face = 0; face < 6; face++) {
- /* check that we have images defined */
- if (!t->Image[face][i]) {
- incomplete(t, "CubeMap Image[n][i] == NULL");
- return;
- }
- /* Don't support GL_DEPTH_COMPONENT for cube maps */
- if (ctx->VersionMajor < 3 && !ctx->Extensions.EXT_gpu_shader4) {
- if (t->Image[face][i]->_BaseFormat == GL_DEPTH_COMPONENT) {
- incomplete(t, "GL_DEPTH_COMPONENT only works with 1/2D tex");
- return;
- }
- }
- /* check that all six images have same size */
- if (t->Image[face][i]->Width2 != width ||
- t->Image[face][i]->Height2 != height) {
- incomplete(t, "CubeMap Image[n][i] bad size");
+
+ /* Extra checks for cube textures */
+ if (face > 0) {
+ /* check that cube faces are the same size */
+ if (img->Width2 != t->Image[0][i]->Width2 ||
+ img->Height2 != t->Image[0][i]->Height2) {
+ incomplete(t, MIPMAP, "CubeMap Image[n][i] bad size");
return;
}
- }
- }
- if (width == 1 && height == 1) {
- return; /* found smallest needed mipmap, all done! */
+ }
}
}
- }
- else if (t->Target == GL_TEXTURE_RECTANGLE_NV) {
- /* XXX special checking? */
- }
- else {
- /* Target = ??? */
- _mesa_problem(ctx, "Bug in gl_test_texture_object_completeness\n");
+
+ if (width == 1 && height == 1 && depth == 1) {
+ return; /* found smallest needed mipmap, all done! */
+ }
}
}
}
-/**
- * Check if the given cube map texture is "cube complete" as defined in
- * the OpenGL specification.
- */
GLboolean
-_mesa_cube_complete(const struct gl_texture_object *texObj)
+_mesa_cube_level_complete(const struct gl_texture_object *texObj,
+ const GLint level)
{
- const GLint baseLevel = texObj->BaseLevel;
const struct gl_texture_image *img0, *img;
GLuint face;
if (texObj->Target != GL_TEXTURE_CUBE_MAP)
return GL_FALSE;
- if ((baseLevel < 0) || (baseLevel >= MAX_TEXTURE_LEVELS))
+ if ((level < 0) || (level >= MAX_TEXTURE_LEVELS))
return GL_FALSE;
/* check first face */
- img0 = texObj->Image[0][baseLevel];
+ img0 = texObj->Image[0][level];
if (!img0 ||
img0->Width < 1 ||
img0->Width != img0->Height)
/* check remaining faces vs. first face */
for (face = 1; face < 6; face++) {
- img = texObj->Image[face][baseLevel];
+ img = texObj->Image[face][level];
if (!img ||
img->Width != img0->Width ||
img->Height != img0->Height ||
return GL_TRUE;
}
+/**
+ * Check if the given cube map texture is "cube complete" as defined in
+ * the OpenGL specification.
+ */
+GLboolean
+_mesa_cube_complete(const struct gl_texture_object *texObj)
+{
+ return _mesa_cube_level_complete(texObj, texObj->BaseLevel);
+}
/**
* Mark a texture object dirty. It forces the object to be incomplete
- * and optionally forces the context to re-validate its state.
+ * and forces the context to re-validate its state.
*
* \param ctx GL context.
* \param texObj texture object.
- * \param invalidate_state also invalidate context state.
*/
void
-_mesa_dirty_texobj(struct gl_context *ctx, struct gl_texture_object *texObj,
- GLboolean invalidate_state)
+_mesa_dirty_texobj(struct gl_context *ctx, struct gl_texture_object *texObj)
{
- texObj->_Complete = GL_FALSE;
- if (invalidate_state)
- ctx->NewState |= _NEW_TEXTURE;
+ texObj->_BaseComplete = GL_FALSE;
+ texObj->_MipmapComplete = GL_FALSE;
+ ctx->NewState |= _NEW_TEXTURE;
}
{
if (!ctx->Shared->FallbackTex[tex]) {
/* create fallback texture now */
- const GLsizei width = 1, height = 1, depth = 1;
- GLubyte texel[4];
+ const GLsizei width = 1, height = 1;
+ GLsizei depth = 1;
+ GLubyte texel[24];
struct gl_texture_object *texObj;
struct gl_texture_image *texImage;
- gl_format texFormat;
+ mesa_format texFormat;
GLuint dims, face, numFaces = 1;
GLenum target;
- texel[0] =
- texel[1] =
- texel[2] = 0x0;
- texel[3] = 0xff;
+ for (face = 0; face < 6; face++) {
+ texel[4*face + 0] =
+ texel[4*face + 1] =
+ texel[4*face + 2] = 0x0;
+ texel[4*face + 3] = 0xff;
+ }
switch (tex) {
case TEXTURE_2D_ARRAY_INDEX:
target = GL_TEXTURE_1D;
break;
case TEXTURE_BUFFER_INDEX:
+ dims = 0;
+ target = GL_TEXTURE_BUFFER;
+ break;
+ case TEXTURE_CUBE_ARRAY_INDEX:
+ dims = 3;
+ target = GL_TEXTURE_CUBE_MAP_ARRAY;
+ depth = 6;
+ break;
case TEXTURE_EXTERNAL_INDEX:
+ dims = 2;
+ target = GL_TEXTURE_EXTERNAL_OES;
+ break;
+ case TEXTURE_2D_MULTISAMPLE_INDEX:
+ dims = 2;
+ target = GL_TEXTURE_2D_MULTISAMPLE;
+ break;
+ case TEXTURE_2D_MULTISAMPLE_ARRAY_INDEX:
+ dims = 3;
+ target = GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
+ break;
default:
/* no-op */
return NULL;
texObj->Sampler.MinFilter = GL_NEAREST;
texObj->Sampler.MagFilter = GL_NEAREST;
- texFormat = ctx->Driver.ChooseTextureFormat(ctx, GL_RGBA, GL_RGBA,
+ texFormat = ctx->Driver.ChooseTextureFormat(ctx, target,
+ GL_RGBA, GL_RGBA,
GL_UNSIGNED_BYTE);
/* need a loop here just for cube maps */
0, /* border */
GL_RGBA, texFormat);
- switch (dims) {
- case 1:
- ctx->Driver.TexImage1D(ctx, texImage, GL_RGBA,
- width, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, texel,
- &ctx->DefaultPacking);
- break;
- case 2:
- ctx->Driver.TexImage2D(ctx, texImage, GL_RGBA,
- width, height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, texel,
- &ctx->DefaultPacking);
- break;
- case 3:
- ctx->Driver.TexImage3D(ctx, texImage, GL_RGBA,
- width, height, depth, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, texel,
- &ctx->DefaultPacking);
- break;
- default:
- _mesa_problem(ctx, "bad dims in _mesa_get_fallback_texture()");
- }
+ ctx->Driver.TexImage(ctx, dims, texImage,
+ GL_RGBA, GL_UNSIGNED_BYTE, texel,
+ &ctx->DefaultPacking);
}
_mesa_test_texobj_completeness(ctx, texObj);
- assert(texObj->_Complete);
+ assert(texObj->_BaseComplete);
+ assert(texObj->_MipmapComplete);
ctx->Shared->FallbackTex[tex] = texObj;
}
}
-/*@}*/
-
+/**
+ * Compute the size of the given texture object, in bytes.
+ */
+static GLuint
+texture_size(const struct gl_texture_object *texObj)
+{
+ const GLuint numFaces = _mesa_num_tex_faces(texObj->Target);
+ GLuint face, level, size = 0;
+
+ for (face = 0; face < numFaces; face++) {
+ for (level = 0; level < MAX_TEXTURE_LEVELS; level++) {
+ const struct gl_texture_image *img = texObj->Image[face][level];
+ if (img) {
+ GLuint sz = _mesa_format_image_size(img->TexFormat, img->Width,
+ img->Height, img->Depth);
+ size += sz;
+ }
+ }
+ }
-/***********************************************************************/
-/** \name API functions */
-/*@{*/
+ return size;
+}
/**
- * Generate texture names.
- *
- * \param n number of texture names to be generated.
- * \param textures an array in which will hold the generated texture names.
- *
- * \sa glGenTextures().
- *
- * Calls _mesa_HashFindFreeKeyBlock() to find a block of free texture
- * IDs which are stored in \p textures. Corresponding empty texture
- * objects are also generated.
- */
-void GLAPIENTRY
-_mesa_GenTextures( GLsizei n, GLuint *textures )
+ * Callback called from _mesa_HashWalk()
+ */
+static void
+count_tex_size(GLuint key, void *data, void *userData)
{
- GET_CURRENT_CONTEXT(ctx);
- GLuint first;
- GLint i;
- ASSERT_OUTSIDE_BEGIN_END(ctx);
+ const struct gl_texture_object *texObj =
+ (const struct gl_texture_object *) data;
+ GLuint *total = (GLuint *) userData;
- if (n < 0) {
- _mesa_error( ctx, GL_INVALID_VALUE, "glGenTextures" );
- return;
- }
-
- if (!textures)
- return;
+ (void) key;
- /*
- * This must be atomic (generation and allocation of texture IDs)
- */
- _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
+ *total = *total + texture_size(texObj);
+}
- first = _mesa_HashFindFreeKeyBlock(ctx->Shared->TexObjects, n);
- /* Allocate new, empty texture objects */
- for (i = 0; i < n; i++) {
- struct gl_texture_object *texObj;
- GLuint name = first + i;
- GLenum target = 0;
- texObj = ctx->Driver.NewTextureObject(ctx, name, target);
- if (!texObj) {
- _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
- _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenTextures");
- return;
- }
+/**
+ * Compute total size (in bytes) of all textures for the given context.
+ * For debugging purposes.
+ */
+GLuint
+_mesa_total_texture_memory(struct gl_context *ctx)
+{
+ GLuint tgt, total = 0;
- /* insert into hash table */
- _mesa_HashInsert(ctx->Shared->TexObjects, texObj->Name, texObj);
+ _mesa_HashWalk(ctx->Shared->TexObjects, count_tex_size, &total);
- textures[i] = name;
+ /* plus, the default texture objects */
+ for (tgt = 0; tgt < NUM_TEXTURE_TARGETS; tgt++) {
+ total += texture_size(ctx->Shared->DefaultTex[tgt]);
}
- _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+ return total;
}
/**
- * Check if the given texture object is bound to the current draw or
- * read framebuffer. If so, Unbind it.
+ * Return the base format for the given texture object by looking
+ * at the base texture image.
+ * \return base format (such as GL_RGBA) or GL_NONE if it can't be determined
*/
-static void
-unbind_texobj_from_fbo(struct gl_context *ctx,
- struct gl_texture_object *texObj)
+GLenum
+_mesa_texture_base_format(const struct gl_texture_object *texObj)
{
- const GLuint n = (ctx->DrawBuffer == ctx->ReadBuffer) ? 1 : 2;
- GLuint i;
+ const struct gl_texture_image *texImage = _mesa_base_tex_image(texObj);
- for (i = 0; i < n; i++) {
- struct gl_framebuffer *fb = (i == 0) ? ctx->DrawBuffer : ctx->ReadBuffer;
- if (_mesa_is_user_fbo(fb)) {
- GLuint j;
- for (j = 0; j < BUFFER_COUNT; j++) {
- if (fb->Attachment[j].Type == GL_TEXTURE &&
- fb->Attachment[j].Texture == texObj) {
- /* Vertices are already flushed by _mesa_DeleteTextures */
- ctx->NewState |= _NEW_BUFFERS;
- _mesa_remove_attachment(ctx, fb->Attachment + j);
- }
- }
- }
- }
+ return texImage ? texImage->_BaseFormat : GL_NONE;
}
-/**
- * Check if the given texture object is bound to any texture image units and
- * unbind it if so (revert to default textures).
- */
+static struct gl_texture_object *
+invalidate_tex_image_error_check(struct gl_context *ctx, GLuint texture,
+ GLint level, const char *name)
+{
+ /* The GL_ARB_invalidate_subdata spec says:
+ *
+ * "If <texture> is zero or is not the name of a texture, the error
+ * INVALID_VALUE is generated."
+ *
+ * This performs the error check in a different order than listed in the
+ * spec. We have to get the texture object before we can validate the
+ * other parameters against values in the texture object.
+ */
+ struct gl_texture_object *const t = _mesa_lookup_texture(ctx, texture);
+ if (texture == 0 || t == NULL) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "%s(texture)", name);
+ return NULL;
+ }
+
+ /* The GL_ARB_invalidate_subdata spec says:
+ *
+ * "If <level> is less than zero or greater than the base 2 logarithm
+ * of the maximum texture width, height, or depth, the error
+ * INVALID_VALUE is generated."
+ */
+ if (level < 0 || level > t->MaxLevel) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "%s(level)", name);
+ return NULL;
+ }
+
+ /* The GL_ARB_invalidate_subdata spec says:
+ *
+ * "If the target of <texture> is TEXTURE_RECTANGLE, TEXTURE_BUFFER,
+ * TEXTURE_2D_MULTISAMPLE, or TEXTURE_2D_MULTISAMPLE_ARRAY, and <level>
+ * is not zero, the error INVALID_VALUE is generated."
+ */
+ if (level != 0) {
+ switch (t->Target) {
+ case GL_TEXTURE_RECTANGLE:
+ case GL_TEXTURE_BUFFER:
+ case GL_TEXTURE_2D_MULTISAMPLE:
+ case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+ _mesa_error(ctx, GL_INVALID_VALUE, "%s(level)", name);
+ return NULL;
+
+ default:
+ break;
+ }
+ }
+
+ return t;
+}
+
+/**
+ * Wrapper for the driver function. Need this because _mesa_new_texture_object
+ * permits a target of 0 and does not initialize targetIndex.
+ */
+struct gl_texture_object *
+_mesa_create_nameless_texture(struct gl_context *ctx, GLenum target)
+{
+ struct gl_texture_object *texObj = NULL;
+ GLint targetIndex;
+
+ if (target == 0)
+ return texObj;
+
+ texObj = ctx->Driver.NewTextureObject(ctx, 0, target);
+ targetIndex = _mesa_tex_target_to_index(ctx, texObj->Target);
+ assert(targetIndex < NUM_TEXTURE_TARGETS);
+ texObj->TargetIndex = targetIndex;
+
+ return texObj;
+}
+
+/**
+ * Helper function for glCreateTextures and glGenTextures. Need this because
+ * glCreateTextures should throw errors if target = 0. This is not exposed to
+ * the rest of Mesa to encourage Mesa internals to use nameless textures,
+ * which do not require expensive hash lookups.
+ */
+static void
+create_textures(struct gl_context *ctx, GLenum target,
+ GLsizei n, GLuint *textures, bool dsa)
+{
+ GLuint first;
+ GLint i;
+ const char *func = dsa ? "Create" : "Gen";
+
+ if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
+ _mesa_debug(ctx, "gl%sTextures %d\n", func, n);
+
+ if (n < 0) {
+ _mesa_error( ctx, GL_INVALID_VALUE, "gl%sTextures(n < 0)", func );
+ return;
+ }
+
+ if (!textures)
+ return;
+
+ /*
+ * This must be atomic (generation and allocation of texture IDs)
+ */
+ mtx_lock(&ctx->Shared->Mutex);
+
+ first = _mesa_HashFindFreeKeyBlock(ctx->Shared->TexObjects, n);
+
+ /* Allocate new, empty texture objects */
+ for (i = 0; i < n; i++) {
+ struct gl_texture_object *texObj;
+ GLint targetIndex;
+ GLuint name = first + i;
+ texObj = ctx->Driver.NewTextureObject(ctx, name, target);
+ if (!texObj) {
+ mtx_unlock(&ctx->Shared->Mutex);
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "gl%sTextures", func);
+ return;
+ }
+
+ /* Initialize the target index if target is non-zero. */
+ if (target != 0) {
+ targetIndex = _mesa_tex_target_to_index(ctx, texObj->Target);
+ if (targetIndex < 0) { /* Bad Target */
+ mtx_unlock(&ctx->Shared->Mutex);
+ _mesa_error(ctx, GL_INVALID_ENUM, "gl%sTextures(target = %s)",
+ func, _mesa_enum_to_string(texObj->Target));
+ return;
+ }
+ assert(targetIndex < NUM_TEXTURE_TARGETS);
+ texObj->TargetIndex = targetIndex;
+ }
+
+ /* insert into hash table */
+ _mesa_HashInsert(ctx->Shared->TexObjects, texObj->Name, texObj);
+
+ textures[i] = name;
+ }
+
+ mtx_unlock(&ctx->Shared->Mutex);
+}
+
+/*@}*/
+
+
+/***********************************************************************/
+/** \name API functions */
+/*@{*/
+
+
+/**
+ * Generate texture names.
+ *
+ * \param n number of texture names to be generated.
+ * \param textures an array in which will hold the generated texture names.
+ *
+ * \sa glGenTextures(), glCreateTextures().
+ *
+ * Calls _mesa_HashFindFreeKeyBlock() to find a block of free texture
+ * IDs which are stored in \p textures. Corresponding empty texture
+ * objects are also generated.
+ */
+void GLAPIENTRY
+_mesa_GenTextures(GLsizei n, GLuint *textures)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ create_textures(ctx, 0, n, textures, false);
+}
+
+/**
+ * Create texture objects.
+ *
+ * \param target the texture target for each name to be generated.
+ * \param n number of texture names to be generated.
+ * \param textures an array in which will hold the generated texture names.
+ *
+ * \sa glCreateTextures(), glGenTextures().
+ *
+ * Calls _mesa_HashFindFreeKeyBlock() to find a block of free texture
+ * IDs which are stored in \p textures. Corresponding empty texture
+ * objects are also generated.
+ */
+void GLAPIENTRY
+_mesa_CreateTextures(GLenum target, GLsizei n, GLuint *textures)
+{
+ GLint targetIndex;
+ GET_CURRENT_CONTEXT(ctx);
+
+ /*
+ * The 4.5 core profile spec (30.10.2014) doesn't specify what
+ * glCreateTextures should do with invalid targets, which was probably an
+ * oversight. This conforms to the spec for glBindTexture.
+ */
+ targetIndex = _mesa_tex_target_to_index(ctx, target);
+ if (targetIndex < 0) {
+ _mesa_error(ctx, GL_INVALID_ENUM, "glCreateTextures(target)");
+ return;
+ }
+
+ create_textures(ctx, target, n, textures, true);
+}
+
+/**
+ * Check if the given texture object is bound to the current draw or
+ * read framebuffer. If so, Unbind it.
+ */
+static void
+unbind_texobj_from_fbo(struct gl_context *ctx,
+ struct gl_texture_object *texObj)
+{
+ bool progress = false;
+
+ /* Section 4.4.2 (Attaching Images to Framebuffer Objects), subsection
+ * "Attaching Texture Images to a Framebuffer," of the OpenGL 3.1 spec
+ * says:
+ *
+ * "If a texture object is deleted while its image is attached to one
+ * or more attachment points in the currently bound framebuffer, then
+ * it is as if FramebufferTexture* had been called, with a texture of
+ * zero, for each attachment point to which this image was attached in
+ * the currently bound framebuffer. In other words, this texture image
+ * is first detached from all attachment points in the currently bound
+ * framebuffer. Note that the texture image is specifically not
+ * detached from any other framebuffer objects. Detaching the texture
+ * image from any other framebuffer objects is the responsibility of
+ * the application."
+ */
+ if (_mesa_is_user_fbo(ctx->DrawBuffer)) {
+ progress = _mesa_detach_renderbuffer(ctx, ctx->DrawBuffer, texObj);
+ }
+ if (_mesa_is_user_fbo(ctx->ReadBuffer)
+ && ctx->ReadBuffer != ctx->DrawBuffer) {
+ progress = _mesa_detach_renderbuffer(ctx, ctx->ReadBuffer, texObj)
+ || progress;
+ }
+
+ if (progress)
+ /* Vertices are already flushed by _mesa_DeleteTextures */
+ ctx->NewState |= _NEW_BUFFERS;
+}
+
+
+/**
+ * Check if the given texture object is bound to any texture image units and
+ * unbind it if so (revert to default textures).
+ */
static void
unbind_texobj_from_texunits(struct gl_context *ctx,
struct gl_texture_object *texObj)
{
- GLuint u, tex;
+ const gl_texture_index index = texObj->TargetIndex;
+ GLuint u;
- for (u = 0; u < Elements(ctx->Texture.Unit); u++) {
+ if (texObj->Target == 0)
+ return;
+
+ for (u = 0; u < ctx->Texture.NumCurrentTexUsed; u++) {
struct gl_texture_unit *unit = &ctx->Texture.Unit[u];
- for (tex = 0; tex < NUM_TEXTURE_TARGETS; tex++) {
- if (texObj == unit->CurrentTex[tex]) {
- _mesa_reference_texobj(&unit->CurrentTex[tex],
- ctx->Shared->DefaultTex[tex]);
- ASSERT(unit->CurrentTex[tex]);
- break;
- }
+
+ if (texObj == unit->CurrentTex[index]) {
+ /* Bind the default texture for this unit/target */
+ _mesa_reference_texobj(&unit->CurrentTex[index],
+ ctx->Shared->DefaultTex[index]);
+ unit->_BoundTextures &= ~(1 << index);
+ }
+ }
+}
+
+
+/**
+ * Check if the given texture object is bound to any shader image unit
+ * and unbind it if that's the case.
+ */
+static void
+unbind_texobj_from_image_units(struct gl_context *ctx,
+ struct gl_texture_object *texObj)
+{
+ GLuint i;
+
+ for (i = 0; i < ctx->Const.MaxImageUnits; i++) {
+ struct gl_image_unit *unit = &ctx->ImageUnits[i];
+
+ if (texObj == unit->TexObj) {
+ _mesa_reference_texobj(&unit->TexObj, NULL);
+ *unit = _mesa_default_image_unit(ctx);
}
}
}
+/**
+ * Unbinds all textures bound to the given texture image unit.
+ */
+static void
+unbind_textures_from_unit(struct gl_context *ctx, GLuint unit)
+{
+ struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
+
+ while (texUnit->_BoundTextures) {
+ const GLuint index = ffs(texUnit->_BoundTextures) - 1;
+ struct gl_texture_object *texObj = ctx->Shared->DefaultTex[index];
+
+ _mesa_reference_texobj(&texUnit->CurrentTex[index], texObj);
+
+ /* Pass BindTexture call to device driver */
+ if (ctx->Driver.BindTexture)
+ ctx->Driver.BindTexture(ctx, unit, 0, texObj);
+
+ texUnit->_BoundTextures &= ~(1 << index);
+ ctx->NewState |= _NEW_TEXTURE;
+ }
+}
+
+
/**
* Delete named textures.
*
{
GET_CURRENT_CONTEXT(ctx);
GLint i;
- ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx); /* too complex */
+
+ if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
+ _mesa_debug(ctx, "glDeleteTextures %d\n", n);
+
+ if (n < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTextures(n < 0)");
+ return;
+ }
+
+ FLUSH_VERTICES(ctx, 0); /* too complex */
+
+ if (n < 0) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glDeleteTextures(n)");
+ return;
+ }
if (!textures)
return;
*/
unbind_texobj_from_texunits(ctx, delObj);
+ /* Check if this texture is currently bound to any shader
+ * image unit. If so, unbind it.
+ * See section 3.9.X of GL_ARB_shader_image_load_store.
+ */
+ unbind_texobj_from_image_units(ctx, delObj);
+
_mesa_unlock_texture(ctx, delObj);
ctx->NewState |= _NEW_TEXTURE;
/* The texture _name_ is now free for re-use.
* Remove it from the hash table now.
*/
- _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
+ mtx_lock(&ctx->Shared->Mutex);
_mesa_HashRemove(ctx->Shared->TexObjects, delObj->Name);
- _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+ mtx_unlock(&ctx->Shared->Mutex);
/* Unreference the texobj. If refcount hits zero, the texture
* will be deleted.
}
}
+/**
+ * This deletes a texObj without altering the hash table.
+ */
+void
+_mesa_delete_nameless_texture(struct gl_context *ctx,
+ struct gl_texture_object *texObj)
+{
+ if (!texObj)
+ return;
+
+ FLUSH_VERTICES(ctx, 0);
+
+ _mesa_lock_texture(ctx, texObj);
+ {
+ /* Check if texture is bound to any framebuffer objects.
+ * If so, unbind.
+ * See section 4.4.2.3 of GL_EXT_framebuffer_object.
+ */
+ unbind_texobj_from_fbo(ctx, texObj);
+
+ /* Check if this texture is currently bound to any texture units.
+ * If so, unbind it.
+ */
+ unbind_texobj_from_texunits(ctx, texObj);
+
+ /* Check if this texture is currently bound to any shader
+ * image unit. If so, unbind it.
+ * See section 3.9.X of GL_ARB_shader_image_load_store.
+ */
+ unbind_texobj_from_image_units(ctx, texObj);
+ }
+ _mesa_unlock_texture(ctx, texObj);
+
+ ctx->NewState |= _NEW_TEXTURE;
+
+ /* Unreference the texobj. If refcount hits zero, the texture
+ * will be deleted.
+ */
+ _mesa_reference_texobj(&texObj, NULL);
+}
+
/**
* Convert a GL texture target enum such as GL_TEXTURE_2D or GL_TEXTURE_3D
* Note that proxy targets are not valid here.
* \return TEXTURE_x_INDEX or -1 if target is invalid
*/
-static GLint
-target_enum_to_index(GLenum target)
+int
+_mesa_tex_target_to_index(const struct gl_context *ctx, GLenum target)
{
switch (target) {
case GL_TEXTURE_1D:
- return TEXTURE_1D_INDEX;
+ return _mesa_is_desktop_gl(ctx) ? TEXTURE_1D_INDEX : -1;
case GL_TEXTURE_2D:
return TEXTURE_2D_INDEX;
case GL_TEXTURE_3D:
- return TEXTURE_3D_INDEX;
- case GL_TEXTURE_CUBE_MAP_ARB:
- return TEXTURE_CUBE_INDEX;
- case GL_TEXTURE_RECTANGLE_NV:
- return TEXTURE_RECT_INDEX;
- case GL_TEXTURE_1D_ARRAY_EXT:
- return TEXTURE_1D_ARRAY_INDEX;
- case GL_TEXTURE_2D_ARRAY_EXT:
- return TEXTURE_2D_ARRAY_INDEX;
- case GL_TEXTURE_BUFFER_ARB:
- return TEXTURE_BUFFER_INDEX;
+ return ctx->API != API_OPENGLES ? TEXTURE_3D_INDEX : -1;
+ case GL_TEXTURE_CUBE_MAP:
+ return ctx->Extensions.ARB_texture_cube_map
+ ? TEXTURE_CUBE_INDEX : -1;
+ case GL_TEXTURE_RECTANGLE:
+ return _mesa_is_desktop_gl(ctx) && ctx->Extensions.NV_texture_rectangle
+ ? TEXTURE_RECT_INDEX : -1;
+ case GL_TEXTURE_1D_ARRAY:
+ return _mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_array
+ ? TEXTURE_1D_ARRAY_INDEX : -1;
+ case GL_TEXTURE_2D_ARRAY:
+ return (_mesa_is_desktop_gl(ctx) && ctx->Extensions.EXT_texture_array)
+ || _mesa_is_gles3(ctx)
+ ? TEXTURE_2D_ARRAY_INDEX : -1;
+ case GL_TEXTURE_BUFFER:
+ return ctx->API == API_OPENGL_CORE &&
+ ctx->Extensions.ARB_texture_buffer_object ?
+ TEXTURE_BUFFER_INDEX : -1;
case GL_TEXTURE_EXTERNAL_OES:
- return TEXTURE_EXTERNAL_INDEX;
+ return _mesa_is_gles(ctx) && ctx->Extensions.OES_EGL_image_external
+ ? TEXTURE_EXTERNAL_INDEX : -1;
+ case GL_TEXTURE_CUBE_MAP_ARRAY:
+ return _mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_texture_cube_map_array
+ ? TEXTURE_CUBE_ARRAY_INDEX : -1;
+ case GL_TEXTURE_2D_MULTISAMPLE:
+ return ((_mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_texture_multisample) ||
+ _mesa_is_gles31(ctx)) ? TEXTURE_2D_MULTISAMPLE_INDEX: -1;
+ case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+ return ((_mesa_is_desktop_gl(ctx) && ctx->Extensions.ARB_texture_multisample) ||
+ _mesa_is_gles31(ctx))
+ ? TEXTURE_2D_MULTISAMPLE_ARRAY_INDEX: -1;
default:
return -1;
}
/**
* Bind a named texture to a texturing target.
- *
+ *
* \param target texture target.
* \param texName texture name.
- *
+ *
* \sa glBindTexture().
*
* Determines the old texture object bound and returns immediately if rebinding
struct gl_texture_unit *texUnit = _mesa_get_current_tex_unit(ctx);
struct gl_texture_object *newTexObj = NULL;
GLint targetIndex;
- ASSERT_OUTSIDE_BEGIN_END(ctx);
if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
_mesa_debug(ctx, "glBindTexture %s %d\n",
- _mesa_lookup_enum_by_nr(target), (GLint) texName);
+ _mesa_enum_to_string(target), (GLint) texName);
- targetIndex = target_enum_to_index(target);
+ targetIndex = _mesa_tex_target_to_index(ctx, target);
if (targetIndex < 0) {
_mesa_error(ctx, GL_INVALID_ENUM, "glBindTexture(target)");
return;
if (newTexObj) {
/* error checking */
if (newTexObj->Target != 0 && newTexObj->Target != target) {
- /* the named texture object's target doesn't match the given target */
+ /* The named texture object's target doesn't match the
+ * given target
+ */
_mesa_error( ctx, GL_INVALID_OPERATION,
"glBindTexture(target mismatch)" );
return;
}
}
else {
+ if (ctx->API == API_OPENGL_CORE) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glBindTexture(non-gen name)");
+ return;
+ }
+
/* if this is a new texture id, allocate a texture object now */
newTexObj = ctx->Driver.NewTextureObject(ctx, texName, target);
if (!newTexObj) {
}
/* and insert it into hash table */
- _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
+ mtx_lock(&ctx->Shared->Mutex);
_mesa_HashInsert(ctx->Shared->TexObjects, texName, newTexObj);
- _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+ mtx_unlock(&ctx->Shared->Mutex);
}
newTexObj->Target = target;
+ newTexObj->TargetIndex = targetIndex;
}
assert(valid_texture_object(newTexObj));
*/
{
GLboolean early_out;
- _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
+ mtx_lock(&ctx->Shared->Mutex);
early_out = ((ctx->Shared->RefCount == 1)
&& (newTexObj == texUnit->CurrentTex[targetIndex]));
- _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
+ mtx_unlock(&ctx->Shared->Mutex);
if (early_out) {
return;
}
* count hits zero.
*/
_mesa_reference_texobj(&texUnit->CurrentTex[targetIndex], newTexObj);
- ASSERT(texUnit->CurrentTex[targetIndex]);
+ ctx->Texture.NumCurrentTexUsed = MAX2(ctx->Texture.NumCurrentTexUsed,
+ ctx->Texture.CurrentUnit + 1);
+ assert(texUnit->CurrentTex[targetIndex]);
+
+ if (texName != 0)
+ texUnit->_BoundTextures |= (1 << targetIndex);
+ else
+ texUnit->_BoundTextures &= ~(1 << targetIndex);
/* Pass BindTexture call to device driver */
if (ctx->Driver.BindTexture)
- ctx->Driver.BindTexture(ctx, target, newTexObj);
+ ctx->Driver.BindTexture(ctx, ctx->Texture.CurrentUnit, target, newTexObj);
+}
+
+/**
+ * Do the actual binding to a numbered texture unit.
+ * The refcount on the previously bound
+ * texture object will be decremented. It'll be deleted if the
+ * count hits zero.
+ */
+static void
+bind_texture_unit(struct gl_context *ctx,
+ GLuint unit,
+ struct gl_texture_object *texObj)
+{
+ struct gl_texture_unit *texUnit;
+
+ /* Get the texture unit (this is an array look-up) */
+ texUnit = _mesa_get_tex_unit_err(ctx, unit, "glBindTextureUnit");
+ if (!texUnit)
+ return;
+
+ /* Check if this texture is only used by this context and is already bound.
+ * If so, just return.
+ */
+ {
+ bool early_out;
+ mtx_lock(&ctx->Shared->Mutex);
+ early_out = ((ctx->Shared->RefCount == 1)
+ && (texObj == texUnit->CurrentTex[texObj->TargetIndex]));
+ mtx_unlock(&ctx->Shared->Mutex);
+ if (early_out) {
+ return;
+ }
+ }
+
+ /* flush before changing binding */
+ FLUSH_VERTICES(ctx, _NEW_TEXTURE);
+
+ _mesa_reference_texobj(&texUnit->CurrentTex[texObj->TargetIndex],
+ texObj);
+ assert(texUnit->CurrentTex[texObj->TargetIndex]);
+ ctx->Texture.NumCurrentTexUsed = MAX2(ctx->Texture.NumCurrentTexUsed,
+ unit + 1);
+ texUnit->_BoundTextures |= (1 << texObj->TargetIndex);
+
+
+ /* Pass BindTexture call to device driver */
+ if (ctx->Driver.BindTexture) {
+ ctx->Driver.BindTexture(ctx, unit, texObj->Target, texObj);
+ }
+}
+
+/**
+ * Bind a named texture to the specified texture unit.
+ *
+ * \param unit texture unit.
+ * \param texture texture name.
+ *
+ * \sa glBindTexture().
+ *
+ * If the named texture is 0, this will reset each target for the specified
+ * texture unit to its default texture.
+ * If the named texture is not 0 or a recognized texture name, this throws
+ * GL_INVALID_OPERATION.
+ */
+void GLAPIENTRY
+_mesa_BindTextureUnit(GLuint unit, GLuint texture)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ struct gl_texture_object *texObj;
+
+ if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
+ _mesa_debug(ctx, "glBindTextureUnit %s %d\n",
+ _mesa_enum_to_string(GL_TEXTURE0+unit), (GLint) texture);
+
+ /* Section 8.1 (Texture Objects) of the OpenGL 4.5 core profile spec
+ * (20141030) says:
+ * "When texture is zero, each of the targets enumerated at the
+ * beginning of this section is reset to its default texture for the
+ * corresponding texture image unit."
+ */
+ if (texture == 0) {
+ unbind_textures_from_unit(ctx, unit);
+ return;
+ }
+
+ /* Get the non-default texture object */
+ texObj = _mesa_lookup_texture(ctx, texture);
+
+ /* Error checking */
+ if (!texObj) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glBindTextureUnit(non-gen name)");
+ return;
+ }
+ if (texObj->Target == 0) {
+ _mesa_error(ctx, GL_INVALID_ENUM, "glBindTextureUnit(target)");
+ return;
+ }
+ assert(valid_texture_object(texObj));
+
+ bind_texture_unit(ctx, unit, texObj);
+}
+
+
+void GLAPIENTRY
+_mesa_BindTextures(GLuint first, GLsizei count, const GLuint *textures)
+{
+ GET_CURRENT_CONTEXT(ctx);
+ GLint i;
+
+ /* The ARB_multi_bind spec says:
+ *
+ * "An INVALID_OPERATION error is generated if <first> + <count>
+ * is greater than the number of texture image units supported
+ * by the implementation."
+ */
+ if (first + count > ctx->Const.MaxCombinedTextureImageUnits) {
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glBindTextures(first=%u + count=%d > the value of "
+ "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS=%u)",
+ first, count, ctx->Const.MaxCombinedTextureImageUnits);
+ return;
+ }
+
+ /* Flush before changing bindings */
+ FLUSH_VERTICES(ctx, 0);
+
+ ctx->Texture.NumCurrentTexUsed = MAX2(ctx->Texture.NumCurrentTexUsed,
+ first + count);
+
+ if (textures) {
+ /* Note that the error semantics for multi-bind commands differ from
+ * those of other GL commands.
+ *
+ * The issues section in the ARB_multi_bind spec says:
+ *
+ * "(11) Typically, OpenGL specifies that if an error is generated by
+ * a command, that command has no effect. This is somewhat
+ * unfortunate for multi-bind commands, because it would require
+ * a first pass to scan the entire list of bound objects for
+ * errors and then a second pass to actually perform the
+ * bindings. Should we have different error semantics?
+ *
+ * RESOLVED: Yes. In this specification, when the parameters for
+ * one of the <count> binding points are invalid, that binding
+ * point is not updated and an error will be generated. However,
+ * other binding points in the same command will be updated if
+ * their parameters are valid and no other error occurs."
+ */
+
+ _mesa_begin_texture_lookups(ctx);
+
+ for (i = 0; i < count; i++) {
+ if (textures[i] != 0) {
+ struct gl_texture_unit *texUnit = &ctx->Texture.Unit[first + i];
+ struct gl_texture_object *current = texUnit->_Current;
+ struct gl_texture_object *texObj;
+
+ if (current && current->Name == textures[i])
+ texObj = current;
+ else
+ texObj = _mesa_lookup_texture_locked(ctx, textures[i]);
+
+ if (texObj && texObj->Target != 0) {
+ const gl_texture_index targetIndex = texObj->TargetIndex;
+
+ if (texUnit->CurrentTex[targetIndex] != texObj) {
+ /* Do the actual binding. The refcount on the previously
+ * bound texture object will be decremented. It will be
+ * deleted if the count hits zero.
+ */
+ _mesa_reference_texobj(&texUnit->CurrentTex[targetIndex],
+ texObj);
+
+ texUnit->_BoundTextures |= (1 << targetIndex);
+ ctx->NewState |= _NEW_TEXTURE;
+
+ /* Pass the BindTexture call to the device driver */
+ if (ctx->Driver.BindTexture)
+ ctx->Driver.BindTexture(ctx, first + i,
+ texObj->Target, texObj);
+ }
+ } else {
+ /* The ARB_multi_bind spec says:
+ *
+ * "An INVALID_OPERATION error is generated if any value
+ * in <textures> is not zero or the name of an existing
+ * texture object (per binding)."
+ */
+ _mesa_error(ctx, GL_INVALID_OPERATION,
+ "glBindTextures(textures[%d]=%u is not zero "
+ "or the name of an existing texture object)",
+ i, textures[i]);
+ }
+ } else {
+ unbind_textures_from_unit(ctx, first + i);
+ }
+ }
+
+ _mesa_end_texture_lookups(ctx);
+ } else {
+ /* Unbind all textures in the range <first> through <first>+<count>-1 */
+ for (i = 0; i < count; i++)
+ unbind_textures_from_unit(ctx, first + i);
+ }
}
/**
* Set texture priorities.
- *
+ *
* \param n number of textures.
* \param texName texture names.
* \param priorities corresponding texture priorities.
- *
+ *
* \sa glPrioritizeTextures().
- *
+ *
* Looks up each texture in the hash, clamps the corresponding priority between
* 0.0 and 1.0, and calls dd_function_table::PrioritizeTexture.
*/
{
GET_CURRENT_CONTEXT(ctx);
GLint i;
- ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+
+ if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
+ _mesa_debug(ctx, "glPrioritizeTextures %d\n", n);
+
+ FLUSH_VERTICES(ctx, 0);
if (n < 0) {
_mesa_error( ctx, GL_INVALID_VALUE, "glPrioritizeTextures" );
/**
* See if textures are loaded in texture memory.
- *
+ *
* \param n number of textures to query.
* \param texName array with the texture names.
* \param residences array which will hold the residence status.
*
- * \return GL_TRUE if all textures are resident and \p residences is left unchanged,
- *
+ * \return GL_TRUE if all textures are resident and
+ * residences is left unchanged,
+ *
* Note: we assume all textures are always resident
*/
GLboolean GLAPIENTRY
GLint i;
ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
+ if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
+ _mesa_debug(ctx, "glAreTexturesResident %d\n", n);
+
if (n < 0) {
_mesa_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident(n)");
return GL_FALSE;
return GL_FALSE;
}
}
-
+
return allResident;
}
*
* \return GL_TRUE if texture name corresponds to a texture, or GL_FALSE
* otherwise.
- *
+ *
* \sa glIsTexture().
*
* Calls _mesa_HashLookup().
GET_CURRENT_CONTEXT(ctx);
ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
+ if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
+ _mesa_debug(ctx, "glIsTexture %d\n", texture);
+
if (!texture)
return GL_FALSE;
void
_mesa_lock_context_textures( struct gl_context *ctx )
{
- _glthread_LOCK_MUTEX(ctx->Shared->TexMutex);
+ mtx_lock(&ctx->Shared->TexMutex);
if (ctx->Shared->TextureStateStamp != ctx->TextureStateTimestamp) {
ctx->NewState |= _NEW_TEXTURE;
_mesa_unlock_context_textures( struct gl_context *ctx )
{
assert(ctx->Shared->TextureStateStamp == ctx->TextureStateTimestamp);
- _glthread_UNLOCK_MUTEX(ctx->Shared->TexMutex);
+ mtx_unlock(&ctx->Shared->TexMutex);
+}
+
+
+void GLAPIENTRY
+_mesa_InvalidateTexSubImage(GLuint texture, GLint level, GLint xoffset,
+ GLint yoffset, GLint zoffset, GLsizei width,
+ GLsizei height, GLsizei depth)
+{
+ struct gl_texture_object *t;
+ struct gl_texture_image *image;
+ GET_CURRENT_CONTEXT(ctx);
+
+ if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
+ _mesa_debug(ctx, "glInvalidateTexSubImage %d\n", texture);
+
+ t = invalidate_tex_image_error_check(ctx, texture, level,
+ "glInvalidateTexSubImage");
+
+ /* The GL_ARB_invalidate_subdata spec says:
+ *
+ * "...the specified subregion must be between -<b> and <dim>+<b> where
+ * <dim> is the size of the dimension of the texture image, and <b> is
+ * the size of the border of that texture image, otherwise
+ * INVALID_VALUE is generated (border is not applied to dimensions that
+ * don't exist in a given texture target)."
+ */
+ image = t->Image[0][level];
+ if (image) {
+ int xBorder;
+ int yBorder;
+ int zBorder;
+ int imageWidth;
+ int imageHeight;
+ int imageDepth;
+
+ /* The GL_ARB_invalidate_subdata spec says:
+ *
+ * "For texture targets that don't have certain dimensions, this
+ * command treats those dimensions as having a size of 1. For
+ * example, to invalidate a portion of a two-dimensional texture,
+ * the application would use <zoffset> equal to zero and <depth>
+ * equal to one."
+ */
+ switch (t->Target) {
+ case GL_TEXTURE_BUFFER:
+ xBorder = 0;
+ yBorder = 0;
+ zBorder = 0;
+ imageWidth = 1;
+ imageHeight = 1;
+ imageDepth = 1;
+ break;
+ case GL_TEXTURE_1D:
+ xBorder = image->Border;
+ yBorder = 0;
+ zBorder = 0;
+ imageWidth = image->Width;
+ imageHeight = 1;
+ imageDepth = 1;
+ break;
+ case GL_TEXTURE_1D_ARRAY:
+ xBorder = image->Border;
+ yBorder = 0;
+ zBorder = 0;
+ imageWidth = image->Width;
+ imageHeight = image->Height;
+ imageDepth = 1;
+ break;
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_CUBE_MAP:
+ case GL_TEXTURE_RECTANGLE:
+ case GL_TEXTURE_2D_MULTISAMPLE:
+ xBorder = image->Border;
+ yBorder = image->Border;
+ zBorder = 0;
+ imageWidth = image->Width;
+ imageHeight = image->Height;
+ imageDepth = 1;
+ break;
+ case GL_TEXTURE_2D_ARRAY:
+ case GL_TEXTURE_CUBE_MAP_ARRAY:
+ case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+ xBorder = image->Border;
+ yBorder = image->Border;
+ zBorder = 0;
+ imageWidth = image->Width;
+ imageHeight = image->Height;
+ imageDepth = image->Depth;
+ break;
+ case GL_TEXTURE_3D:
+ xBorder = image->Border;
+ yBorder = image->Border;
+ zBorder = image->Border;
+ imageWidth = image->Width;
+ imageHeight = image->Height;
+ imageDepth = image->Depth;
+ break;
+ default:
+ assert(!"Should not get here.");
+ xBorder = 0;
+ yBorder = 0;
+ zBorder = 0;
+ imageWidth = 0;
+ imageHeight = 0;
+ imageDepth = 0;
+ break;
+ }
+
+ if (xoffset < -xBorder) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glInvalidateSubTexImage(xoffset)");
+ return;
+ }
+
+ if (xoffset + width > imageWidth + xBorder) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glInvalidateSubTexImage(xoffset+width)");
+ return;
+ }
+
+ if (yoffset < -yBorder) {
+ _mesa_error(ctx, GL_INVALID_VALUE, "glInvalidateSubTexImage(yoffset)");
+ return;
+ }
+
+ if (yoffset + height > imageHeight + yBorder) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glInvalidateSubTexImage(yoffset+height)");
+ return;
+ }
+
+ if (zoffset < -zBorder) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glInvalidateSubTexImage(zoffset)");
+ return;
+ }
+
+ if (zoffset + depth > imageDepth + zBorder) {
+ _mesa_error(ctx, GL_INVALID_VALUE,
+ "glInvalidateSubTexImage(zoffset+depth)");
+ return;
+ }
+ }
+
+ /* We don't actually do anything for this yet. Just return after
+ * validating the parameters and generating the required errors.
+ */
+ return;
+}
+
+
+void GLAPIENTRY
+_mesa_InvalidateTexImage(GLuint texture, GLint level)
+{
+ GET_CURRENT_CONTEXT(ctx);
+
+ if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
+ _mesa_debug(ctx, "glInvalidateTexImage(%d, %d)\n", texture, level);
+
+ invalidate_tex_image_error_check(ctx, texture, level,
+ "glInvalidateTexImage");
+
+ /* We don't actually do anything for this yet. Just return after
+ * validating the parameters and generating the required errors.
+ */
+ return;
}
/*@}*/