/** * \file texobj.c * Texture object management. */ /* * Mesa 3-D graphics library * * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * 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 * 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 #include "bufferobj.h" #include "context.h" #include "enums.h" #include "fbobject.h" #include "formats.h" #include "hash.h" #include "imports.h" #include "macros.h" #include "shaderimage.h" #include "teximage.h" #include "texobj.h" #include "texstate.h" #include "mtypes.h" #include "program/prog_instruction.h" #include "texturebindless.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. */ struct gl_texture_object * _mesa_lookup_texture(struct gl_context *ctx, GLuint id) { return (struct gl_texture_object *) _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 = NULL; if (id > 0) texObj = _mesa_lookup_texture(ctx, id); /* Returns NULL if not found. */ if (!texObj) _mesa_error(ctx, GL_INVALID_OPERATION, "%s(texture)", func); return texObj; } 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 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: case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: case GL_TEXTURE_CUBE_MAP: return ctx->Extensions.ARB_texture_cube_map ? texUnit->CurrentTex[TEXTURE_CUBE_INDEX] : NULL; case GL_PROXY_TEXTURE_CUBE_MAP: return ctx->Extensions.ARB_texture_cube_map ? ctx->Texture.ProxyTex[TEXTURE_CUBE_INDEX] : NULL; case GL_TEXTURE_CUBE_MAP_ARRAY: return _mesa_has_texture_cube_map_array(ctx) ? texUnit->CurrentTex[TEXTURE_CUBE_ARRAY_INDEX] : NULL; case GL_PROXY_TEXTURE_CUBE_MAP_ARRAY: return _mesa_has_texture_cube_map_array(ctx) ? 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 (_mesa_has_ARB_texture_buffer_object(ctx) || _mesa_has_OES_texture_buffer(ctx)) ? 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; } } /** * Allocate and initialize a new texture object. But don't put it into the * texture object hash table. * * 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, * GL_TEXTURE_CUBE_MAP or GL_TEXTURE_RECTANGLE_NV. zero is ok for the sake * of GenTextures() * * \return pointer to new texture object. */ struct gl_texture_object * _mesa_new_texture_object(struct gl_context *ctx, GLuint name, GLenum target) { struct gl_texture_object *obj; obj = MALLOC_STRUCT(gl_texture_object); if (!obj) return NULL; _mesa_initialize_texture_object(ctx, obj, name, target); return obj; } /** * Initialize a new texture object to default values. * \param obj the texture object * \param name the texture name * \param target the texture target */ void _mesa_initialize_texture_object( struct gl_context *ctx, struct gl_texture_object *obj, GLuint name, GLenum target ) { assert(target == 0 || target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || target == GL_TEXTURE_3D || target == GL_TEXTURE_CUBE_MAP || target == GL_TEXTURE_RECTANGLE_NV || target == GL_TEXTURE_1D_ARRAY_EXT || target == GL_TEXTURE_2D_ARRAY_EXT || target == GL_TEXTURE_EXTERNAL_OES || 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 */ simple_mtx_init(&obj->Mutex, mtx_plain); obj->RefCount = 1; obj->Name = name; obj->Target = target; if (target != 0) { obj->TargetIndex = _mesa_tex_target_to_index(ctx, target); } else { obj->TargetIndex = NUM_TEXTURE_TARGETS; /* invalid/error value */ } obj->Priority = 1.0F; obj->BaseLevel = 0; obj->MaxLevel = 1000; /* must be one; no support for (YUV) planes in separate buffers */ obj->RequiredTextureImageUnits = 1; /* sampler state */ if (target == GL_TEXTURE_RECTANGLE_NV || target == GL_TEXTURE_EXTERNAL_OES) { 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; } else { obj->Sampler.WrapS = GL_REPEAT; obj->Sampler.WrapT = GL_REPEAT; obj->Sampler.WrapR = GL_REPEAT; obj->Sampler.MinFilter = GL_NEAREST_MIPMAP_LINEAR; } obj->Sampler.MagFilter = GL_LINEAR; obj->Sampler.MinLod = -1000.0; obj->Sampler.MaxLod = 1000.0; obj->Sampler.LodBias = 0.0; obj->Sampler.MaxAnisotropy = 1.0; obj->Sampler.CompareMode = GL_NONE; /* ARB_shadow */ obj->Sampler.CompareFunc = GL_LEQUAL; /* ARB_shadow */ obj->DepthMode = ctx->API == API_OPENGL_CORE ? GL_RED : GL_LUMINANCE; obj->StencilSampling = false; obj->Sampler.CubeMapSeamless = GL_FALSE; obj->Sampler.HandleAllocated = GL_FALSE; obj->Swizzle[0] = GL_RED; obj->Swizzle[1] = GL_GREEN; obj->Swizzle[2] = GL_BLUE; 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; /* GL_ARB_bindless_texture */ _mesa_init_texture_handles(obj); } /** * Some texture initialization can't be finished until we know which * target it's getting bound to (GL_TEXTURE_1D/2D/etc). */ static void finish_texture_init(struct gl_context *ctx, GLenum target, struct gl_texture_object *obj, int targetIndex) { GLenum filter = GL_LINEAR; assert(obj->Target == 0); obj->Target = target; obj->TargetIndex = targetIndex; assert(obj->TargetIndex < NUM_TEXTURE_TARGETS); 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) { /* XXX we probably don't need to make all these calls */ ctx->Driver.TexParameter(ctx, obj, GL_TEXTURE_WRAP_S); ctx->Driver.TexParameter(ctx, obj, GL_TEXTURE_WRAP_T); ctx->Driver.TexParameter(ctx, obj, GL_TEXTURE_WRAP_R); ctx->Driver.TexParameter(ctx, obj, GL_TEXTURE_MIN_FILTER); ctx->Driver.TexParameter(ctx, obj, GL_TEXTURE_MAG_FILTER); } break; default: /* nothing needs done */ break; } } /** * Deallocate a texture object struct. It should have already been * removed from the texture object pool. * Called via ctx->Driver.DeleteTexture() if not overriden by a driver. * * \param shared the shared GL state to which the object belongs. * \param texObj the texture object to delete. */ void _mesa_delete_texture_object(struct gl_context *ctx, struct gl_texture_object *texObj) { GLuint i, face; /* Set Target to an invalid value. With some assertions elsewhere * we can try to detect possible use of deleted textures. */ texObj->Target = 0x99; /* free the texture images */ for (face = 0; face < 6; face++) { for (i = 0; i < MAX_TEXTURE_LEVELS; i++) { if (texObj->Image[face][i]) { ctx->Driver.DeleteTextureImage(ctx, texObj->Image[face][i]); } } } /* Delete all texture/image handles. */ _mesa_delete_texture_handles(ctx, texObj); _mesa_reference_buffer_object(ctx, &texObj->BufferObject, NULL); /* destroy the mutex -- it may have allocated memory (eg on bsd) */ simple_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. * * \param dest destination texture object. * \param src source texture object. */ void _mesa_copy_texture_object( struct gl_texture_object *dest, 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.BorderColor.f[1] = src->Sampler.BorderColor.f[1]; dest->Sampler.BorderColor.f[2] = src->Sampler.BorderColor.f[2]; dest->Sampler.BorderColor.f[3] = src->Sampler.BorderColor.f[3]; dest->Sampler.WrapS = src->Sampler.WrapS; dest->Sampler.WrapT = src->Sampler.WrapT; dest->Sampler.WrapR = src->Sampler.WrapR; dest->Sampler.MinFilter = src->Sampler.MinFilter; dest->Sampler.MagFilter = src->Sampler.MagFilter; dest->Sampler.MinLod = src->Sampler.MinLod; dest->Sampler.MaxLod = src->Sampler.MaxLod; dest->Sampler.LodBias = src->Sampler.LodBias; dest->BaseLevel = src->BaseLevel; dest->MaxLevel = src->MaxLevel; dest->Sampler.MaxAnisotropy = src->Sampler.MaxAnisotropy; dest->Sampler.CompareMode = src->Sampler.CompareMode; dest->Sampler.CompareFunc = src->Sampler.CompareFunc; dest->Sampler.CubeMapSeamless = src->Sampler.CubeMapSeamless; 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->_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; } /** * Free all texture images of the given texture objectm, except for * \p retainTexImage. * * \param ctx GL context. * \param texObj texture object. * \param retainTexImage a texture image that will \em not be freed. * * \sa _mesa_clear_texture_image(). */ void _mesa_clear_texture_object(struct gl_context *ctx, struct gl_texture_object *texObj, struct gl_texture_image *retainTexImage) { GLuint i, j; if (texObj->Target == 0) return; for (i = 0; i < MAX_FACES; i++) { for (j = 0; j < MAX_TEXTURE_LEVELS; j++) { struct gl_texture_image *texImage = texObj->Image[i][j]; if (texImage && texImage != retainTexImage) _mesa_clear_texture_image(ctx, texImage); } } } /** * Check if the given texture object is valid by examining its Target field. * For debugging only. */ static GLboolean valid_texture_object(const struct gl_texture_object *tex) { switch (tex->Target) { case 0: case GL_TEXTURE_1D: case GL_TEXTURE_2D: case GL_TEXTURE_3D: case GL_TEXTURE_CUBE_MAP: case GL_TEXTURE_RECTANGLE_NV: case GL_TEXTURE_1D_ARRAY_EXT: 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"); return GL_FALSE; default: _mesa_problem(NULL, "invalid texture object Target 0x%x, Id = %u", tex->Target, tex->Name); return GL_FALSE; } } /** * Reference (or unreference) a texture object. * If '*ptr', decrement *ptr's refcount (and delete if it becomes zero). * If 'tex' is non-null, increment its refcount. * This is normally only called from the _mesa_reference_texobj() macro * when there's a real pointer change. */ void _mesa_reference_texobj_(struct gl_texture_object **ptr, struct gl_texture_object *tex) { assert(ptr); if (*ptr) { /* Unreference the old texture */ GLboolean deleteFlag = GL_FALSE; struct gl_texture_object *oldTex = *ptr; assert(valid_texture_object(oldTex)); (void) valid_texture_object; /* silence warning in release builds */ simple_mtx_lock(&oldTex->Mutex); assert(oldTex->RefCount > 0); oldTex->RefCount--; deleteFlag = (oldTex->RefCount == 0); simple_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); else _mesa_problem(NULL, "Unable to delete texture, no context"); } *ptr = NULL; } assert(!*ptr); if (tex) { /* reference new texture */ assert(valid_texture_object(tex)); simple_mtx_lock(&tex->Mutex); assert(tex->RefCount > 0); tex->RefCount++; *ptr = tex; simple_mtx_unlock(&tex->Mutex); } } enum base_mipmap { BASE, MIPMAP }; /** * 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, enum base_mipmap bm, const char *fmt, ...) { 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); _mesa_debug(NULL, "Texture Obj %d incomplete because: %s\n", t->Name, s); } if (bm == BASE) t->_BaseComplete = GL_FALSE; t->_MipmapComplete = GL_FALSE; } /** * Examine a texture object to determine if it is complete. * * The gl_texture_object::Complete flag will be set to GL_TRUE or GL_FALSE * accordingly. * * \param ctx GL context. * \param t texture object. * * According to the texture target, verifies that each of the mipmaps is * present and has the expected size. */ void _mesa_test_texobj_completeness( const struct gl_context *ctx, struct gl_texture_object *t ) { const GLint baseLevel = t->BaseLevel; const struct gl_texture_image *baseImage; GLint maxLevels = 0; /* 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, "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 (!baseImage) { incomplete(t, BASE, "Image[baseLevel=%d] == NULL", baseLevel); return; } /* Check width/height/depth for zero */ if (baseImage->Width == 0 || baseImage->Height == 0 || baseImage->Depth == 0) { incomplete(t, BASE, "texture width or height or depth = 0"); return; } /* Check if the texture values are integer */ { GLenum datatype = _mesa_get_format_datatype(baseImage->TexFormat); t->_IsIntegerFormat = datatype == GL_INT || datatype == GL_UNSIGNED_INT; } /* 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; } /* 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; break; case GL_TEXTURE_CUBE_MAP: case GL_TEXTURE_CUBE_MAP_ARRAY: maxLevels = ctx->Const.MaxCubeTextureLevels; 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 */ break; default: _mesa_problem(ctx, "Bad t->Target in _mesa_test_texobj_completeness"); return; } assert(maxLevels > 0); t->_MaxLevel = MIN3(t->MaxLevel, /* 'p' in the GL spec */ (int) (baseLevel + baseImage->MaxNumLevels - 1), /* 'q' in the GL spec */ 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 - 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 * know that all the mipmap levels are the right size and all cube * map faces are the same size. * We don't need to do any of the additional checks below. */ return; } if (t->Target == GL_TEXTURE_CUBE_MAP) { /* Make sure that all six cube map level 0 images are the same size and * format. * 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 != baseImage->Width2) { incomplete(t, BASE, "Cube face missing or mismatched size"); return; } if (t->Image[face][baseLevel]->InternalFormat != baseImage->InternalFormat) { incomplete(t, BASE, "Cube face format mismatch"); return; } if (t->Image[face][baseLevel]->Border != baseImage->Border) { incomplete(t, BASE, "Cube face border size mismatch"); return; } } } /* * 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; 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, MIPMAP, "minLevel > maxLevel"); return; } /* Get the base image's dimensions */ width = baseImage->Width2; height = baseImage->Height2; depth = baseImage->Depth2; /* 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; } 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[face][i]; if (!img) { incomplete(t, MIPMAP, "TexImage[%d] is missing", i); return; } if (img->InternalFormat != baseImage->InternalFormat) { incomplete(t, MIPMAP, "Format[i] != Format[baseLevel]"); return; } if (img->Border != baseImage->Border) { incomplete(t, MIPMAP, "Border[i] != Border[baseLevel]"); return; } if (img->Width2 != width) { incomplete(t, MIPMAP, "TexImage[%d] bad width %u", i, img->Width2); return; } if (img->Height2 != height) { incomplete(t, MIPMAP, "TexImage[%d] bad height %u", i, img->Height2); return; } if (img->Depth2 != depth) { 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! */ } } } } GLboolean _mesa_cube_level_complete(const struct gl_texture_object *texObj, const GLint level) { const struct gl_texture_image *img0, *img; GLuint face; if (texObj->Target != GL_TEXTURE_CUBE_MAP) return GL_FALSE; if ((level < 0) || (level >= MAX_TEXTURE_LEVELS)) return GL_FALSE; /* check first face */ img0 = texObj->Image[0][level]; if (!img0 || img0->Width < 1 || img0->Width != img0->Height) return GL_FALSE; /* check remaining faces vs. first face */ for (face = 1; face < 6; face++) { img = texObj->Image[face][level]; if (!img || img->Width != img0->Width || img->Height != img0->Height || img->TexFormat != img0->TexFormat) return GL_FALSE; } 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 forces the context to re-validate its state. * * \param ctx GL context. * \param texObj texture object. */ void _mesa_dirty_texobj(struct gl_context *ctx, struct gl_texture_object *texObj) { texObj->_BaseComplete = GL_FALSE; texObj->_MipmapComplete = GL_FALSE; ctx->NewState |= _NEW_TEXTURE_OBJECT; } /** * Return pointer to a default/fallback texture of the given type/target. * The texture is an RGBA texture with all texels = (0,0,0,1). * That's the value a GLSL sampler should get when sampling from an * incomplete texture. */ struct gl_texture_object * _mesa_get_fallback_texture(struct gl_context *ctx, gl_texture_index tex) { if (!ctx->Shared->FallbackTex[tex]) { /* create fallback texture now */ const GLsizei width = 1, height = 1; GLsizei depth = 1; GLubyte texel[24]; struct gl_texture_object *texObj; struct gl_texture_image *texImage; mesa_format texFormat; GLuint dims, face, numFaces = 1; GLenum target; 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: dims = 3; target = GL_TEXTURE_2D_ARRAY; break; case TEXTURE_1D_ARRAY_INDEX: dims = 2; target = GL_TEXTURE_1D_ARRAY; break; case TEXTURE_CUBE_INDEX: dims = 2; target = GL_TEXTURE_CUBE_MAP; numFaces = 6; break; case TEXTURE_3D_INDEX: dims = 3; target = GL_TEXTURE_3D; break; case TEXTURE_RECT_INDEX: dims = 2; target = GL_TEXTURE_RECTANGLE; break; case TEXTURE_2D_INDEX: dims = 2; target = GL_TEXTURE_2D; break; case TEXTURE_1D_INDEX: dims = 1; 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; } /* create texture object */ texObj = ctx->Driver.NewTextureObject(ctx, 0, target); if (!texObj) return NULL; assert(texObj->RefCount == 1); texObj->Sampler.MinFilter = GL_NEAREST; texObj->Sampler.MagFilter = GL_NEAREST; texFormat = ctx->Driver.ChooseTextureFormat(ctx, target, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE); /* need a loop here just for cube maps */ for (face = 0; face < numFaces; face++) { const GLenum faceTarget = _mesa_cube_face_target(target, face); /* initialize level[0] texture image */ texImage = _mesa_get_tex_image(ctx, texObj, faceTarget, 0); _mesa_init_teximage_fields(ctx, texImage, width, (dims > 1) ? height : 1, (dims > 2) ? depth : 1, 0, /* border */ GL_RGBA, texFormat); ctx->Driver.TexImage(ctx, dims, texImage, GL_RGBA, GL_UNSIGNED_BYTE, texel, &ctx->DefaultPacking); } _mesa_test_texobj_completeness(ctx, texObj); assert(texObj->_BaseComplete); assert(texObj->_MipmapComplete); ctx->Shared->FallbackTex[tex] = texObj; /* Complete the driver's operation in case another context will also * use the same fallback texture. */ if (ctx->Driver.Finish) ctx->Driver.Finish(ctx); } return ctx->Shared->FallbackTex[tex]; } /** * 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; } } } return size; } /** * Callback called from _mesa_HashWalk() */ static void count_tex_size(GLuint key, void *data, void *userData) { const struct gl_texture_object *texObj = (const struct gl_texture_object *) data; GLuint *total = (GLuint *) userData; (void) key; *total = *total + texture_size(texObj); } /** * 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; _mesa_HashWalk(ctx->Shared->TexObjects, count_tex_size, &total); /* plus, the default texture objects */ for (tgt = 0; tgt < NUM_TEXTURE_TARGETS; tgt++) { total += texture_size(ctx->Shared->DefaultTex[tgt]); } return total; } /** * 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 */ GLenum _mesa_texture_base_format(const struct gl_texture_object *texObj) { const struct gl_texture_image *texImage = _mesa_base_tex_image(texObj); return texImage ? texImage->_BaseFormat : GL_NONE; } 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 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 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 is TEXTURE_RECTANGLE, TEXTURE_BUFFER, * TEXTURE_2D_MULTISAMPLE, or TEXTURE_2D_MULTISAMPLE_ARRAY, and * 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; } /** * 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. * \param target either 0 or a valid / error-checked texture target enum */ static void create_textures(struct gl_context *ctx, GLenum target, GLsizei n, GLuint *textures, const char *caller) { GLuint first; GLint i; if (!textures) return; /* * This must be atomic (generation and allocation of texture IDs) */ _mesa_HashLockMutex(ctx->Shared->TexObjects); 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; texObj = ctx->Driver.NewTextureObject(ctx, name, target); if (!texObj) { _mesa_HashUnlockMutex(ctx->Shared->TexObjects); _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", caller); return; } /* insert into hash table */ _mesa_HashInsertLocked(ctx->Shared->TexObjects, texObj->Name, texObj); textures[i] = name; } _mesa_HashUnlockMutex(ctx->Shared->TexObjects); } static void create_textures_err(struct gl_context *ctx, GLenum target, GLsizei n, GLuint *textures, const char *caller) { if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE)) _mesa_debug(ctx, "%s %d\n", caller, n); if (n < 0) { _mesa_error(ctx, GL_INVALID_VALUE, "%s(n < 0)", caller); return; } create_textures(ctx, target, n, textures, caller); } /*@}*/ /***********************************************************************/ /** \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_no_error(GLsizei n, GLuint *textures) { GET_CURRENT_CONTEXT(ctx); create_textures(ctx, 0, n, textures, "glGenTextures"); } void GLAPIENTRY _mesa_GenTextures(GLsizei n, GLuint *textures) { GET_CURRENT_CONTEXT(ctx); create_textures_err(ctx, 0, n, textures, "glGenTextures"); } /** * 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_no_error(GLenum target, GLsizei n, GLuint *textures) { GET_CURRENT_CONTEXT(ctx); create_textures(ctx, target, n, textures, "glCreateTextures"); } 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_err(ctx, target, n, textures, "glCreateTextures"); } /** * 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) { const gl_texture_index index = texObj->TargetIndex; GLuint u; if (texObj->Target == 0) { /* texture was never bound */ return; } assert(index < NUM_TEXTURE_TARGETS); for (u = 0; u < ctx->Texture.NumCurrentTexUsed; u++) { struct gl_texture_unit *unit = &ctx->Texture.Unit[u]; 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_OBJECT; } } /** * Delete named textures. * * \param n number of textures to be deleted. * \param textures array of texture IDs to be deleted. * * \sa glDeleteTextures(). * * If we're about to delete a texture that's currently bound to any * texture unit, unbind the texture first. Decrement the reference * count on the texture object and delete it if it's zero. * Recall that texture objects can be shared among several rendering * contexts. */ static void delete_textures(struct gl_context *ctx, GLsizei n, const GLuint *textures) { FLUSH_VERTICES(ctx, 0); /* too complex */ if (!textures) return; for (GLsizei i = 0; i < n; i++) { if (textures[i] > 0) { struct gl_texture_object *delObj = _mesa_lookup_texture(ctx, textures[i]); if (delObj) { _mesa_lock_texture(ctx, delObj); /* 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, delObj); /* Check if this texture is currently bound to any texture units. * If so, unbind it. */ 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); /* Make all handles that reference this texture object non-resident * in the current context. */ _mesa_make_texture_handles_non_resident(ctx, delObj); _mesa_unlock_texture(ctx, delObj); ctx->NewState |= _NEW_TEXTURE_OBJECT; /* The texture _name_ is now free for re-use. * Remove it from the hash table now. */ _mesa_HashRemove(ctx->Shared->TexObjects, delObj->Name); /* Unreference the texobj. If refcount hits zero, the texture * will be deleted. */ _mesa_reference_texobj(&delObj, NULL); } } } } /** * 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_OBJECT; /* Unreference the texobj. If refcount hits zero, the texture * will be deleted. */ _mesa_reference_texobj(&texObj, NULL); } void GLAPIENTRY _mesa_DeleteTextures_no_error(GLsizei n, const GLuint *textures) { GET_CURRENT_CONTEXT(ctx); delete_textures(ctx, n, textures); } void GLAPIENTRY _mesa_DeleteTextures(GLsizei n, const GLuint *textures) { GET_CURRENT_CONTEXT(ctx); 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; } delete_textures(ctx, n, textures); } /** * Convert a GL texture target enum such as GL_TEXTURE_2D or GL_TEXTURE_3D * into the corresponding Mesa texture target index. * Note that proxy targets are not valid here. * \return TEXTURE_x_INDEX or -1 if target is invalid */ int _mesa_tex_target_to_index(const struct gl_context *ctx, GLenum target) { switch (target) { case GL_TEXTURE_1D: return _mesa_is_desktop_gl(ctx) ? TEXTURE_1D_INDEX : -1; case GL_TEXTURE_2D: return TEXTURE_2D_INDEX; case GL_TEXTURE_3D: 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 (_mesa_has_ARB_texture_buffer_object(ctx) || _mesa_has_OES_texture_buffer(ctx)) ? TEXTURE_BUFFER_INDEX : -1; case GL_TEXTURE_EXTERNAL_OES: return _mesa_is_gles(ctx) && ctx->Extensions.OES_EGL_image_external ? TEXTURE_EXTERNAL_INDEX : -1; case GL_TEXTURE_CUBE_MAP_ARRAY: return _mesa_has_texture_cube_map_array(ctx) ? 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; } } /** * Do actual texture binding. All error checking should have been done prior * to calling this function. Note that the texture target (1D, 2D, etc) is * always specified by the texObj->TargetIndex. * * \param unit index of texture unit to update * \param texObj the new texture object (cannot be NULL) */ static void bind_texture_object(struct gl_context *ctx, unsigned unit, struct gl_texture_object *texObj) { struct gl_texture_unit *texUnit; int targetIndex; assert(unit < ARRAY_SIZE(ctx->Texture.Unit)); texUnit = &ctx->Texture.Unit[unit]; assert(texObj); assert(valid_texture_object(texObj)); targetIndex = texObj->TargetIndex; assert(targetIndex >= 0); assert(targetIndex < NUM_TEXTURE_TARGETS); /* Check if this texture is only used by this context and is already bound. * If so, just return. For GL_OES_image_external, rebinding the texture * always must invalidate cached resources. */ if (targetIndex != TEXTURE_EXTERNAL_INDEX) { bool early_out; simple_mtx_lock(&ctx->Shared->Mutex); early_out = ((ctx->Shared->RefCount == 1) && (texObj == texUnit->CurrentTex[targetIndex])); simple_mtx_unlock(&ctx->Shared->Mutex); if (early_out) { return; } } /* flush before changing binding */ FLUSH_VERTICES(ctx, _NEW_TEXTURE_OBJECT); /* If the refcount on the previously bound texture is decremented to * zero, it'll be deleted here. */ _mesa_reference_texobj(&texUnit->CurrentTex[targetIndex], texObj); ctx->Texture.NumCurrentTexUsed = MAX2(ctx->Texture.NumCurrentTexUsed, unit + 1); if (texObj->Name != 0) texUnit->_BoundTextures |= (1 << targetIndex); else texUnit->_BoundTextures &= ~(1 << targetIndex); /* Pass BindTexture call to device driver */ if (ctx->Driver.BindTexture) { ctx->Driver.BindTexture(ctx, unit, texObj->Target, texObj); } } /** * Light-weight bind texture for internal users * * This is really just \c finish_texture_init plus \c bind_texture_object. * This is intended to be used by internal Mesa functions that use * \c _mesa_CreateTexture and need to bind textures (e.g., meta). */ void _mesa_bind_texture(struct gl_context *ctx, GLenum target, struct gl_texture_object *tex_obj) { const GLint targetIndex = _mesa_tex_target_to_index(ctx, target); assert(targetIndex >= 0 && targetIndex < NUM_TEXTURE_TARGETS); if (tex_obj->Target == 0) finish_texture_init(ctx, target, tex_obj, targetIndex); assert(tex_obj->Target == target); assert(tex_obj->TargetIndex == targetIndex); bind_texture_object(ctx, ctx->Texture.CurrentUnit, tex_obj); } /** * Implement glBindTexture(). Do error checking, look-up or create a new * texture object, then bind it in the current texture unit. * * \param target texture target. * \param texName texture name. */ static ALWAYS_INLINE void bind_texture(struct gl_context *ctx, GLenum target, GLuint texName, bool no_error) { struct gl_texture_object *newTexObj = NULL; int targetIndex; targetIndex = _mesa_tex_target_to_index(ctx, target); if (!no_error && targetIndex < 0) { _mesa_error(ctx, GL_INVALID_ENUM, "glBindTexture(target = %s)", _mesa_enum_to_string(target)); return; } assert(targetIndex < NUM_TEXTURE_TARGETS); /* * Get pointer to new texture object (newTexObj) */ if (texName == 0) { /* Use a default texture object */ newTexObj = ctx->Shared->DefaultTex[targetIndex]; } else { /* non-default texture object */ newTexObj = _mesa_lookup_texture(ctx, texName); if (newTexObj) { /* error checking */ if (!no_error && newTexObj->Target != 0 && newTexObj->Target != target) { /* The named texture object's target doesn't match the * given target */ _mesa_error( ctx, GL_INVALID_OPERATION, "glBindTexture(target mismatch)" ); return; } if (newTexObj->Target == 0) { finish_texture_init(ctx, target, newTexObj, targetIndex); } } else { if (!no_error && 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) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindTexture"); return; } /* and insert it into hash table */ _mesa_HashInsert(ctx->Shared->TexObjects, texName, newTexObj); } } assert(newTexObj->Target == target); assert(newTexObj->TargetIndex == targetIndex); bind_texture_object(ctx, ctx->Texture.CurrentUnit, newTexObj); } void GLAPIENTRY _mesa_BindTexture_no_error(GLenum target, GLuint texName) { GET_CURRENT_CONTEXT(ctx); bind_texture(ctx, target, texName, true); } void GLAPIENTRY _mesa_BindTexture(GLenum target, GLuint texName) { GET_CURRENT_CONTEXT(ctx); if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE)) _mesa_debug(ctx, "glBindTexture %s %d\n", _mesa_enum_to_string(target), (GLint) texName); bind_texture(ctx, target, texName, false); } /** * OpenGL 4.5 / GL_ARB_direct_state_access glBindTextureUnit(). * * \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. */ static ALWAYS_INLINE void bind_texture_unit(struct gl_context *ctx, GLuint unit, GLuint texture, bool no_error) { struct gl_texture_object *texObj; /* 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); if (!no_error) { /* Error checking */ if (!texObj) { _mesa_error(ctx, GL_INVALID_OPERATION, "glBindTextureUnit(non-gen name)"); return; } if (texObj->Target == 0) { /* Texture object was gen'd but never bound so the target is not set */ _mesa_error(ctx, GL_INVALID_OPERATION, "glBindTextureUnit(target)"); return; } } assert(valid_texture_object(texObj)); bind_texture_object(ctx, unit, texObj); } void GLAPIENTRY _mesa_BindTextureUnit_no_error(GLuint unit, GLuint texture) { GET_CURRENT_CONTEXT(ctx); bind_texture_unit(ctx, unit, texture, true); } void GLAPIENTRY _mesa_BindTextureUnit(GLuint unit, GLuint texture) { GET_CURRENT_CONTEXT(ctx); if (unit >= _mesa_max_tex_unit(ctx)) { _mesa_error(ctx, GL_INVALID_VALUE, "glBindTextureUnit(unit=%u)", unit); return; } if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE)) _mesa_debug(ctx, "glBindTextureUnit %s %d\n", _mesa_enum_to_string(GL_TEXTURE0+unit), (GLint) texture); bind_texture_unit(ctx, unit, texture, false); } /** * OpenGL 4.4 / GL_ARB_multi_bind glBindTextures(). */ static ALWAYS_INLINE void bind_textures(struct gl_context *ctx, GLuint first, GLsizei count, const GLuint *textures, bool no_error) { GLsizei i; 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 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_HashLockMutex(ctx->Shared->TexObjects); 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) { bind_texture_object(ctx, first + i, texObj); } else if (!no_error) { /* The ARB_multi_bind spec says: * * "An INVALID_OPERATION error is generated if any value * in 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_HashUnlockMutex(ctx->Shared->TexObjects); } else { /* Unbind all textures in the range through +-1 */ for (i = 0; i < count; i++) unbind_textures_from_unit(ctx, first + i); } } void GLAPIENTRY _mesa_BindTextures_no_error(GLuint first, GLsizei count, const GLuint *textures) { GET_CURRENT_CONTEXT(ctx); bind_textures(ctx, first, count, textures, true); } void GLAPIENTRY _mesa_BindTextures(GLuint first, GLsizei count, const GLuint *textures) { GET_CURRENT_CONTEXT(ctx); /* The ARB_multi_bind spec says: * * "An INVALID_OPERATION error is generated if + * 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; } bind_textures(ctx, first, count, textures, false); } /** * 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. */ void GLAPIENTRY _mesa_PrioritizeTextures( GLsizei n, const GLuint *texName, const GLclampf *priorities ) { GET_CURRENT_CONTEXT(ctx); GLint i; 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" ); return; } if (!priorities) return; for (i = 0; i < n; i++) { if (texName[i] > 0) { struct gl_texture_object *t = _mesa_lookup_texture(ctx, texName[i]); if (t) { t->Priority = CLAMP( priorities[i], 0.0F, 1.0F ); } } } ctx->NewState |= _NEW_TEXTURE_OBJECT; } /** * 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 * residences is left unchanged, * * Note: we assume all textures are always resident */ GLboolean GLAPIENTRY _mesa_AreTexturesResident(GLsizei n, const GLuint *texName, GLboolean *residences) { GET_CURRENT_CONTEXT(ctx); GLboolean allResident = GL_TRUE; 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; } if (!texName || !residences) return GL_FALSE; /* We only do error checking on the texture names */ for (i = 0; i < n; i++) { struct gl_texture_object *t; if (texName[i] == 0) { _mesa_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident"); return GL_FALSE; } t = _mesa_lookup_texture(ctx, texName[i]); if (!t) { _mesa_error(ctx, GL_INVALID_VALUE, "glAreTexturesResident"); return GL_FALSE; } } return allResident; } /** * See if a name corresponds to a texture. * * \param texture texture name. * * \return GL_TRUE if texture name corresponds to a texture, or GL_FALSE * otherwise. * * \sa glIsTexture(). * * Calls _mesa_HashLookup(). */ GLboolean GLAPIENTRY _mesa_IsTexture( GLuint texture ) { struct gl_texture_object *t; 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; t = _mesa_lookup_texture(ctx, texture); /* IsTexture is true only after object has been bound once. */ return t && t->Target; } /** * Simplest implementation of texture locking: grab the shared tex * mutex. Examine the shared context state timestamp and if there has * been a change, set the appropriate bits in ctx->NewState. * * This is used to deal with synchronizing things when a texture object * is used/modified by different contexts (or threads) which are sharing * the texture. * * See also _mesa_lock/unlock_texture() in teximage.h */ void _mesa_lock_context_textures( struct gl_context *ctx ) { mtx_lock(&ctx->Shared->TexMutex); if (ctx->Shared->TextureStateStamp != ctx->TextureStateTimestamp) { ctx->NewState |= _NEW_TEXTURE_OBJECT; ctx->TextureStateTimestamp = ctx->Shared->TextureStateStamp; } } void _mesa_unlock_context_textures( struct gl_context *ctx ) { assert(ctx->Shared->TextureStateStamp == ctx->TextureStateTimestamp); mtx_unlock(&ctx->Shared->TexMutex); } void GLAPIENTRY _mesa_InvalidateTexSubImage_no_error(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth) { /* no-op */ } 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 - and + where * is the size of the dimension of the texture image, and 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 equal to zero and * 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_no_error(GLuint texture, GLint level) { /* no-op */ } 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; } /*@}*/ ef='#n1953'>1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199
/*
 * Copyright (C) 2010  Brian Paul   All Rights Reserved.
 * Copyright (C) 2010  Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * 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
 * 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.
 *
 * Author: Kristian Høgsberg <krh@bitplanet.net>
 */

#include "glheader.h"
#include "context.h"
#include "blend.h"
#include "debug_output.h"
#include "enable.h"
#include "enums.h"
#include "errors.h"
#include "extensions.h"
#include "get.h"
#include "macros.h"
#include "mtypes.h"
#include "state.h"
#include "texcompress.h"
#include "texstate.h"
#include "framebuffer.h"
#include "samplerobj.h"
#include "stencil.h"
#include "version.h"

/* This is a table driven implemetation of the glGet*v() functions.
 * The basic idea is that most getters just look up an int somewhere
 * in struct gl_context and then convert it to a bool or float according to
 * which of glGetIntegerv() glGetBooleanv() etc is being called.
 * Instead of generating code to do this, we can just record the enum
 * value and the offset into struct gl_context in an array of structs.  Then
 * in glGet*(), we lookup the struct for the enum in question, and use
 * the offset to get the int we need.
 *
 * Sometimes we need to look up a float, a boolean, a bit in a
 * bitfield, a matrix or other types instead, so we need to track the
 * type of the value in struct gl_context.  And sometimes the value isn't in
 * struct gl_context but in the drawbuffer, the array object, current texture
 * unit, or maybe it's a computed value.  So we need to also track
 * where or how to find the value.  Finally, we sometimes need to
 * check that one of a number of extensions are enabled, the GL
 * version or flush or call _mesa_update_state().  This is done by
 * attaching optional extra information to the value description
 * struct, it's sort of like an array of opcodes that describe extra
 * checks or actions.
 *
 * Putting all this together we end up with struct value_desc below,
 * and with a couple of macros to help, the table of struct value_desc
 * is about as concise as the specification in the old python script.
 */

#define FLOAT_TO_BOOLEAN(X)   ( (X) ? GL_TRUE : GL_FALSE )
#define FLOAT_TO_FIXED(F)     ( ((F) * 65536.0f > INT_MAX) ? INT_MAX : \
                                ((F) * 65536.0f < INT_MIN) ? INT_MIN : \
                                (GLint) ((F) * 65536.0f) )

#define INT_TO_BOOLEAN(I)     ( (I) ? GL_TRUE : GL_FALSE )
#define INT_TO_FIXED(I)       ( ((I) > SHRT_MAX) ? INT_MAX : \
                                ((I) < SHRT_MIN) ? INT_MIN : \
                                (GLint) ((I) * 65536) )

#define INT64_TO_BOOLEAN(I)   ( (I) ? GL_TRUE : GL_FALSE )
#define INT64_TO_INT(I)       ( (GLint)((I > INT_MAX) ? INT_MAX : ((I < INT_MIN) ? INT_MIN : (I))) )

#define BOOLEAN_TO_INT(B)     ( (GLint) (B) )
#define BOOLEAN_TO_INT64(B)   ( (GLint64) (B) )
#define BOOLEAN_TO_FLOAT(B)   ( (B) ? 1.0F : 0.0F )
#define BOOLEAN_TO_FIXED(B)   ( (GLint) ((B) ? 1 : 0) << 16 )

#define ENUM_TO_INT64(E)      ( (GLint64) (E) )
#define ENUM_TO_FIXED(E)      (E)

enum value_type {
   TYPE_INVALID,
   TYPE_INT,
   TYPE_INT_2,
   TYPE_INT_3,
   TYPE_INT_4,
   TYPE_INT_N,
   TYPE_UINT,
   TYPE_UINT_2,
   TYPE_UINT_3,
   TYPE_UINT_4,
   TYPE_INT64,
   TYPE_ENUM16,
   TYPE_ENUM,
   TYPE_ENUM_2,
   TYPE_BOOLEAN,
   TYPE_UBYTE,
   TYPE_SHORT,
   TYPE_BIT_0,
   TYPE_BIT_1,
   TYPE_BIT_2,
   TYPE_BIT_3,
   TYPE_BIT_4,
   TYPE_BIT_5,
   TYPE_BIT_6,
   TYPE_BIT_7,
   TYPE_FLOAT,
   TYPE_FLOAT_2,
   TYPE_FLOAT_3,
   TYPE_FLOAT_4,
   TYPE_FLOAT_8,
   TYPE_FLOATN,
   TYPE_FLOATN_2,
   TYPE_FLOATN_3,
   TYPE_FLOATN_4,
   TYPE_DOUBLEN,
   TYPE_DOUBLEN_2,
   TYPE_MATRIX,
   TYPE_MATRIX_T,
   TYPE_CONST
};

enum value_location {
   LOC_BUFFER,
   LOC_CONTEXT,
   LOC_ARRAY,
   LOC_TEXUNIT,
   LOC_CUSTOM
};

enum value_extra {
   EXTRA_END = 0x8000,
   EXTRA_VERSION_30,
   EXTRA_VERSION_31,
   EXTRA_VERSION_32,
   EXTRA_VERSION_40,
   EXTRA_VERSION_43,
   EXTRA_API_GL,
   EXTRA_API_GL_CORE,
   EXTRA_API_ES2,
   EXTRA_API_ES3,
   EXTRA_API_ES31,
   EXTRA_API_ES32,
   EXTRA_NEW_BUFFERS,
   EXTRA_NEW_FRAG_CLAMP,
   EXTRA_VALID_DRAW_BUFFER,
   EXTRA_VALID_TEXTURE_UNIT,
   EXTRA_VALID_CLIP_DISTANCE,
   EXTRA_FLUSH_CURRENT,
   EXTRA_GLSL_130,
   EXTRA_EXT_UBO_GS,
   EXTRA_EXT_ATOMICS_GS,
   EXTRA_EXT_SHADER_IMAGE_GS,
   EXTRA_EXT_ATOMICS_TESS,
   EXTRA_EXT_SHADER_IMAGE_TESS,
   EXTRA_EXT_SSBO_GS,
   EXTRA_EXT_FB_NO_ATTACH_GS,
   EXTRA_EXT_ES_GS,
   EXTRA_EXT_PROVOKING_VERTEX_32,
};

#define NO_EXTRA NULL
#define NO_OFFSET 0

struct value_desc {
   GLenum pname;
   GLubyte location;  /**< enum value_location */
   GLubyte type;      /**< enum value_type */
   int offset;
   const int *extra;
};

union value {
   GLfloat value_float;
   GLfloat value_float_4[4];
   GLdouble value_double_2[2];
   GLmatrix *value_matrix;
   GLint value_int;
   GLint value_int_4[4];
   GLint64 value_int64;
   GLenum value_enum;
   GLubyte value_ubyte;
   GLshort value_short;
   GLuint value_uint;

   /* Sigh, see GL_COMPRESSED_TEXTURE_FORMATS_ARB handling */
   struct {
      GLint n, ints[100];
   } value_int_n;
   GLboolean value_bool;
};

#define BUFFER_FIELD(field, type) \
   LOC_BUFFER, type, offsetof(struct gl_framebuffer, field)
#define CONTEXT_FIELD(field, type) \
   LOC_CONTEXT, type, offsetof(struct gl_context, field)
#define ARRAY_FIELD(field, type) \
   LOC_ARRAY, type, offsetof(struct gl_vertex_array_object, field)
#undef CONST /* already defined through windows.h */
#define CONST(value) \
   LOC_CONTEXT, TYPE_CONST, value

#define BUFFER_INT(field) BUFFER_FIELD(field, TYPE_INT)
#define BUFFER_ENUM(field) BUFFER_FIELD(field, TYPE_ENUM)
#define BUFFER_ENUM16(field) BUFFER_FIELD(field, TYPE_ENUM16)
#define BUFFER_BOOL(field) BUFFER_FIELD(field, TYPE_BOOLEAN)

#define CONTEXT_INT(field) CONTEXT_FIELD(field, TYPE_INT)
#define CONTEXT_INT2(field) CONTEXT_FIELD(field, TYPE_INT_2)
#define CONTEXT_INT64(field) CONTEXT_FIELD(field, TYPE_INT64)
#define CONTEXT_UINT(field) CONTEXT_FIELD(field, TYPE_UINT)
#define CONTEXT_ENUM16(field) CONTEXT_FIELD(field, TYPE_ENUM16)
#define CONTEXT_ENUM(field) CONTEXT_FIELD(field, TYPE_ENUM)
#define CONTEXT_ENUM2(field) CONTEXT_FIELD(field, TYPE_ENUM_2)
#define CONTEXT_BOOL(field) CONTEXT_FIELD(field, TYPE_BOOLEAN)
#define CONTEXT_BIT0(field) CONTEXT_FIELD(field, TYPE_BIT_0)
#define CONTEXT_BIT1(field) CONTEXT_FIELD(field, TYPE_BIT_1)
#define CONTEXT_BIT2(field) CONTEXT_FIELD(field, TYPE_BIT_2)
#define CONTEXT_BIT3(field) CONTEXT_FIELD(field, TYPE_BIT_3)
#define CONTEXT_BIT4(field) CONTEXT_FIELD(field, TYPE_BIT_4)
#define CONTEXT_BIT5(field) CONTEXT_FIELD(field, TYPE_BIT_5)
#define CONTEXT_BIT6(field) CONTEXT_FIELD(field, TYPE_BIT_6)
#define CONTEXT_BIT7(field) CONTEXT_FIELD(field, TYPE_BIT_7)
#define CONTEXT_FLOAT(field) CONTEXT_FIELD(field, TYPE_FLOAT)
#define CONTEXT_FLOAT2(field) CONTEXT_FIELD(field, TYPE_FLOAT_2)
#define CONTEXT_FLOAT3(field) CONTEXT_FIELD(field, TYPE_FLOAT_3)
#define CONTEXT_FLOAT4(field) CONTEXT_FIELD(field, TYPE_FLOAT_4)
#define CONTEXT_FLOAT8(field) CONTEXT_FIELD(field, TYPE_FLOAT_8)
#define CONTEXT_MATRIX(field) CONTEXT_FIELD(field, TYPE_MATRIX)
#define CONTEXT_MATRIX_T(field) CONTEXT_FIELD(field, TYPE_MATRIX_T)

/* Vertex array fields */
#define ARRAY_INT(field) ARRAY_FIELD(field, TYPE_INT)
#define ARRAY_ENUM(field) ARRAY_FIELD(field, TYPE_ENUM)
#define ARRAY_ENUM16(field) ARRAY_FIELD(field, TYPE_ENUM16)
#define ARRAY_BOOL(field) ARRAY_FIELD(field, TYPE_BOOLEAN)
#define ARRAY_UBYTE(field) ARRAY_FIELD(field, TYPE_UBYTE)
#define ARRAY_SHORT(field) ARRAY_FIELD(field, TYPE_SHORT)

#define EXT(f)					\
   offsetof(struct gl_extensions, f)

#define EXTRA_EXT(e)				\
   static const int extra_##e[] = {		\
      EXT(e), EXTRA_END				\
   }

#define EXTRA_EXT2(e1, e2)			\
   static const int extra_##e1##_##e2[] = {	\
      EXT(e1), EXT(e2), EXTRA_END		\
   }

/* The 'extra' mechanism is a way to specify extra checks (such as
 * extensions or specific gl versions) or actions (flush current, new
 * buffers) that we need to do before looking up an enum.  We need to
 * declare them all up front so we can refer to them in the value_desc
 * structs below.
 *
 * Each EXTRA_ will be executed.  For EXTRA_* enums of extensions and API
 * versions, listing multiple ones in an array means an error will be thrown
 * only if none of them are available.  If you need to check for "AND"
 * behavior, you would need to make a custom EXTRA_ enum.
 */

static const int extra_new_buffers[] = {
   EXTRA_NEW_BUFFERS,
   EXTRA_END
};

static const int extra_new_frag_clamp[] = {
   EXTRA_NEW_FRAG_CLAMP,
   EXTRA_END
};

static const int extra_valid_draw_buffer[] = {
   EXTRA_VALID_DRAW_BUFFER,
   EXTRA_END
};

static const int extra_valid_texture_unit[] = {
   EXTRA_VALID_TEXTURE_UNIT,
   EXTRA_END
};

static const int extra_valid_clip_distance[] = {
   EXTRA_VALID_CLIP_DISTANCE,
   EXTRA_END
};

static const int extra_flush_current_valid_texture_unit[] = {
   EXTRA_FLUSH_CURRENT,
   EXTRA_VALID_TEXTURE_UNIT,
   EXTRA_END
};

static const int extra_flush_current[] = {
   EXTRA_FLUSH_CURRENT,
   EXTRA_END
};

static const int extra_EXT_texture_integer_and_new_buffers[] = {
   EXT(EXT_texture_integer),
   EXTRA_NEW_BUFFERS,
   EXTRA_END
};

static const int extra_GLSL_130_es3[] = {
   EXTRA_GLSL_130,
   EXTRA_API_ES3,
   EXTRA_END
};

static const int extra_texture_buffer_object[] = {
   EXT(ARB_texture_buffer_object),
   EXTRA_END
};

static const int extra_ARB_transform_feedback2_api_es3[] = {
   EXT(ARB_transform_feedback2),
   EXTRA_API_ES3,
   EXTRA_END
};

static const int extra_ARB_uniform_buffer_object_and_geometry_shader[] = {
   EXTRA_EXT_UBO_GS,
   EXTRA_END
};

static const int extra_ARB_ES2_compatibility_api_es2[] = {
   EXT(ARB_ES2_compatibility),
   EXTRA_API_ES2,
   EXTRA_END
};

static const int extra_ARB_ES3_compatibility_api_es3[] = {
   EXT(ARB_ES3_compatibility),
   EXTRA_API_ES3,
   EXTRA_END
};

static const int extra_EXT_framebuffer_sRGB_and_new_buffers[] = {
   EXT(EXT_framebuffer_sRGB),
   EXTRA_NEW_BUFFERS,
   EXTRA_END
};

static const int extra_EXT_packed_float[] = {
   EXT(EXT_packed_float),
   EXTRA_NEW_BUFFERS,
   EXTRA_END
};

static const int extra_EXT_texture_array_es3[] = {
   EXT(EXT_texture_array),
   EXTRA_API_ES3,
   EXTRA_END
};

static const int extra_ARB_shader_atomic_counters_and_geometry_shader[] = {
   EXTRA_EXT_ATOMICS_GS,
   EXTRA_END
};

static const int extra_ARB_shader_image_load_store_and_geometry_shader[] = {
   EXTRA_EXT_SHADER_IMAGE_GS,
   EXTRA_END
};

static const int extra_ARB_shader_atomic_counters_and_tessellation[] = {
   EXTRA_EXT_ATOMICS_TESS,
   EXTRA_END
};

static const int extra_ARB_shader_image_load_store_and_tessellation[] = {
   EXTRA_EXT_SHADER_IMAGE_TESS,
   EXTRA_END
};

/* HACK: remove when ARB_compute_shader is actually supported */
static const int extra_ARB_compute_shader_es31[] = {
   EXT(ARB_compute_shader),
   EXTRA_API_ES31,
   EXTRA_END
};

static const int extra_ARB_shader_storage_buffer_object_es31[] = {
   EXT(ARB_shader_storage_buffer_object),
   EXTRA_API_ES31,
   EXTRA_END
};

static const int extra_ARB_shader_storage_buffer_object_and_geometry_shader[] = {
   EXTRA_EXT_SSBO_GS,
   EXTRA_END
};

static const int extra_ARB_shader_image_load_store_shader_storage_buffer_object_es31[] = {
   EXT(ARB_shader_image_load_store),
   EXT(ARB_shader_storage_buffer_object),
   EXTRA_API_ES31,
   EXTRA_END
};

static const int extra_ARB_framebuffer_no_attachments_and_geometry_shader[] = {
   EXTRA_EXT_FB_NO_ATTACH_GS,
   EXTRA_END
};

static const int extra_ARB_viewport_array_or_oes_geometry_shader[] = {
   EXT(ARB_viewport_array),
   EXTRA_EXT_ES_GS,
   EXTRA_END
};

static const int extra_ARB_viewport_array_or_oes_viewport_array[] = {
   EXT(ARB_viewport_array),
   EXT(OES_viewport_array),
   EXTRA_END
};

static const int extra_ARB_gpu_shader5_or_oes_geometry_shader[] = {
   EXT(ARB_gpu_shader5),
   EXTRA_EXT_ES_GS,
   EXTRA_END
};

static const int extra_ARB_gpu_shader5_or_OES_sample_variables[] = {
   EXT(ARB_gpu_shader5),
   EXT(OES_sample_variables),
   EXTRA_END
};

static const int extra_ES32[] = {
   EXT(ARB_ES3_2_compatibility),
   EXTRA_API_ES32,
   EXTRA_END
};

static const int extra_KHR_robustness_or_GL[] = {
   EXT(KHR_robustness),
   EXTRA_API_GL,
   EXTRA_API_GL_CORE,
   EXTRA_END
};

static const int extra_INTEL_conservative_rasterization[] = {
   EXT(INTEL_conservative_rasterization),
   EXTRA_END
};

EXTRA_EXT(ARB_texture_cube_map);
EXTRA_EXT(EXT_texture_array);
EXTRA_EXT(NV_fog_distance);
EXTRA_EXT(EXT_texture_filter_anisotropic);
EXTRA_EXT(NV_point_sprite);
EXTRA_EXT(NV_texture_rectangle);
EXTRA_EXT(EXT_stencil_two_side);
EXTRA_EXT(EXT_depth_bounds_test);
EXTRA_EXT(ARB_depth_clamp);
EXTRA_EXT(AMD_depth_clamp_separate);
EXTRA_EXT(ATI_fragment_shader);
EXTRA_EXT(EXT_provoking_vertex);
EXTRA_EXT(ARB_fragment_shader);
EXTRA_EXT(ARB_fragment_program);
EXTRA_EXT2(ARB_framebuffer_object, EXT_framebuffer_multisample);
EXTRA_EXT(ARB_seamless_cube_map);
EXTRA_EXT(ARB_sync);
EXTRA_EXT(ARB_vertex_shader);
EXTRA_EXT(EXT_transform_feedback);
EXTRA_EXT(ARB_transform_feedback3);
EXTRA_EXT(EXT_pixel_buffer_object);
EXTRA_EXT(ARB_vertex_program);
EXTRA_EXT2(NV_point_sprite, ARB_point_sprite);
EXTRA_EXT2(ARB_vertex_program, ARB_fragment_program);
EXTRA_EXT(ARB_color_buffer_float);
EXTRA_EXT(EXT_framebuffer_sRGB);
EXTRA_EXT(OES_EGL_image_external);
EXTRA_EXT(ARB_blend_func_extended);
EXTRA_EXT(ARB_uniform_buffer_object);
EXTRA_EXT(ARB_timer_query);
EXTRA_EXT2(ARB_texture_cube_map_array, OES_texture_cube_map_array);
EXTRA_EXT(ARB_texture_buffer_range);
EXTRA_EXT(ARB_texture_multisample);
EXTRA_EXT(ARB_texture_gather);
EXTRA_EXT(ARB_shader_atomic_counters);
EXTRA_EXT(ARB_draw_indirect);
EXTRA_EXT(ARB_shader_image_load_store);
EXTRA_EXT(ARB_query_buffer_object);
EXTRA_EXT2(ARB_transform_feedback3, ARB_gpu_shader5);
EXTRA_EXT(INTEL_performance_query);
EXTRA_EXT(ARB_explicit_uniform_location);
EXTRA_EXT(ARB_clip_control);
EXTRA_EXT(ARB_polygon_offset_clamp);
EXTRA_EXT(ARB_framebuffer_no_attachments);
EXTRA_EXT(ARB_tessellation_shader);
EXTRA_EXT(ARB_shader_storage_buffer_object);
EXTRA_EXT(ARB_indirect_parameters);
EXTRA_EXT(ATI_meminfo);
EXTRA_EXT(NVX_gpu_memory_info);
EXTRA_EXT(ARB_cull_distance);
EXTRA_EXT(EXT_window_rectangles);
EXTRA_EXT(KHR_blend_equation_advanced_coherent);
EXTRA_EXT(OES_primitive_bounding_box);
EXTRA_EXT(ARB_compute_variable_group_size);
EXTRA_EXT(KHR_robustness);
EXTRA_EXT(ARB_sparse_buffer);
EXTRA_EXT(NV_conservative_raster);
EXTRA_EXT(NV_conservative_raster_dilate);
EXTRA_EXT(NV_conservative_raster_pre_snap_triangles);
EXTRA_EXT(ARB_sample_locations);
EXTRA_EXT(AMD_framebuffer_multisample_advanced);

static const int
extra_ARB_color_buffer_float_or_glcore[] = {
   EXT(ARB_color_buffer_float),
   EXTRA_API_GL_CORE,
   EXTRA_END
};

static const int
extra_NV_primitive_restart[] = {
   EXT(NV_primitive_restart),
   EXTRA_END
};

static const int extra_version_30[] = { EXTRA_VERSION_30, EXTRA_END };
static const int extra_version_31[] = { EXTRA_VERSION_31, EXTRA_END };
static const int extra_version_32[] = { EXTRA_VERSION_32, EXTRA_END };
static const int extra_version_43[] = { EXTRA_VERSION_43, EXTRA_END };

static const int extra_gl30_es3[] = {
    EXTRA_VERSION_30,
    EXTRA_API_ES3,
    EXTRA_END,
};

static const int extra_gl32_es3[] = {
    EXTRA_VERSION_32,
    EXTRA_API_ES3,
    EXTRA_END,
};

static const int extra_version_32_OES_geometry_shader[] = {
    EXTRA_VERSION_32,
    EXTRA_EXT_ES_GS,
    EXTRA_END
};

static const int extra_gl40_ARB_sample_shading[] = {
   EXTRA_VERSION_40,
   EXT(ARB_sample_shading),
   EXTRA_END
};

static const int
extra_ARB_vertex_program_api_es2[] = {
   EXT(ARB_vertex_program),
   EXTRA_API_ES2,
   EXTRA_END
};

/* The ReadBuffer get token is valid under either full GL or under
 * GLES2 if the NV_read_buffer extension is available. */
static const int
extra_NV_read_buffer_api_gl[] = {
   EXTRA_API_ES2,
   EXTRA_API_GL,
   EXTRA_END
};

static const int extra_core_ARB_color_buffer_float_and_new_buffers[] = {
   EXTRA_API_GL_CORE,
   EXT(ARB_color_buffer_float),
   EXTRA_NEW_BUFFERS,
   EXTRA_END
};

static const int extra_EXT_shader_framebuffer_fetch[] = {
   EXTRA_API_ES2,
   EXTRA_API_ES3,
   EXT(EXT_shader_framebuffer_fetch),
   EXTRA_END
};

static const int extra_EXT_provoking_vertex_32[] = {
   EXTRA_EXT_PROVOKING_VERTEX_32,
   EXTRA_END
};

static const int extra_EXT_disjoint_timer_query[] = {
   EXTRA_API_ES2,
   EXTRA_API_ES3,
   EXT(EXT_disjoint_timer_query),
   EXTRA_END
};


/* This is the big table describing all the enums we accept in
 * glGet*v().  The table is partitioned into six parts: enums
 * understood by all GL APIs (OpenGL, GLES and GLES2), enums shared
 * between OpenGL and GLES, enums exclusive to GLES, etc for the
 * remaining combinations. To look up the enums valid in a given API
 * we will use a hash table specific to that API. These tables are in
 * turn generated at build time and included through get_hash.h.
 */

#include "get_hash.h"

/* All we need now is a way to look up the value struct from the enum.
 * The code generated by gcc for the old generated big switch
 * statement is a big, balanced, open coded if/else tree, essentially
 * an unrolled binary search.  It would be natural to sort the new
 * enum table and use bsearch(), but we will use a read-only hash
 * table instead.  bsearch() has a nice guaranteed worst case
 * performance, but we're also guaranteed to hit that worst case
 * (log2(n) iterations) for about half the enums.  Instead, using an
 * open addressing hash table, we can find the enum on the first try
 * for 80% of the enums, 1 collision for 10% and never more than 5
 * collisions for any enum (typical numbers).  And the code is very
 * simple, even though it feels a little magic. */

/**
 * Handle irregular enums
 *
 * Some values don't conform to the "well-known type at context
 * pointer + offset" pattern, so we have this function to catch all
 * the corner cases.  Typically, it's a computed value or a one-off
 * pointer to a custom struct or something.
 *
 * In this case we can't return a pointer to the value, so we'll have
 * to use the temporary variable 'v' declared back in the calling
 * glGet*v() function to store the result.
 *
 * \param ctx the current context
 * \param d the struct value_desc that describes the enum
 * \param v pointer to the tmp declared in the calling glGet*v() function
 */
static void
find_custom_value(struct gl_context *ctx, const struct value_desc *d, union value *v)
{
   struct gl_buffer_object **buffer_obj;
   struct gl_array_attributes *array;
   GLuint unit, *p;

   switch (d->pname) {
   case GL_MAJOR_VERSION:
      v->value_int = ctx->Version / 10;
      break;
   case GL_MINOR_VERSION:
      v->value_int = ctx->Version % 10;
      break;

   case GL_TEXTURE_1D:
   case GL_TEXTURE_2D:
   case GL_TEXTURE_3D:
   case GL_TEXTURE_CUBE_MAP:
   case GL_TEXTURE_RECTANGLE_NV:
   case GL_TEXTURE_EXTERNAL_OES:
      v->value_bool = _mesa_IsEnabled(d->pname);
      break;

   case GL_LINE_STIPPLE_PATTERN:
      /* This is the only GLushort, special case it here by promoting
       * to an int rather than introducing a new type. */
      v->value_int = ctx->Line.StipplePattern;
      break;

   case GL_CURRENT_RASTER_TEXTURE_COORDS:
      unit = ctx->Texture.CurrentUnit;
      v->value_float_4[0] = ctx->Current.RasterTexCoords[unit][0];
      v->value_float_4[1] = ctx->Current.RasterTexCoords[unit][1];
      v->value_float_4[2] = ctx->Current.RasterTexCoords[unit][2];
      v->value_float_4[3] = ctx->Current.RasterTexCoords[unit][3];
      break;

   case GL_CURRENT_TEXTURE_COORDS:
      unit = ctx->Texture.CurrentUnit;
      v->value_float_4[0] = ctx->Current.Attrib[VERT_ATTRIB_TEX0 + unit][0];
      v->value_float_4[1] = ctx->Current.Attrib[VERT_ATTRIB_TEX0 + unit][1];
      v->value_float_4[2] = ctx->Current.Attrib[VERT_ATTRIB_TEX0 + unit][2];
      v->value_float_4[3] = ctx->Current.Attrib[VERT_ATTRIB_TEX0 + unit][3];
      break;

   case GL_COLOR_WRITEMASK:
      v->value_int_4[0] = GET_COLORMASK_BIT(ctx->Color.ColorMask, 0, 0);
      v->value_int_4[1] = GET_COLORMASK_BIT(ctx->Color.ColorMask, 0, 1);
      v->value_int_4[2] = GET_COLORMASK_BIT(ctx->Color.ColorMask, 0, 2);
      v->value_int_4[3] = GET_COLORMASK_BIT(ctx->Color.ColorMask, 0, 3);
      break;

   case GL_DEPTH_CLAMP:
      v->value_bool = ctx->Transform.DepthClampNear || ctx->Transform.DepthClampFar;
      break;

   case GL_EDGE_FLAG:
      v->value_bool = ctx->Current.Attrib[VERT_ATTRIB_EDGEFLAG][0] == 1.0F;
      break;

   case GL_READ_BUFFER:
      v->value_enum = ctx->ReadBuffer->ColorReadBuffer;
      break;

   case GL_MAP2_GRID_DOMAIN:
      v->value_float_4[0] = ctx->Eval.MapGrid2u1;
      v->value_float_4[1] = ctx->Eval.MapGrid2u2;
      v->value_float_4[2] = ctx->Eval.MapGrid2v1;
      v->value_float_4[3] = ctx->Eval.MapGrid2v2;
      break;

   case GL_TEXTURE_STACK_DEPTH:
      unit = ctx->Texture.CurrentUnit;
      v->value_int = ctx->TextureMatrixStack[unit].Depth + 1;
      break;
   case GL_TEXTURE_MATRIX:
      unit = ctx->Texture.CurrentUnit;
      v->value_matrix = ctx->TextureMatrixStack[unit].Top;
      break;

   case GL_VERTEX_ARRAY:
      v->value_bool = !!(ctx->Array.VAO->Enabled & VERT_BIT_POS);
      break;
   case GL_NORMAL_ARRAY:
      v->value_bool = !!(ctx->Array.VAO->Enabled & VERT_BIT_NORMAL);
      break;
   case GL_COLOR_ARRAY:
      v->value_bool = !!(ctx->Array.VAO->Enabled & VERT_BIT_COLOR0);
      break;
   case GL_TEXTURE_COORD_ARRAY:
      v->value_bool = !!(ctx->Array.VAO->Enabled & VERT_BIT_TEX(ctx->Array.ActiveTexture));
      break;
   case GL_INDEX_ARRAY:
      v->value_bool = !!(ctx->Array.VAO->Enabled & VERT_BIT_COLOR_INDEX);
      break;
   case GL_EDGE_FLAG_ARRAY:
      v->value_bool = !!(ctx->Array.VAO->Enabled & VERT_BIT_EDGEFLAG);
      break;
   case GL_SECONDARY_COLOR_ARRAY:
      v->value_bool = !!(ctx->Array.VAO->Enabled & VERT_BIT_COLOR1);
      break;
   case GL_FOG_COORDINATE_ARRAY:
      v->value_bool = !!(ctx->Array.VAO->Enabled & VERT_BIT_FOG);
      break;
   case GL_POINT_SIZE_ARRAY_OES:
      v->value_bool = !!(ctx->Array.VAO->Enabled & VERT_BIT_POINT_SIZE);
      break;

   case GL_TEXTURE_COORD_ARRAY_TYPE:
   case GL_TEXTURE_COORD_ARRAY_STRIDE:
      array = &ctx->Array.VAO->VertexAttrib[VERT_ATTRIB_TEX(ctx->Array.ActiveTexture)];
      v->value_int = *(GLuint *) ((char *) array + d->offset);
      break;

   case GL_TEXTURE_COORD_ARRAY_SIZE:
      array = &ctx->Array.VAO->VertexAttrib[VERT_ATTRIB_TEX(ctx->Array.ActiveTexture)];
      v->value_int = array->Format.Size;
      break;

   case GL_VERTEX_ARRAY_SIZE:
      array = &ctx->Array.VAO->VertexAttrib[VERT_ATTRIB_POS];
      v->value_int = array->Format.Size;
      break;

   case GL_ACTIVE_TEXTURE_ARB:
      v->value_int = GL_TEXTURE0_ARB + ctx->Texture.CurrentUnit;
      break;
   case GL_CLIENT_ACTIVE_TEXTURE_ARB:
      v->value_int = GL_TEXTURE0_ARB + ctx->Array.ActiveTexture;
      break;

   case GL_MODELVIEW_STACK_DEPTH:
   case GL_PROJECTION_STACK_DEPTH:
      v->value_int = *(GLint *) ((char *) ctx + d->offset) + 1;
      break;

   case GL_MAX_TEXTURE_SIZE:
   case GL_MAX_3D_TEXTURE_SIZE:
   case GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB:
      p = (GLuint *) ((char *) ctx + d->offset);
      v->value_int = 1 << (*p - 1);
      break;

   case GL_SCISSOR_BOX:
      v->value_int_4[0] = ctx->Scissor.ScissorArray[0].X;
      v->value_int_4[1] = ctx->Scissor.ScissorArray[0].Y;
      v->value_int_4[2] = ctx->Scissor.ScissorArray[0].Width;
      v->value_int_4[3] = ctx->Scissor.ScissorArray[0].Height;
      break;

   case GL_SCISSOR_TEST:
      v->value_bool = ctx->Scissor.EnableFlags & 1;
      break;

   case GL_LIST_INDEX:
      v->value_int =
         ctx->ListState.CurrentList ? ctx->ListState.CurrentList->Name : 0;
      break;
   case GL_LIST_MODE:
      if (!ctx->CompileFlag)
         v->value_enum = 0;
      else if (ctx->ExecuteFlag)
         v->value_enum = GL_COMPILE_AND_EXECUTE;
      else
         v->value_enum = GL_COMPILE;
      break;

   case GL_VIEWPORT:
      v->value_float_4[0] = ctx->ViewportArray[0].X;
      v->value_float_4[1] = ctx->ViewportArray[0].Y;
      v->value_float_4[2] = ctx->ViewportArray[0].Width;
      v->value_float_4[3] = ctx->ViewportArray[0].Height;
      break;

   case GL_DEPTH_RANGE:
      v->value_double_2[0] = ctx->ViewportArray[0].Near;
      v->value_double_2[1] = ctx->ViewportArray[0].Far;
      break;

   case GL_ACTIVE_STENCIL_FACE_EXT:
      v->value_enum = ctx->Stencil.ActiveFace ? GL_BACK : GL_FRONT;
      break;

   case GL_STENCIL_FAIL:
      v->value_enum = ctx->Stencil.FailFunc[ctx->Stencil.ActiveFace];
      break;
   case GL_STENCIL_FUNC:
      v->value_enum = ctx->Stencil.Function[ctx->Stencil.ActiveFace];
      break;
   case GL_STENCIL_PASS_DEPTH_FAIL:
      v->value_enum = ctx->Stencil.ZFailFunc[ctx->Stencil.ActiveFace];
      break;
   case GL_STENCIL_PASS_DEPTH_PASS:
      v->value_enum = ctx->Stencil.ZPassFunc[ctx->Stencil.ActiveFace];
      break;
   case GL_STENCIL_REF:
      v->value_int = _mesa_get_stencil_ref(ctx, ctx->Stencil.ActiveFace);
      break;
   case GL_STENCIL_BACK_REF:
      v->value_int = _mesa_get_stencil_ref(ctx, 1);
      break;
   case GL_STENCIL_VALUE_MASK:
      v->value_int = ctx->Stencil.ValueMask[ctx->Stencil.ActiveFace];
      break;
   case GL_STENCIL_WRITEMASK:
      v->value_int = ctx->Stencil.WriteMask[ctx->Stencil.ActiveFace];
      break;

   case GL_NUM_EXTENSIONS:
      v->value_int = _mesa_get_extension_count(ctx);
      break;

   case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES:
      v->value_int = _mesa_get_color_read_type(ctx, NULL, "glGetIntegerv");
      break;
   case GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES:
      v->value_int = _mesa_get_color_read_format(ctx, NULL, "glGetIntegerv");
      break;

   case GL_CURRENT_MATRIX_STACK_DEPTH_ARB:
      v->value_int = ctx->CurrentStack->Depth + 1;
      break;
   case GL_CURRENT_MATRIX_ARB:
   case GL_TRANSPOSE_CURRENT_MATRIX_ARB:
      v->value_matrix = ctx->CurrentStack->Top;
      break;

   case GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB:
      v->value_int = _mesa_get_compressed_formats(ctx, NULL);
      break;
   case GL_COMPRESSED_TEXTURE_FORMATS_ARB:
      v->value_int_n.n =
         _mesa_get_compressed_formats(ctx, v->value_int_n.ints);
      assert(v->value_int_n.n <= (int) ARRAY_SIZE(v->value_int_n.ints));
      break;

   case GL_MAX_VARYING_FLOATS_ARB:
      v->value_int = ctx->Const.MaxVarying * 4;
      break;

   /* Various object names */

   case GL_TEXTURE_BINDING_1D:
   case GL_TEXTURE_BINDING_2D:
   case GL_TEXTURE_BINDING_3D:
   case GL_TEXTURE_BINDING_1D_ARRAY_EXT:
   case GL_TEXTURE_BINDING_2D_ARRAY_EXT:
   case GL_TEXTURE_BINDING_CUBE_MAP_ARB:
   case GL_TEXTURE_BINDING_RECTANGLE_NV:
   case GL_TEXTURE_BINDING_EXTERNAL_OES:
   case GL_TEXTURE_BINDING_CUBE_MAP_ARRAY:
   case GL_TEXTURE_BINDING_2D_MULTISAMPLE:
   case GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY:
      unit = ctx->Texture.CurrentUnit;
      v->value_int =
         ctx->Texture.Unit[unit].CurrentTex[d->offset]->Name;
      break;

   /* GL_EXT_external_objects */
   case GL_DRIVER_UUID_EXT:
      _mesa_get_driver_uuid(ctx, v->value_int_4);
      break;
   case GL_DEVICE_UUID_EXT:
      _mesa_get_device_uuid(ctx, v->value_int_4);
      break;

   /* GL_EXT_packed_float */
   case GL_RGBA_SIGNED_COMPONENTS_EXT:
      {
         /* Note: we only check the 0th color attachment. */
         const struct gl_renderbuffer *rb =
            ctx->DrawBuffer->_ColorDrawBuffers[0];
         if (rb && _mesa_is_format_signed(rb->Format)) {
            /* Issue 17 of GL_EXT_packed_float:  If a component (such as
             * alpha) has zero bits, the component should not be considered
             * signed and so the bit for the respective component should be
             * zeroed.
             */
            GLint r_bits =
               _mesa_get_format_bits(rb->Format, GL_RED_BITS);
            GLint g_bits =
               _mesa_get_format_bits(rb->Format, GL_GREEN_BITS);
            GLint b_bits =
               _mesa_get_format_bits(rb->Format, GL_BLUE_BITS);
            GLint a_bits =
               _mesa_get_format_bits(rb->Format, GL_ALPHA_BITS);
            GLint l_bits =
               _mesa_get_format_bits(rb->Format, GL_TEXTURE_LUMINANCE_SIZE);
            GLint i_bits =
               _mesa_get_format_bits(rb->Format, GL_TEXTURE_INTENSITY_SIZE);

            v->value_int_4[0] = r_bits + l_bits + i_bits > 0;
            v->value_int_4[1] = g_bits + l_bits + i_bits > 0;
            v->value_int_4[2] = b_bits + l_bits + i_bits > 0;
            v->value_int_4[3] = a_bits + i_bits > 0;
         }
         else {
            v->value_int_4[0] =
            v->value_int_4[1] =
            v->value_int_4[2] =
            v->value_int_4[3] = 0;
         }
      }
      break;

   /* GL_ARB_vertex_buffer_object */
   case GL_VERTEX_ARRAY_BUFFER_BINDING_ARB:
   case GL_NORMAL_ARRAY_BUFFER_BINDING_ARB:
   case GL_COLOR_ARRAY_BUFFER_BINDING_ARB:
   case GL_INDEX_ARRAY_BUFFER_BINDING_ARB:
   case GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB:
   case GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB:
   case GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB:
      buffer_obj = (struct gl_buffer_object **)
         ((char *) ctx->Array.VAO + d->offset);
      v->value_int = (*buffer_obj)->Name;
      break;
   case GL_ARRAY_BUFFER_BINDING_ARB:
      v->value_int = ctx->Array.ArrayBufferObj->Name;
      break;
   case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB:
      v->value_int =
         ctx->Array.VAO->BufferBinding[VERT_ATTRIB_TEX(ctx->Array.ActiveTexture)].BufferObj->Name;
      break;
   case GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB:
      v->value_int = ctx->Array.VAO->IndexBufferObj->Name;
      break;

   /* ARB_vertex_array_bgra */
   case GL_COLOR_ARRAY_SIZE:
      array = &ctx->Array.VAO->VertexAttrib[VERT_ATTRIB_COLOR0];
      v->value_int = array->Format.Format == GL_BGRA ? GL_BGRA : array->Format.Size;
      break;
   case GL_SECONDARY_COLOR_ARRAY_SIZE:
      array = &ctx->Array.VAO->VertexAttrib[VERT_ATTRIB_COLOR1];
      v->value_int = array->Format.Format == GL_BGRA ? GL_BGRA : array->Format.Size;
      break;

   /* ARB_copy_buffer */
   case GL_COPY_READ_BUFFER:
      v->value_int = ctx->CopyReadBuffer->Name;