mesa: Expose texture buffer objects when the context is GL 3.1 core.
[mesa.git] / src / mesa / main / teximage.c
index 2380efe8f3b88dc0504c54913a8b4afb2ffe642e..94a8bb795fcf6738d9a2cf3799f8eeb5022f8253 100644 (file)
 #include "mfeatures.h"
 #include "state.h"
 #include "texcompress.h"
+#include "texcompress_cpal.h"
 #include "teximage.h"
 #include "texobj.h"
 #include "texstate.h"
-#include "texpal.h"
 #include "mtypes.h"
 #include "glformats.h"
 
@@ -754,7 +754,8 @@ _mesa_select_tex_object(struct gl_context *ctx,
       case GL_PROXY_TEXTURE_2D_ARRAY_EXT:
          return arrayTex ? ctx->Texture.ProxyTex[TEXTURE_2D_ARRAY_INDEX] : NULL;
       case GL_TEXTURE_BUFFER:
-         return ctx->Extensions.ARB_texture_buffer_object
+         return _mesa_is_desktop_gl(ctx) 
+            && ctx->Extensions.ARB_texture_buffer_object
             ? texUnit->CurrentTex[TEXTURE_BUFFER_INDEX] : NULL;
       case GL_TEXTURE_EXTERNAL_OES:
          return ctx->Extensions.OES_EGL_image_external
@@ -839,8 +840,8 @@ _mesa_get_tex_image(struct gl_context *ctx, struct gl_texture_object *texObj,
  * \return pointer to texture image or NULL if invalid target, invalid
  *         level, or out of memory.
  */
-struct gl_texture_image *
-_mesa_get_proxy_tex_image(struct gl_context *ctx, GLenum target, GLint level)
+static struct gl_texture_image *
+get_proxy_tex_image(struct gl_context *ctx, GLenum target, GLint level)
 {
    struct gl_texture_image *texImage;
    GLuint texIndex;
@@ -947,9 +948,9 @@ _mesa_max_texture_levels(struct gl_context *ctx, GLenum target)
               ctx->Extensions.EXT_texture_array)
          ? ctx->Const.MaxTextureLevels : 0;
    case GL_TEXTURE_BUFFER:
-      return _mesa_is_desktop_gl(ctx) &&
-         (ctx->Extensions.ARB_texture_buffer_object ||
-          (ctx->Version >= 31)) ? 1 : 0;
+      return _mesa_is_desktop_gl(ctx)
+         && ctx->Extensions.ARB_texture_buffer_object
+         ? 1 : 0;
    case GL_TEXTURE_EXTERNAL_OES:
       /* fall-through */
    default:
@@ -1631,6 +1632,39 @@ _mesa_es_error_check_format_and_type(GLenum format, GLenum type,
    return type_valid ? GL_NO_ERROR : GL_INVALID_OPERATION;
 }
 
+
+
+/**
+ * Return expected size of a compressed texture.
+ */
+static GLuint
+compressed_tex_size(GLsizei width, GLsizei height, GLsizei depth,
+                    GLenum glformat)
+{
+   gl_format mesaFormat = _mesa_glenum_to_compressed_format(glformat);
+   return _mesa_format_image_size(mesaFormat, width, height, depth);
+}
+
+
+/*
+ * Return compressed texture block size, in pixels.
+ */
+static void
+get_compressed_block_size(GLenum glformat, GLuint *bw, GLuint *bh)
+{
+   gl_format mesaFormat = _mesa_glenum_to_compressed_format(glformat);
+   _mesa_get_format_block_size(mesaFormat, bw, bh);
+}
+
+
+/**
+ * Special value returned by error some texture error checking functions when
+ * an error is detected and the proxy texture image's width/height/depth/format
+ * fields should be zeroed-out.
+ */
+#define PROXY_ERROR 2
+
+
 /**
  * Test the glTexImage[123]D() parameters for errors.
  * 
@@ -1646,13 +1680,14 @@ _mesa_es_error_check_format_and_type(GLenum format, GLenum type,
  * \param depth image depth given by the user.
  * \param border image border given by the user.
  * 
- * \return GL_TRUE if an error was detected, or GL_FALSE if no errors.
+ * \return PROXY_ERROR if there's an error that should zero-out the proxy image,
+ *         GL_TRUE if a regular GL error is found, or GL_FALSE if no error, 
  *
  * Verifies each of the parameters against the constants specified in
  * __struct gl_contextRec::Const and the supported extensions, and according
  * to the OpenGL specification.
  */
-static GLboolean
+static GLenum
 texture_error_check( struct gl_context *ctx,
                      GLuint dimensions, GLenum target,
                      GLint level, GLint internalFormat,
@@ -1672,12 +1707,17 @@ texture_error_check( struct gl_context *ctx,
     */
    const GLboolean indexFormat = (format == GL_COLOR_INDEX);
 
+   /* Note: for proxy textures, some error conditions immediately generate
+    * a GL error in the usual way.  But others do not generate a GL error.
+    * Instead, they cause the width, height, depth, format fields of the
+    * texture image to be zeroed-out.  The GL spec seems to indicate that the
+    * zero-out behaviour is only used in cases related to memory allocation.
+    */
+
    /* Basic level check (more checking in ctx->Driver.TestProxyTexImage) */
    if (level < 0 || level >= MAX_TEXTURE_LEVELS) {
-      if (!isProxy) {
-         _mesa_error(ctx, GL_INVALID_VALUE,
-                     "glTexImage%dD(level=%d)", dimensions, level);
-      }
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glTexImage%dD(level=%d)", dimensions, level);
       return GL_TRUE;
    }
 
@@ -1686,18 +1726,14 @@ texture_error_check( struct gl_context *ctx,
        ((ctx->API != API_OPENGL ||
          target == GL_TEXTURE_RECTANGLE_NV ||
          target == GL_PROXY_TEXTURE_RECTANGLE_NV) && border != 0)) {
-      if (!isProxy) {
-         _mesa_error(ctx, GL_INVALID_VALUE,
-                     "glTexImage%dD(border=%d)", dimensions, border);
-      }
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glTexImage%dD(border=%d)", dimensions, border);
       return GL_TRUE;
    }
 
    if (width < 0 || height < 0 || depth < 0) {
-      if (!isProxy) {
-         _mesa_error(ctx, GL_INVALID_VALUE,
-                     "glTexImage%dD(width, height or depth < 0)", dimensions);
-      }
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glTexImage%dD(width, height or depth < 0)", dimensions);
       return GL_TRUE;
    }
 
@@ -1741,32 +1777,32 @@ texture_error_check( struct gl_context *ctx,
                                                     type, width, height,
                                                     depth, border);
    if (!sizeOK) {
-      if (!isProxy) {
+      if (isProxy) {
+         /* No GL error is recorded, but we need to zero-out the image dims */
+         return PROXY_ERROR;
+      }
+      else {
          _mesa_error(ctx, GL_INVALID_VALUE,
                      "glTexImage%dD(level=%d, width=%d, height=%d, depth=%d)",
                      dimensions, level, width, height, depth);
+         return GL_TRUE;
       }
-      return GL_TRUE;
    }
 
    /* Check internalFormat */
    if (_mesa_base_tex_format(ctx, internalFormat) < 0) {
-      if (!isProxy) {
-         _mesa_error(ctx, GL_INVALID_VALUE,
-                     "glTexImage%dD(internalFormat=%s)",
-                     dimensions, _mesa_lookup_enum_by_nr(internalFormat));
-      }
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "glTexImage%dD(internalFormat=%s)",
+                  dimensions, _mesa_lookup_enum_by_nr(internalFormat));
       return GL_TRUE;
    }
 
    /* Check incoming image format and type */
    err = _mesa_error_check_format_and_type(ctx, format, type);
    if (err != GL_NO_ERROR) {
-      if (!isProxy) {
-         _mesa_error(ctx, err,
-                     "glTexImage%dD(incompatible format 0x%x, type 0x%x)",
-                     dimensions, format, type);
-      }
+      _mesa_error(ctx, err,
+                  "glTexImage%dD(incompatible format 0x%x, type 0x%x)",
+                  dimensions, format, type);
       return GL_TRUE;
    }
 
@@ -1777,10 +1813,9 @@ texture_error_check( struct gl_context *ctx,
        (_mesa_is_ycbcr_format(internalFormat) != _mesa_is_ycbcr_format(format)) ||
        (_mesa_is_depthstencil_format(internalFormat) != _mesa_is_depthstencil_format(format)) ||
        (_mesa_is_dudv_format(internalFormat) != _mesa_is_dudv_format(format))) {
-      if (!isProxy)
-         _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glTexImage%dD(incompatible internalFormat 0x%x, format 0x%x)",
-                     dimensions, internalFormat, format);
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glTexImage%dD(incompatible internalFormat 0x%x, format 0x%x)",
+                  dimensions, internalFormat, format);
       return GL_TRUE;
    }
 
@@ -1791,7 +1826,8 @@ texture_error_check( struct gl_context *ctx,
           type != GL_UNSIGNED_SHORT_8_8_REV_MESA) {
          char message[100];
          _mesa_snprintf(message, sizeof(message),
-                        "glTexImage%dD(format/type YCBCR mismatch", dimensions);
+                        "glTexImage%dD(format/type YCBCR mismatch)",
+                        dimensions);
          _mesa_error(ctx, GL_INVALID_ENUM, "%s", message);
          return GL_TRUE; /* error */
       }
@@ -1799,18 +1835,17 @@ texture_error_check( struct gl_context *ctx,
           target != GL_PROXY_TEXTURE_2D &&
           target != GL_TEXTURE_RECTANGLE_NV &&
           target != GL_PROXY_TEXTURE_RECTANGLE_NV) {
-         if (!isProxy)
-            _mesa_error(ctx, GL_INVALID_ENUM, "glTexImage(target)");
+         _mesa_error(ctx, GL_INVALID_ENUM,
+                     "glTexImage%dD(bad target for YCbCr texture)",
+                     dimensions);
          return GL_TRUE;
       }
       if (border != 0) {
-         if (!isProxy) {
-            char message[100];
-            _mesa_snprintf(message, sizeof(message),
-                           "glTexImage%dD(format=GL_YCBCR_MESA and border=%d)",
-                           dimensions, border);
-            _mesa_error(ctx, GL_INVALID_VALUE, "%s", message);
-         }
+         char message[100];
+         _mesa_snprintf(message, sizeof(message),
+                        "glTexImage%dD(format=GL_YCBCR_MESA and border=%d)",
+                        dimensions, border);
+         _mesa_error(ctx, GL_INVALID_VALUE, "%s", message);
          return GL_TRUE;
       }
    }
@@ -1831,9 +1866,9 @@ texture_error_check( struct gl_context *ctx,
           target != GL_PROXY_TEXTURE_RECTANGLE_ARB &&
          !((_mesa_is_cube_face(target) || target == GL_PROXY_TEXTURE_CUBE_MAP) &&
            (ctx->Version >= 30 || ctx->Extensions.EXT_gpu_shader4))) {
-         if (!isProxy)
-            _mesa_error(ctx, GL_INVALID_ENUM,
-                        "glTexImage(target/internalFormat)");
+         _mesa_error(ctx, GL_INVALID_ENUM,
+                     "glTexImage%dD(bad target for depth texture)",
+                     dimensions);
          return GL_TRUE;
       }
    }
@@ -1841,21 +1876,18 @@ texture_error_check( struct gl_context *ctx,
    /* additional checks for compressed textures */
    if (_mesa_is_compressed_format(ctx, internalFormat)) {
       if (!target_can_be_compressed(ctx, target, internalFormat)) {
-         if (!isProxy)
-            _mesa_error(ctx, GL_INVALID_ENUM,
-                        "glTexImage%dD(target)", dimensions);
+         _mesa_error(ctx, GL_INVALID_ENUM,
+                     "glTexImage%dD(target can't be compressed)", dimensions);
          return GL_TRUE;
       }
       if (compressedteximage_only_format(ctx, internalFormat)) {
          _mesa_error(ctx, GL_INVALID_OPERATION,
-               "glTexImage%dD(no compression for format)", dimensions);
+                     "glTexImage%dD(no compression for format)", dimensions);
          return GL_TRUE;
       }
       if (border != 0) {
-         if (!isProxy) {
-            _mesa_error(ctx, GL_INVALID_OPERATION,
-                        "glTexImage%dD(border!=0)", dimensions);
-         }
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "glTexImage%dD(border!=0)", dimensions);
          return GL_TRUE;
       }
    }
@@ -1864,11 +1896,9 @@ texture_error_check( struct gl_context *ctx,
    if ((ctx->Version >= 30 || ctx->Extensions.EXT_texture_integer) &&
        (_mesa_is_enum_format_integer(format) !=
         _mesa_is_enum_format_integer(internalFormat))) {
-      if (!isProxy) {
-         _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glTexImage%dD(integer/non-integer format mismatch)",
-                     dimensions);
-      }
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glTexImage%dD(integer/non-integer format mismatch)",
+                  dimensions);
       return GL_TRUE;
    }
 
@@ -1883,6 +1913,187 @@ texture_error_check( struct gl_context *ctx,
 }
 
 
+/**
+ * Error checking for glCompressedTexImage[123]D().
+ * \param reason  returns reason for error, if any
+ * \return error code or GL_NO_ERROR or PROXY_ERROR.
+ */
+static GLenum
+compressed_texture_error_check(struct gl_context *ctx, GLint dimensions,
+                               GLenum target, GLint level,
+                               GLenum internalFormat, GLsizei width,
+                               GLsizei height, GLsizei depth, GLint border,
+                               GLsizei imageSize)
+{
+   const GLenum proxyTarget = get_proxy_target(target);
+   const GLint maxLevels = _mesa_max_texture_levels(ctx, target);
+   GLint expectedSize;
+   GLenum choose_format;
+   GLenum choose_type;
+   GLenum proxy_format;
+   GLenum error = GL_NO_ERROR;
+   char *reason = ""; /* no error */
+
+   if (!target_can_be_compressed(ctx, target, internalFormat)) {
+      reason = "target";
+      error = GL_INVALID_ENUM;
+      goto error;
+   }
+
+   /* This will detect any invalid internalFormat value */
+   if (!_mesa_is_compressed_format(ctx, internalFormat)) {
+      reason = "internalFormat";
+      error = GL_INVALID_ENUM;
+      goto error;
+   }
+
+   switch (internalFormat) {
+#if FEATURE_ES
+   case GL_PALETTE4_RGB8_OES:
+   case GL_PALETTE4_RGBA8_OES:
+   case GL_PALETTE4_R5_G6_B5_OES:
+   case GL_PALETTE4_RGBA4_OES:
+   case GL_PALETTE4_RGB5_A1_OES:
+   case GL_PALETTE8_RGB8_OES:
+   case GL_PALETTE8_RGBA8_OES:
+   case GL_PALETTE8_R5_G6_B5_OES:
+   case GL_PALETTE8_RGBA4_OES:
+   case GL_PALETTE8_RGB5_A1_OES:
+      _mesa_cpal_compressed_format_type(internalFormat, &choose_format,
+                                       &choose_type);
+      proxy_format = choose_format;
+
+      /* check level (note that level should be zero or less!) */
+      if (level > 0 || level < -maxLevels) {
+        reason = "level";
+        error = GL_INVALID_VALUE;
+         goto error;
+      }
+
+      if (dimensions != 2) {
+        reason = "compressed paletted textures must be 2D";
+        error = GL_INVALID_OPERATION;
+         goto error;
+      }
+
+      /* Figure out the expected texture size (in bytes).  This will be
+       * checked against the actual size later.
+       */
+      expectedSize = _mesa_cpal_compressed_size(level, internalFormat,
+                                               width, height);
+
+      /* This is for the benefit of the TestProxyTexImage below.  It expects
+       * level to be non-negative.  OES_compressed_paletted_texture uses a
+       * weird mechanism where the level specified to glCompressedTexImage2D
+       * is -(n-1) number of levels in the texture, and the data specifies the
+       * complete mipmap stack.  This is done to ensure the palette is the
+       * same for all levels.
+       */
+      level = -level;
+      break;
+#endif
+
+   default:
+      choose_format = GL_NONE;
+      choose_type = GL_NONE;
+      proxy_format = internalFormat;
+
+      /* check level */
+      if (level < 0 || level >= maxLevels) {
+        reason = "level";
+        error = GL_INVALID_VALUE;
+         goto error;
+      }
+
+      /* Figure out the expected texture size (in bytes).  This will be
+       * checked against the actual size later.
+       */
+      expectedSize = compressed_tex_size(width, height, depth, internalFormat);
+      break;
+   }
+
+   /* This should really never fail */
+   if (_mesa_base_tex_format(ctx, internalFormat) < 0) {
+      reason = "internalFormat";
+      error = GL_INVALID_ENUM;
+      goto error;
+   }
+
+   /* No compressed formats support borders at this time */
+   if (border != 0) {
+      reason = "border != 0";
+      error = GL_INVALID_VALUE;
+      goto error;
+   }
+
+   /* For cube map, width must equal height */
+   if (_mesa_is_cube_face(target) && width != height) {
+      reason = "width != height";
+      error = GL_INVALID_VALUE;
+      goto error;
+   }
+
+   /* check image size against compression block size */
+   {
+      gl_format texFormat =
+         ctx->Driver.ChooseTextureFormat(ctx, target, proxy_format,
+                                        choose_format, choose_type);
+      GLuint bw, bh;
+
+      _mesa_get_format_block_size(texFormat, &bw, &bh);
+      if ((width > bw && width % bw > 0) ||
+          (height > bh && height % bh > 0)) {
+         /*
+          * Per GL_ARB_texture_compression:  GL_INVALID_OPERATION is
+          * generated [...] if any parameter combinations are not
+          * supported by the specific compressed internal format. 
+          */
+         reason = "invalid width or height for compression format";
+         error = GL_INVALID_OPERATION;
+         goto error;
+      }
+   }
+
+   /* check image sizes */
+   if (!ctx->Driver.TestProxyTexImage(ctx, proxyTarget, level,
+                                     proxy_format, choose_format,
+                                     choose_type,
+                                     width, height, depth, border)) {
+      /* See error comment above */
+      if (target == proxyTarget) {
+         return PROXY_ERROR;
+      }
+      reason = "invalid width, height or format";
+      error = GL_INVALID_OPERATION;
+      goto error;
+   }
+
+   /* check image size in bytes */
+   if (expectedSize != imageSize) {
+      /* Per GL_ARB_texture_compression:  GL_INVALID_VALUE is generated [...]
+       * if <imageSize> is not consistent with the format, dimensions, and
+       * contents of the specified image.
+       */
+      reason = "imageSize inconsistant with width/height/format";
+      error = GL_INVALID_VALUE;
+      goto error;
+   }
+
+   if (!mutable_tex_object(ctx, target)) {
+      reason = "immutable texture";
+      error = GL_INVALID_OPERATION;
+      goto error;
+   }
+
+   return GL_NO_ERROR;
+
+error:
+   _mesa_error(ctx, error, "glCompressedTexImage%dD(%s)", dimensions, reason);
+   return error;
+}
+
+
+
 /**
  * Test glTexSubImage[123]D() parameters for errors.
  * 
@@ -2587,7 +2798,8 @@ _mesa_choose_texture_format(struct gl_context *ctx,
    }
 
    /* choose format from scratch */
-   f = ctx->Driver.ChooseTextureFormat(ctx, internalFormat, format, type);
+   f = ctx->Driver.ChooseTextureFormat(ctx, texObj->Target, internalFormat,
+                                       format, type);
    ASSERT(f != MESA_FORMAT_NONE);
    return f;
 }
@@ -2635,71 +2847,125 @@ strip_texture_border(GLenum target,
    }
 }
 
+
 /**
- * Common code to implement all the glTexImage1D/2D/3D functions.
+ * Common code to implement all the glTexImage1D/2D/3D functions
+ * as well as glCompressedTexImage1D/2D/3D.
+ * \param compressed  only GL_TRUE for glCompressedTexImage1D/2D/3D calls.
+ * \param format  the user's image format (only used if !compressed)
+ * \param type  the user's image type (only used if !compressed)
+ * \param imageSize  only used for glCompressedTexImage1D/2D/3D calls.
  */
 static void
-teximage(struct gl_context *ctx, GLuint dims,
+teximage(struct gl_context *ctx, GLboolean compressed, GLuint dims,
          GLenum target, GLint level, GLint internalFormat,
          GLsizei width, GLsizei height, GLsizei depth,
          GLint border, GLenum format, GLenum type,
-         const GLvoid *pixels)
+         GLsizei imageSize, const GLvoid *pixels)
 {
-   GLboolean error;
+   const char *func = compressed ? "glCompressedTexImage" : "glTexImage";
+   GLenum error;
    struct gl_pixelstore_attrib unpack_no_border;
    const struct gl_pixelstore_attrib *unpack = &ctx->Unpack;
 
    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
-   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx, "glTexImage%uD %s %d %s %d %d %d %d %s %s %p\n",
-                  dims,
-                  _mesa_lookup_enum_by_nr(target), level,
-                  _mesa_lookup_enum_by_nr(internalFormat),
-                  width, height, depth, border,
-                  _mesa_lookup_enum_by_nr(format),
-                  _mesa_lookup_enum_by_nr(type), pixels);
+   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE)) {
+      if (compressed)
+         _mesa_debug(ctx,
+                     "glCompressedTexImage%uD %s %d %s %d %d %d %d %p\n",
+                     dims,
+                     _mesa_lookup_enum_by_nr(target), level,
+                     _mesa_lookup_enum_by_nr(internalFormat),
+                     width, height, depth, border, pixels);
+      else
+         _mesa_debug(ctx,
+                     "glTexImage%uD %s %d %s %d %d %d %d %s %s %p\n",
+                     dims,
+                     _mesa_lookup_enum_by_nr(target), level,
+                     _mesa_lookup_enum_by_nr(internalFormat),
+                     width, height, depth, border,
+                     _mesa_lookup_enum_by_nr(format),
+                     _mesa_lookup_enum_by_nr(type), pixels);
+   }
 
    internalFormat = override_internal_format(internalFormat, width, height);
 
    /* target error checking */
    if (!legal_teximage_target(ctx, dims, target)) {
-      _mesa_error(ctx, GL_INVALID_ENUM, "glTexImage%uD(target=%s)",
-                  dims, _mesa_lookup_enum_by_nr(target));
+      _mesa_error(ctx, GL_INVALID_ENUM, "%s%uD(target=%s)",
+                  func, dims, _mesa_lookup_enum_by_nr(target));
       return;
    }
 
    /* general error checking */
-   error = texture_error_check(ctx, dims, target, level, internalFormat,
-                               format, type, width, height, depth, border);
+   if (compressed) {
+      error = compressed_texture_error_check(ctx, dims, target, level,
+                                             internalFormat,
+                                             width, height, depth,
+                                             border, imageSize);
+   }
+   else {
+      error = texture_error_check(ctx, dims, target, level, internalFormat,
+                                  format, type, width, height, depth, border);
+   }
+
+#if FEATURE_ES
+   /* Here we convert a cpal compressed image into a regular glTexImage2D
+    * call by decompressing the texture.  If we really want to support cpal
+    * textures in any driver this would have to be changed.
+    */
+   if (compressed && !error && dims == 2) {
+      switch (internalFormat) {
+      case GL_PALETTE4_RGB8_OES:
+      case GL_PALETTE4_RGBA8_OES:
+      case GL_PALETTE4_R5_G6_B5_OES:
+      case GL_PALETTE4_RGBA4_OES:
+      case GL_PALETTE4_RGB5_A1_OES:
+      case GL_PALETTE8_RGB8_OES:
+      case GL_PALETTE8_RGBA8_OES:
+      case GL_PALETTE8_R5_G6_B5_OES:
+      case GL_PALETTE8_RGBA4_OES:
+      case GL_PALETTE8_RGB5_A1_OES:
+         _mesa_cpal_compressed_teximage2d(target, level, internalFormat,
+                                          width, height, imageSize, pixels);
+         return;
+      }
+   }
+#endif
 
    if (_mesa_is_proxy_texture(target)) {
       /* Proxy texture: just clear or set state depending on error checking */
       struct gl_texture_image *texImage =
-         _mesa_get_proxy_tex_image(ctx, target, level);
+         get_proxy_tex_image(ctx, target, level);
+      gl_format texFormat = MESA_FORMAT_NONE;
 
-      if (error) {
-         /* when error, clear all proxy texture image parameters */
-         if (texImage)
-            clear_teximage_fields(texImage);
-      }
-      else {
-         /* no error, set the tex image parameters */
+      if (!error) {
+         /* No parameter errors.  Choose a texture format and see if we
+          * can really allocate the texture.
+          */
          struct gl_texture_object *texObj =
             _mesa_get_current_tex_object(ctx, target);
-         gl_format texFormat = _mesa_choose_texture_format(ctx, texObj,
-                                                           target, level,
-                                                           internalFormat,
-                                                           format, type);
-
-         if (legal_texture_size(ctx, texFormat, width, height, depth)) {
-            _mesa_init_teximage_fields(ctx, texImage, width, height,
-                                       depth, border, internalFormat,
-                                       texFormat);
+         texFormat = _mesa_choose_texture_format(ctx, texObj, target, level,
+                                                 internalFormat, format, type);
+         if (!legal_texture_size(ctx, texFormat, width, height, depth)) {
+            error = PROXY_ERROR;
          }
-         else if (texImage) {
+      }
+
+      if (error == PROXY_ERROR) {
+         /* image too large, etc.  Clear all proxy texture image parameters. */
+         if (texImage)
             clear_teximage_fields(texImage);
-         }
+      }
+      else if (error == GL_FALSE) {
+         /* no error: store the teximage parameters */
+         if (texImage)
+            _mesa_init_teximage_fields(ctx, texImage, width, height, depth,
+                                       border, internalFormat, texFormat);
+      }
+      else {
+         /* other, regular error (was already recorded) */
       }
    }
    else {
@@ -2733,7 +2999,7 @@ teximage(struct gl_context *ctx, GLuint dims,
         texImage = _mesa_get_tex_image(ctx, texObj, target, level);
 
         if (!texImage) {
-           _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage%uD", dims);
+           _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s%uD", func, dims);
         }
          else {
             gl_format texFormat;
@@ -2750,8 +3016,14 @@ teximage(struct gl_context *ctx, GLuint dims,
                                           border, internalFormat, texFormat);
 
                /* Give the texture to the driver.  <pixels> may be null. */
-               ctx->Driver.TexImage(ctx, dims, texImage, format,
-                                    type, pixels, unpack);
+               if (compressed) {
+                  ctx->Driver.CompressedTexImage(ctx, dims, texImage,
+                                                 imageSize, pixels);
+               }
+               else {
+                  ctx->Driver.TexImage(ctx, dims, texImage, format,
+                                       type, pixels, unpack);
+               }
 
                check_gen_mipmap(ctx, target, texObj, level);
 
@@ -2760,7 +3032,7 @@ teximage(struct gl_context *ctx, GLuint dims,
                _mesa_dirty_texobj(ctx, texObj, GL_TRUE);
             }
             else {
-               _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage%uD", dims);
+               _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s%uD", func, dims);
             }
          }
       }
@@ -2778,8 +3050,8 @@ _mesa_TexImage1D( GLenum target, GLint level, GLint internalFormat,
                   GLenum type, const GLvoid *pixels )
 {
    GET_CURRENT_CONTEXT(ctx);
-   teximage(ctx, 1, target, level, internalFormat, width, 1, 1,
-            border, format, type, pixels);
+   teximage(ctx, GL_FALSE, 1, target, level, internalFormat, width, 1, 1,
+            border, format, type, 0, pixels);
 }
 
 
@@ -2790,8 +3062,8 @@ _mesa_TexImage2D( GLenum target, GLint level, GLint internalFormat,
                   const GLvoid *pixels )
 {
    GET_CURRENT_CONTEXT(ctx);
-   teximage(ctx, 2, target, level, internalFormat, width, height, 1,
-            border, format, type, pixels);
+   teximage(ctx, GL_FALSE, 2, target, level, internalFormat, width, height, 1,
+            border, format, type, 0, pixels);
 }
 
 
@@ -2806,8 +3078,9 @@ _mesa_TexImage3D( GLenum target, GLint level, GLint internalFormat,
                   const GLvoid *pixels )
 {
    GET_CURRENT_CONTEXT(ctx);
-   teximage(ctx, 3, target, level, internalFormat, width, height, depth,
-            border, format, type, pixels);
+   teximage(ctx, GL_FALSE, 3, target, level, internalFormat,
+            width, height, depth,
+            border, format, type, 0, pixels);
 }
 
 
@@ -3251,190 +3524,6 @@ _mesa_CopyTexSubImage3D( GLenum target, GLint level,
 /**********************************************************************/
 
 
-/**
- * Return expected size of a compressed texture.
- */
-static GLuint
-compressed_tex_size(GLsizei width, GLsizei height, GLsizei depth,
-                    GLenum glformat)
-{
-   gl_format mesaFormat = _mesa_glenum_to_compressed_format(glformat);
-   return _mesa_format_image_size(mesaFormat, width, height, depth);
-}
-
-
-/*
- * Return compressed texture block size, in pixels.
- */
-static void
-get_compressed_block_size(GLenum glformat, GLuint *bw, GLuint *bh)
-{
-   gl_format mesaFormat = _mesa_glenum_to_compressed_format(glformat);
-   _mesa_get_format_block_size(mesaFormat, bw, bh);
-}
-
-
-/**
- * Error checking for glCompressedTexImage[123]D().
- * \param reason  returns reason for error, if any
- * \return error code or GL_NO_ERROR.
- */
-static GLenum
-compressed_texture_error_check(struct gl_context *ctx, GLint dimensions,
-                               GLenum target, GLint level,
-                               GLenum internalFormat, GLsizei width,
-                               GLsizei height, GLsizei depth, GLint border,
-                               GLsizei imageSize, char **reason)
-{
-   const GLenum proxyTarget = get_proxy_target(target);
-   const GLint maxLevels = _mesa_max_texture_levels(ctx, target);
-   GLint expectedSize;
-   GLenum choose_format;
-   GLenum choose_type;
-   GLenum proxy_format;
-
-   *reason = ""; /* no error */
-
-   if (!target_can_be_compressed(ctx, target, internalFormat)) {
-      *reason = "target";
-      return GL_INVALID_ENUM;
-   }
-
-   /* This will detect any invalid internalFormat value */
-   if (!_mesa_is_compressed_format(ctx, internalFormat)) {
-      *reason = "internalFormat";
-      return GL_INVALID_ENUM;
-   }
-
-   switch (internalFormat) {
-#if FEATURE_ES
-   case GL_PALETTE4_RGB8_OES:
-   case GL_PALETTE4_RGBA8_OES:
-   case GL_PALETTE4_R5_G6_B5_OES:
-   case GL_PALETTE4_RGBA4_OES:
-   case GL_PALETTE4_RGB5_A1_OES:
-   case GL_PALETTE8_RGB8_OES:
-   case GL_PALETTE8_RGBA8_OES:
-   case GL_PALETTE8_R5_G6_B5_OES:
-   case GL_PALETTE8_RGBA4_OES:
-   case GL_PALETTE8_RGB5_A1_OES:
-      _mesa_cpal_compressed_format_type(internalFormat, &choose_format,
-                                       &choose_type);
-      proxy_format = choose_format;
-
-      /* check level */
-      if (level > 0 || level < -maxLevels) {
-        *reason = "level";
-        return GL_INVALID_VALUE;
-      }
-
-      if (dimensions != 2) {
-        *reason = "compressed paletted textures must be 2D";
-        return GL_INVALID_OPERATION;
-      }
-
-      /* Figure out the expected texture size (in bytes).  This will be
-       * checked against the actual size later.
-       */
-      expectedSize = _mesa_cpal_compressed_size(level, internalFormat,
-                                               width, height);
-
-      /* This is for the benefit of the TestProxyTexImage below.  It expects
-       * level to be non-negative.  OES_compressed_paletted_texture uses a
-       * weird mechanism where the level specified to glCompressedTexImage2D
-       * is -(n-1) number of levels in the texture, and the data specifies the
-       * complete mipmap stack.  This is done to ensure the palette is the
-       * same for all levels.
-       */
-      level = -level;
-      break;
-#endif
-
-   default:
-      choose_format = GL_NONE;
-      choose_type = GL_NONE;
-      proxy_format = internalFormat;
-
-      /* check level */
-      if (level < 0 || level >= maxLevels) {
-        *reason = "level";
-        return GL_INVALID_VALUE;
-      }
-
-      /* Figure out the expected texture size (in bytes).  This will be
-       * checked against the actual size later.
-       */
-      expectedSize = compressed_tex_size(width, height, depth, internalFormat);
-      break;
-   }
-
-   /* This should really never fail */
-   if (_mesa_base_tex_format(ctx, internalFormat) < 0) {
-      *reason = "internalFormat";
-      return GL_INVALID_ENUM;
-   }
-
-   /* No compressed formats support borders at this time */
-   if (border != 0) {
-      *reason = "border != 0";
-      return GL_INVALID_VALUE;
-   }
-
-   /* For cube map, width must equal height */
-   if (_mesa_is_cube_face(target) && width != height) {
-      *reason = "width != height";
-      return GL_INVALID_VALUE;
-   }
-
-   /* check image size against compression block size */
-   {
-      gl_format texFormat =
-         ctx->Driver.ChooseTextureFormat(ctx, proxy_format,
-                                        choose_format, choose_type);
-      GLuint bw, bh;
-
-      _mesa_get_format_block_size(texFormat, &bw, &bh);
-      if ((width > bw && width % bw > 0) ||
-          (height > bh && height % bh > 0)) {
-         /*
-          * Per GL_ARB_texture_compression:  GL_INVALID_OPERATION is
-          * generated [...] if any parameter combinations are not
-          * supported by the specific compressed internal format. 
-          */
-         *reason = "invalid width or height for compression format";
-         return GL_INVALID_OPERATION;
-      }
-   }
-
-   /* check image sizes */
-   if (!ctx->Driver.TestProxyTexImage(ctx, proxyTarget, level,
-                                     proxy_format, choose_format,
-                                     choose_type,
-                                     width, height, depth, border)) {
-      /* See error comment above */
-      *reason = "invalid width, height or format";
-      return GL_INVALID_OPERATION;
-   }
-
-   /* check image size in bytes */
-   if (expectedSize != imageSize) {
-      /* Per GL_ARB_texture_compression:  GL_INVALID_VALUE is generated [...]
-       * if <imageSize> is not consistent with the format, dimensions, and
-       * contents of the specified image.
-       */
-      *reason = "imageSize inconsistant with width/height/format";
-      return GL_INVALID_VALUE;
-   }
-
-   if (!mutable_tex_object(ctx, target)) {
-      *reason = "immutable texture";
-      return GL_INVALID_OPERATION;
-   }
-
-   return GL_NO_ERROR;
-}
-
-
 /**
  * Error checking for glCompressedTexSubImage[123]D().
  * \warning  There are some bad assumptions here about the size of compressed
@@ -3577,141 +3666,6 @@ compressed_subtexture_error_check2(struct gl_context *ctx, GLuint dims,
 }
 
 
-/**
- * Implementation of the glCompressedTexImage1/2/3D() functions.
- */
-static void
-compressedteximage(struct gl_context *ctx, GLuint dims,
-                   GLenum target, GLint level,
-                   GLenum internalFormat, GLsizei width,
-                   GLsizei height, GLsizei depth, GLint border,
-                   GLsizei imageSize, const GLvoid *data)
-{
-   GLenum error;
-   char *reason = "";
-
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
-
-   if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
-      _mesa_debug(ctx,
-                  "glCompressedTexImage%uDARB %s %d %s %d %d %d %d %d %p\n",
-                  dims,
-                  _mesa_lookup_enum_by_nr(target), level,
-                  _mesa_lookup_enum_by_nr(internalFormat),
-                  width, height, depth, border, imageSize, data);
-
-   /* check target */
-   if (!legal_teximage_target(ctx, dims, target)) {
-      _mesa_error(ctx, GL_INVALID_ENUM, "glCompressedTexImage%uD(target=%s)",
-                  dims, _mesa_lookup_enum_by_nr(target));
-      return;
-   }
-
-   error = compressed_texture_error_check(ctx, dims, target, level,
-                                          internalFormat, width, height, depth,
-                                          border, imageSize, &reason);
-
-#if FEATURE_ES
-   /* XXX this is kind of a hack */
-   if (!error && dims == 2) {
-      switch (internalFormat) {
-      case GL_PALETTE4_RGB8_OES:
-      case GL_PALETTE4_RGBA8_OES:
-      case GL_PALETTE4_R5_G6_B5_OES:
-      case GL_PALETTE4_RGBA4_OES:
-      case GL_PALETTE4_RGB5_A1_OES:
-      case GL_PALETTE8_RGB8_OES:
-      case GL_PALETTE8_RGBA8_OES:
-      case GL_PALETTE8_R5_G6_B5_OES:
-      case GL_PALETTE8_RGBA4_OES:
-      case GL_PALETTE8_RGB5_A1_OES:
-         _mesa_cpal_compressed_teximage2d(target, level, internalFormat,
-                                          width, height, imageSize, data);
-         return;
-      }
-   }
-#endif
-
-   if (_mesa_is_proxy_texture(target)) {
-      /* Proxy texture: just check for errors and update proxy state */
-      struct gl_texture_image *texImage;
-
-      if (!error) {
-         struct gl_texture_object *texObj =
-            _mesa_get_current_tex_object(ctx, target);
-         gl_format texFormat =
-            _mesa_choose_texture_format(ctx, texObj, target, level,
-                                        internalFormat, GL_NONE, GL_NONE);
-         if (!legal_texture_size(ctx, texFormat, width, height, depth)) {
-            error = GL_OUT_OF_MEMORY;
-         }
-      }
-
-      texImage = _mesa_get_proxy_tex_image(ctx, target, level);
-      if (texImage) {
-         if (error) {
-            /* if error, clear all proxy texture image parameters */
-            clear_teximage_fields(texImage);
-         }
-         else {
-            /* no error: store the teximage parameters */
-            _mesa_init_teximage_fields(ctx, texImage, width, height,
-                                       depth, border, internalFormat,
-                                       MESA_FORMAT_NONE);
-         }
-      }
-   }
-   else {
-      /* non-proxy target */
-      struct gl_texture_object *texObj;
-      struct gl_texture_image *texImage;
-
-      if (error) {
-         _mesa_error(ctx, error, "glCompressedTexImage%uD(%s)", dims, reason);
-         return;
-      }
-
-      texObj = _mesa_get_current_tex_object(ctx, target);
-
-      _mesa_lock_texture(ctx, texObj);
-      {
-        texImage = _mesa_get_tex_image(ctx, texObj, target, level);
-        if (!texImage) {
-           _mesa_error(ctx, GL_OUT_OF_MEMORY,
-                        "glCompressedTexImage%uD", dims);
-        }
-         else {
-            gl_format texFormat;
-
-            ctx->Driver.FreeTextureImageBuffer(ctx, texImage);
-
-            texFormat = _mesa_choose_texture_format(ctx, texObj, target, level,
-                                                    internalFormat, GL_NONE,
-                                                    GL_NONE);
-
-            if (legal_texture_size(ctx, texFormat, width, height, depth)) {
-               _mesa_init_teximage_fields(ctx, texImage,
-                                          width, height, depth,
-                                          border, internalFormat, texFormat);
-
-               ctx->Driver.CompressedTexImage(ctx, dims, texImage, imageSize,
-                                              data);
-
-               check_gen_mipmap(ctx, target, texObj, level);
-
-               _mesa_dirty_texobj(ctx, texObj, GL_TRUE);
-            }
-            else {
-               _mesa_error(ctx, GL_OUT_OF_MEMORY,
-                           "glCompressedTexImage%uD", dims);
-            }
-         }
-      }
-      _mesa_unlock_texture(ctx, texObj);
-   }
-}
-
-
 void GLAPIENTRY
 _mesa_CompressedTexImage1DARB(GLenum target, GLint level,
                               GLenum internalFormat, GLsizei width,
@@ -3719,8 +3673,8 @@ _mesa_CompressedTexImage1DARB(GLenum target, GLint level,
                               const GLvoid *data)
 {
    GET_CURRENT_CONTEXT(ctx);
-   compressedteximage(ctx, 1, target, level, internalFormat,
-                      width, 1, 1, border, imageSize, data);
+   teximage(ctx, GL_TRUE, 1, target, level, internalFormat,
+            width, 1, 1, border, GL_NONE, GL_NONE, imageSize, data);
 }
 
 
@@ -3731,8 +3685,8 @@ _mesa_CompressedTexImage2DARB(GLenum target, GLint level,
                               const GLvoid *data)
 {
    GET_CURRENT_CONTEXT(ctx);
-   compressedteximage(ctx, 2, target, level, internalFormat,
-                      width, height, 1, border, imageSize, data);
+   teximage(ctx, GL_TRUE, 2, target, level, internalFormat,
+            width, height, 1, border, GL_NONE, GL_NONE, imageSize, data);
 }
 
 
@@ -3743,8 +3697,8 @@ _mesa_CompressedTexImage3DARB(GLenum target, GLint level,
                               GLsizei imageSize, const GLvoid *data)
 {
    GET_CURRENT_CONTEXT(ctx);
-   compressedteximage(ctx, 3, target, level, internalFormat,
-                      width, height, depth, border, imageSize, data);
+   teximage(ctx, GL_TRUE, 3, target, level, internalFormat,
+            width, height, depth, border, GL_NONE, GL_NONE, imageSize, data);
 }
 
 
@@ -4022,7 +3976,8 @@ _mesa_TexBuffer(GLenum target, GLenum internalFormat, GLuint buffer)
    GET_CURRENT_CONTEXT(ctx);
    ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
-   if (!ctx->Extensions.ARB_texture_buffer_object) {
+   if (!(ctx->Extensions.ARB_texture_buffer_object
+         && _mesa_is_desktop_gl(ctx))) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glTexBuffer");
       return;
    }