mesa: implement TexImage*Multisample
[mesa.git] / src / mesa / main / teximage.c
index 1124b583650bc8acf70e9a1b25a40b727f4ca99f..0dcf88ae424e3bc07c886ee16142d69d1921ea2d 100644 (file)
@@ -190,7 +190,9 @@ _mesa_base_tex_format( struct gl_context *ctx, GLint internalFormat )
       }
    }
 
-   if (ctx->Extensions.EXT_texture_compression_s3tc) {
+   /* Assume that the ANGLE flag will always be set if the EXT flag is set.
+    */
+   if (ctx->Extensions.ANGLE_texture_compression_dxt) {
       switch (internalFormat) {
          case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
             return GL_RGB;
@@ -203,19 +205,8 @@ _mesa_base_tex_format( struct gl_context *ctx, GLint internalFormat )
       }
    }
 
-   /* GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE && GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE */
-   if (ctx->API == API_OPENGLES2 &&
-       ctx->Extensions.ANGLE_texture_compression_dxt) {
-      switch (internalFormat) {
-         case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
-         case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
-            return GL_RGBA;
-         default:
-            ; /* fallthrough */
-      }
-   }
-
-   if (ctx->Extensions.S3_s3tc) {
+   if (_mesa_is_desktop_gl(ctx)
+       && ctx->Extensions.ANGLE_texture_compression_dxt) {
       switch (internalFormat) {
          case GL_RGB_S3TC:
          case GL_RGB4_S3TC:
@@ -670,7 +661,7 @@ _mesa_is_proxy_texture(GLenum target)
     * NUM_TEXTURE_TARGETS should match number of terms below, except there's no
     * proxy for GL_TEXTURE_BUFFER and GL_TEXTURE_EXTERNAL_OES.
     */
-   assert(NUM_TEXTURE_TARGETS == 8 + 2);
+   assert(NUM_TEXTURE_TARGETS == 10 + 2);
 
    return (target == GL_PROXY_TEXTURE_1D ||
            target == GL_PROXY_TEXTURE_2D ||
@@ -679,7 +670,9 @@ _mesa_is_proxy_texture(GLenum target)
            target == GL_PROXY_TEXTURE_RECTANGLE_NV ||
            target == GL_PROXY_TEXTURE_1D_ARRAY_EXT ||
            target == GL_PROXY_TEXTURE_2D_ARRAY_EXT ||
-           target == GL_PROXY_TEXTURE_CUBE_MAP_ARRAY);
+           target == GL_PROXY_TEXTURE_CUBE_MAP_ARRAY ||
+           target == GL_PROXY_TEXTURE_2D_MULTISAMPLE ||
+           target == GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY);
 }
 
 
@@ -720,6 +713,12 @@ _mesa_get_proxy_target(GLenum target)
    case GL_TEXTURE_CUBE_MAP_ARRAY:
    case GL_PROXY_TEXTURE_CUBE_MAP_ARRAY:
       return GL_PROXY_TEXTURE_CUBE_MAP_ARRAY;
+   case GL_TEXTURE_2D_MULTISAMPLE:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE:
+      return GL_PROXY_TEXTURE_2D_MULTISAMPLE;
+   case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY:
+      return GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY;
    default:
       _mesa_problem(NULL, "unexpected target in _mesa_get_proxy_target()");
       return 0;
@@ -797,6 +796,18 @@ _mesa_select_tex_object(struct gl_context *ctx,
       case GL_TEXTURE_EXTERNAL_OES:
          return 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_select_tex_object()");
          return NULL;
@@ -927,6 +938,16 @@ get_proxy_tex_image(struct gl_context *ctx, GLenum target, GLint level)
          return NULL;
       texIndex = TEXTURE_CUBE_ARRAY_INDEX;
       break;
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE:
+      if (level > 0)
+         return 0;
+      texIndex = TEXTURE_2D_MULTISAMPLE_INDEX;
+      break;
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY:
+      if (level > 0)
+         return 0;
+      texIndex = TEXTURE_2D_MULTISAMPLE_ARRAY_INDEX;
+      break;
    default:
       return NULL;
    }
@@ -996,6 +1017,13 @@ _mesa_max_texture_levels(struct gl_context *ctx, GLenum target)
    case GL_TEXTURE_BUFFER:
       return ctx->API == API_OPENGL_CORE &&
              ctx->Extensions.ARB_texture_buffer_object ? 1 : 0;
+   case GL_TEXTURE_2D_MULTISAMPLE:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE:
+   case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY:
+      return _mesa_is_desktop_gl(ctx)
+         && ctx->Extensions.ARB_texture_multisample
+         ? 1 : 0;
    case GL_TEXTURE_EXTERNAL_OES:
       /* fall-through */
    default:
@@ -1029,6 +1057,8 @@ _mesa_get_texture_dimensions(GLenum target)
    case GL_TEXTURE_1D_ARRAY:
    case GL_PROXY_TEXTURE_1D_ARRAY:
    case GL_TEXTURE_EXTERNAL_OES:
+   case GL_TEXTURE_2D_MULTISAMPLE:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE:
       return 2;
    case GL_TEXTURE_3D:
    case GL_PROXY_TEXTURE_3D:
@@ -1036,6 +1066,8 @@ _mesa_get_texture_dimensions(GLenum target)
    case GL_PROXY_TEXTURE_2D_ARRAY:
    case GL_TEXTURE_CUBE_MAP_ARRAY:
    case GL_PROXY_TEXTURE_CUBE_MAP_ARRAY:
+   case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY:
       return 3;
    case GL_TEXTURE_BUFFER:
       /* fall-through */
@@ -1077,6 +1109,8 @@ _mesa_get_tex_max_num_levels(GLenum target, GLsizei width, GLsizei height,
       break;
    case GL_TEXTURE_RECTANGLE:
    case GL_TEXTURE_EXTERNAL_OES:
+   case GL_TEXTURE_2D_MULTISAMPLE:
+   case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
       return 1;
    default:
       assert(0);
@@ -1162,6 +1196,8 @@ clear_teximage_fields(struct gl_texture_image *img)
    img->HeightLog2 = 0;
    img->DepthLog2 = 0;
    img->TexFormat = MESA_FORMAT_NONE;
+   img->NumSamples = 0;
+   img->FixedSampleLocations = GL_TRUE;
 }
 
 
@@ -1205,6 +1241,9 @@ _mesa_init_teximage_fields(struct gl_context *ctx,
    img->Width2 = width - 2 * border;   /* == 1 << img->WidthLog2; */
    img->WidthLog2 = _mesa_logbase2(img->Width2);
 
+   img->NumSamples = 0;
+   img->FixedSampleLocations = GL_TRUE;
+
    switch(target) {
    case GL_TEXTURE_1D:
    case GL_TEXTURE_BUFFER:
@@ -1243,6 +1282,8 @@ _mesa_init_teximage_fields(struct gl_context *ctx,
    case GL_PROXY_TEXTURE_2D:
    case GL_PROXY_TEXTURE_RECTANGLE:
    case GL_PROXY_TEXTURE_CUBE_MAP:
+   case GL_TEXTURE_2D_MULTISAMPLE:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE:
       img->Height2 = height - 2 * border; /* == 1 << img->HeightLog2; */
       img->HeightLog2 = _mesa_logbase2(img->Height2);
       if (depth == 0)
@@ -1255,6 +1296,8 @@ _mesa_init_teximage_fields(struct gl_context *ctx,
    case GL_PROXY_TEXTURE_2D_ARRAY:
    case GL_TEXTURE_CUBE_MAP_ARRAY:
    case GL_PROXY_TEXTURE_CUBE_MAP_ARRAY:
+   case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY:
       img->Height2 = height - 2 * border; /* == 1 << img->HeightLog2; */
       img->HeightLog2 = _mesa_logbase2(img->Height2);
       img->Depth2 = depth; /* no border */
@@ -1326,6 +1369,8 @@ _mesa_legal_texture_dimensions(struct gl_context *ctx, GLenum target,
 
    case GL_TEXTURE_2D:
    case GL_PROXY_TEXTURE_2D:
+   case GL_TEXTURE_2D_MULTISAMPLE:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE:
       maxSize = 1 << (ctx->Const.MaxTextureLevels - 1);
       maxSize >>= level;
       if (width < 2 * border || width > 2 * border + maxSize)
@@ -1408,6 +1453,8 @@ _mesa_legal_texture_dimensions(struct gl_context *ctx, GLenum target,
 
    case GL_TEXTURE_2D_ARRAY_EXT:
    case GL_PROXY_TEXTURE_2D_ARRAY_EXT:
+   case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY:
       maxSize = 1 << (ctx->Const.MaxTextureLevels - 1);
       maxSize >>= level;
       if (width < 2 * border || width > 2 * border + maxSize)
@@ -2348,6 +2395,7 @@ copytexture_error_check( struct gl_context *ctx, GLuint dimensions,
                          GLint width, GLint height, GLint border )
 {
    GLint baseFormat;
+   GLint rb_base_format;
    struct gl_renderbuffer *rb;
    GLenum rb_internal_format;
 
@@ -2421,16 +2469,48 @@ copytexture_error_check( struct gl_context *ctx, GLuint dimensions,
 
    baseFormat = _mesa_base_tex_format(ctx, internalFormat);
    if (baseFormat < 0) {
-      _mesa_error(ctx, GL_INVALID_VALUE,
+      _mesa_error(ctx, GL_INVALID_OPERATION,
                   "glCopyTexImage%dD(internalFormat)", dimensions);
       return GL_TRUE;
    }
 
    rb_internal_format = rb->InternalFormat;
+   rb_base_format = _mesa_base_tex_format(ctx, rb->InternalFormat);
+   if (_mesa_is_color_format(internalFormat)) {
+      if (rb_base_format < 0) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "glCopyTexImage%dD(internalFormat)", dimensions);
+         return GL_TRUE;
+      }
+   }
 
-   if ((_mesa_is_desktop_gl(ctx) &&
-        ctx->Extensions.ARB_framebuffer_object) ||
-       _mesa_is_gles3(ctx)) {
+   if (_mesa_is_gles(ctx)) {
+      bool valid = true;
+      if (_mesa_base_format_component_count(baseFormat) >
+          _mesa_base_format_component_count(rb_base_format)) {
+         valid = false;
+      }
+      if (baseFormat == GL_DEPTH_COMPONENT ||
+          baseFormat == GL_DEPTH_STENCIL ||
+          rb_base_format == GL_DEPTH_COMPONENT ||
+          rb_base_format == GL_DEPTH_STENCIL ||
+          ((baseFormat == GL_LUMINANCE_ALPHA ||
+            baseFormat == GL_ALPHA) &&
+           rb_base_format != GL_RGBA) ||
+          internalFormat == GL_RGB9_E5) {
+         valid = false;
+      }
+      if (internalFormat == GL_RGB9_E5) {
+         valid = false;
+      }
+      if (!valid) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "glCopyTexImage%dD(internalFormat)", dimensions);
+         return GL_TRUE;
+      }
+   }
+
+   if (_mesa_is_gles3(ctx)) {
       bool rb_is_srgb = false;
       bool dst_is_srgb = false;
 
@@ -2444,22 +2524,16 @@ copytexture_error_check( struct gl_context *ctx, GLuint dimensions,
       }
 
       if (rb_is_srgb != dst_is_srgb) {
-         /* Page 190 (page 211 of the PDF) in section 8.6 of the OpenGL 4.3
-          * Core Profile spec says:
-          *
-          *     "An INVALID_OPERATION error is generated under any of the
-          *     following conditions:
-          *
-          *     ...
+         /* Page 137 (page 149 of the PDF) in section 3.8.5 of the
+          * OpenGLES 3.0.0 spec says:
           *
-          *     - if the value of FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
-          *       for the framebuffer attachment corresponding to the read
-          *       buffer is LINEAR (see section 9.2.3) and internalformat
-          *       is one of the sRGB formats in table 8.23
-          *     - if the value of FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
-          *       for the framebuffer attachment corresponding to the read
-          *       buffer is SRGB and internalformat is not one of the sRGB
-          *       formats. in table 8.23."
+          *     "The error INVALID_OPERATION is also generated if the
+          *     value of FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING for the
+          *     framebuffer attachment corresponding to the read buffer
+          *     is LINEAR (see section 6.1.13) and internalformat is
+          *     one of the sRGB formats described in section 3.8.16, or
+          *     if the value of FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is
+          *     SRGB and internalformat is not one of the sRGB formats."
           */
          _mesa_error(ctx, GL_INVALID_OPERATION,
                      "glCopyTexImage%dD(srgb usage mismatch)", dimensions);
@@ -2909,7 +2983,7 @@ teximage(struct gl_context *ctx, GLboolean compressed, GLuint dims,
    gl_format texFormat;
    GLboolean dimensionsOK, sizeOK;
 
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+   FLUSH_VERTICES(ctx, 0);
 
    if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE)) {
       if (compressed)
@@ -2978,8 +3052,18 @@ teximage(struct gl_context *ctx, GLboolean compressed, GLuint dims,
    texObj = _mesa_get_current_tex_object(ctx, target);
    assert(texObj);
 
-   texFormat = _mesa_choose_texture_format(ctx, texObj, target, level,
-                                           internalFormat, format, type);
+   if (compressed) {
+      /* For glCompressedTexImage() the driver has no choice about the
+       * texture format since we'll never transcode the user's compressed
+       * image data.  The internalFormat was error checked earlier.
+       */
+      texFormat = _mesa_glenum_to_compressed_format(internalFormat);
+   }
+   else {
+      texFormat = _mesa_choose_texture_format(ctx, texObj, target, level,
+                                              internalFormat, format, type);
+   }
+
    assert(texFormat != MESA_FORMAT_NONE);
 
    /* check that width, height, depth are legal for the mipmap level */
@@ -3139,7 +3223,7 @@ _mesa_EGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image)
    struct gl_texture_image *texImage;
    bool valid_target;
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+   FLUSH_VERTICES(ctx, 0);
 
    switch (target) {
    case GL_TEXTURE_2D:
@@ -3159,6 +3243,12 @@ _mesa_EGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image)
       return;
    }
 
+   if (!image) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                 "glEGLImageTargetTexture2D(image=%p)", image);
+      return;
+   }
+
    if (ctx->NewState & _NEW_PIXEL)
       _mesa_update_state(ctx);
 
@@ -3201,7 +3291,7 @@ texsubimage(struct gl_context *ctx, GLuint dims, GLenum target, GLint level,
    struct gl_texture_object *texObj;
    struct gl_texture_image *texImage;
 
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+   FLUSH_VERTICES(ctx, 0);
 
    if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
       _mesa_debug(ctx, "glTexSubImage%uD %s %d %d %d %d %d %d %d %s %s %p\n",
@@ -3340,7 +3430,7 @@ copyteximage(struct gl_context *ctx, GLuint dims,
    const GLuint face = _mesa_tex_target_to_face(target);
    gl_format texFormat;
 
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+   FLUSH_VERTICES(ctx, 0);
 
    if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
       _mesa_debug(ctx, "glCopyTexImage%uD %s %d %s %d %d %d %d %d\n",
@@ -3463,7 +3553,7 @@ copytexsubimage(struct gl_context *ctx, GLuint dims, GLenum target, GLint level,
    struct gl_texture_object *texObj;
    struct gl_texture_image *texImage;
 
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+   FLUSH_VERTICES(ctx, 0);
 
    if (MESA_VERBOSE & (VERBOSE_API|VERBOSE_TEXTURE))
       _mesa_debug(ctx, "glCopyTexSubImage%uD %s %d %d %d %d %d %d %d %d\n",
@@ -3711,7 +3801,7 @@ compressed_tex_sub_image(GLuint dims, GLenum target, GLint level,
    struct gl_texture_object *texObj;
    struct gl_texture_image *texImage;
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+   FLUSH_VERTICES(ctx, 0);
 
    if (compressed_subtexture_error_check(ctx, dims, target, level,
                                          xoffset, yoffset, zoffset,
@@ -3881,7 +3971,7 @@ get_texbuffer_format(const struct gl_context *ctx, GLenum internalFormat)
    case GL_RG8:
       return MESA_FORMAT_GR88;
    case GL_RG16:
-      return MESA_FORMAT_RG1616;
+      return MESA_FORMAT_GR1616;
    case GL_RG16F:
       return MESA_FORMAT_RG_FLOAT16;
    case GL_RG32F:
@@ -3968,48 +4058,283 @@ validate_texbuffer_format(const struct gl_context *ctx, GLenum internalFormat)
 }
 
 
+static void
+texbufferrange(struct gl_context *ctx, GLenum target, GLenum internalFormat,
+               struct gl_buffer_object *bufObj,
+               GLintptr offset, GLsizeiptr size)
+{
+   struct gl_texture_object *texObj;
+   gl_format format;
+
+   FLUSH_VERTICES(ctx, 0);
+
+   if (target != GL_TEXTURE_BUFFER_ARB) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glTexBuffer(target)");
+      return;
+   }
+
+   format = validate_texbuffer_format(ctx, internalFormat);
+   if (format == MESA_FORMAT_NONE) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glTexBuffer(internalFormat 0x%x)",
+                  internalFormat);
+      return;
+   }
+
+   texObj = _mesa_get_current_tex_object(ctx, target);
+
+   _mesa_lock_texture(ctx, texObj);
+   {
+      _mesa_reference_buffer_object(ctx, &texObj->BufferObject, bufObj);
+      texObj->BufferObjectFormat = internalFormat;
+      texObj->_BufferObjectFormat = format;
+      texObj->BufferOffset = offset;
+      texObj->BufferSize = size;
+   }
+   _mesa_unlock_texture(ctx, texObj);
+}
+
 /** GL_ARB_texture_buffer_object */
 void GLAPIENTRY
 _mesa_TexBuffer(GLenum target, GLenum internalFormat, GLuint buffer)
 {
-   struct gl_texture_object *texObj;
    struct gl_buffer_object *bufObj;
-   gl_format format;
 
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
+   /* NOTE: ARB_texture_buffer_object has interactions with
+    * the compatibility profile that are not implemented.
+    */
    if (!(ctx->API == API_OPENGL_CORE &&
          ctx->Extensions.ARB_texture_buffer_object)) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glTexBuffer");
       return;
    }
 
-   if (target != GL_TEXTURE_BUFFER_ARB) {
-      _mesa_error(ctx, GL_INVALID_ENUM, "glTexBuffer(target)");
+   bufObj = _mesa_lookup_bufferobj(ctx, buffer);
+   if (!bufObj && buffer) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glTexBuffer(buffer %u)", buffer);
       return;
    }
 
-   format = validate_texbuffer_format(ctx, internalFormat);
-   if (format == MESA_FORMAT_NONE) {
-      _mesa_error(ctx, GL_INVALID_ENUM, "glTexBuffer(internalFormat 0x%x)",
-                  internalFormat);
+   texbufferrange(ctx, target, internalFormat, bufObj, 0, buffer ? -1 : 0);
+}
+
+/** GL_ARB_texture_buffer_range */
+void GLAPIENTRY
+_mesa_TexBufferRange(GLenum target, GLenum internalFormat, GLuint buffer,
+                     GLintptr offset, GLsizeiptr size)
+{
+   struct gl_buffer_object *bufObj;
+
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (!(ctx->API == API_OPENGL_CORE &&
+         ctx->Extensions.ARB_texture_buffer_range)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glTexBufferRange");
       return;
    }
 
    bufObj = _mesa_lookup_bufferobj(ctx, buffer);
-   if (buffer && !bufObj) {
-      _mesa_error(ctx, GL_INVALID_OPERATION, "glTexBuffer(buffer %u)", buffer);
+   if (bufObj) {
+      if (offset < 0 ||
+          size <= 0 ||
+          (offset + size) > bufObj->Size) {
+         _mesa_error(ctx, GL_INVALID_VALUE, "glTexBufferRange");
+         return;
+      }
+      if (offset % ctx->Const.TextureBufferOffsetAlignment) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "glTexBufferRange(invalid offset alignment)");
+         return;
+      }
+   } else if (buffer) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glTexBufferRange(buffer %u)",
+                  buffer);
       return;
+   } else {
+      offset = 0;
+      size = 0;
+   }
+
+   texbufferrange(ctx, target, internalFormat, bufObj, offset, size);
+}
+
+static GLboolean
+is_renderable_texture_format(struct gl_context *ctx, GLenum internalformat)
+{
+   /* Everything that is allowed for renderbuffers,
+    * except for a base format of GL_STENCIL_INDEX.
+    */
+   GLenum baseFormat = _mesa_base_fbo_format(ctx, internalformat);
+   return baseFormat != 0 && baseFormat != GL_STENCIL_INDEX;
+}
+
+/** GL_ARB_texture_multisample */
+static GLboolean
+check_multisample_target(GLuint dims, GLenum target)
+{
+   switch(target) {
+   case GL_TEXTURE_2D_MULTISAMPLE:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE:
+      return dims == 2;
+
+   case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY:
+      return dims == 3;
+
+   default:
+      return GL_FALSE;
+   }
+}
+
+static void
+teximagemultisample(GLuint dims, GLenum target, GLsizei samples,
+                    GLint internalformat, GLsizei width, GLsizei height,
+                    GLsizei depth, GLboolean fixedsamplelocations)
+{
+   struct gl_texture_object *texObj;
+   struct gl_texture_image *texImage;
+   GLboolean sizeOK, dimensionsOK;
+   gl_format texFormat;
+
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (!(ctx->Extensions.ARB_texture_multisample
+      && _mesa_is_desktop_gl(ctx))) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glTexImage%uDMultisample", dims);
+      return;
+   }
+
+   if (!check_multisample_target(dims, target)) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glTexImage%uDMultisample(target)", dims);
+      return;
+   }
+
+   /* check that the specified internalformat is color/depth/stencil-renderable;
+    * refer GL3.1 spec 4.4.4
+    */
+
+   if (!is_renderable_texture_format(ctx, internalformat)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+            "glTexImage%uDMultisample(internalformat=%s)",
+            dims,
+            _mesa_lookup_enum_by_nr(internalformat));
+      return;
+   }
+
+   if (_mesa_is_enum_format_integer(internalformat)) {
+      if (samples > ctx->Const.MaxIntegerSamples) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glTexImage%uDMultisample(samples>GL_MAX_INTEGER_SAMPLES)",
+               dims);
+         return;
+      }
+   }
+   else if (_mesa_is_depth_or_stencil_format(internalformat)) {
+      if (samples > ctx->Const.MaxDepthTextureSamples) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glTexImage%uDMultisample(samples>GL_MAX_DEPTH_TEXTURE_SAMPLES)",
+               dims);
+         return;
+      }
+   }
+   else {
+      if (samples > ctx->Const.MaxColorTextureSamples) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+               "glTexImage%uDMultisample(samples>GL_MAX_COLOR_TEXTURE_SAMPLES)",
+               dims);
+         return;
+      }
    }
 
+   /* TODO: should ask the driver for the exact limit for this internalformat
+    * once IDR's internalformat_query bits land
+    */
+
    texObj = _mesa_get_current_tex_object(ctx, target);
+   texImage = _mesa_get_tex_image(ctx, texObj, 0, 0);
 
-   _mesa_lock_texture(ctx, texObj);
-   {
-      _mesa_reference_buffer_object(ctx, &texObj->BufferObject, bufObj);
-      texObj->BufferObjectFormat = internalFormat;
-      texObj->_BufferObjectFormat = format;
+   if (texImage == NULL) {
+      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage%uDMultisample()", dims);
+      return;
    }
-   _mesa_unlock_texture(ctx, texObj);
+
+   texFormat = _mesa_choose_texture_format(ctx, texObj, target, 0,
+         internalformat, GL_NONE, GL_NONE);
+   assert(texFormat != MESA_FORMAT_NONE);
+
+   dimensionsOK = _mesa_legal_texture_dimensions(ctx, target, 0,
+         width, height, depth, 0);
+
+   sizeOK = ctx->Driver.TestProxyTexImage(ctx, target, 0, texFormat,
+         width, height, depth, 0);
+
+   if (_mesa_is_proxy_texture(target)) {
+      if (dimensionsOK && sizeOK) {
+         _mesa_init_teximage_fields(ctx, texImage,
+               width, height, depth, 0, internalformat, texFormat);
+         texImage->NumSamples = samples;
+         texImage->FixedSampleLocations = fixedsamplelocations;
+      }
+      else {
+         /* clear all image fields */
+         _mesa_init_teximage_fields(ctx, texImage,
+               0, 0, 0, 0, GL_NONE, MESA_FORMAT_NONE);
+      }
+   }
+   else {
+      if (!dimensionsOK) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+               "glTexImage%uDMultisample(invalid width or height)", dims);
+         return;
+      }
+
+      if (!sizeOK) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY,
+               "glTexImage%uDMultisample(texture too large)", dims);
+         return;
+      }
+
+      ctx->Driver.FreeTextureImageBuffer(ctx, texImage);
+
+      _mesa_init_teximage_fields(ctx, texImage,
+            width, height, depth, 0, internalformat, texFormat);
+
+      texImage->NumSamples = samples;
+      texImage->FixedSampleLocations = fixedsamplelocations;
+
+      if (width > 0 && height > 0 && depth > 0) {
+
+         if (!ctx->Driver.AllocTextureStorage(ctx, texObj, 1,
+                  width, height, depth)) {
+            /* tidy up the texture image state. strictly speaking,
+             * we're allowed to just leave this in whatever state we
+             * like, but being tidy is good.
+             */
+            _mesa_init_teximage_fields(ctx, texImage,
+                  0, 0, 0, 0, GL_NONE, MESA_FORMAT_NONE);
+         }
+      }
+
+      _mesa_update_fbo_texture(ctx, texObj, 0, 0);
+   }
+}
+
+void GLAPIENTRY
+_mesa_TexImage2DMultisample(GLenum target, GLsizei samples,
+                            GLint internalformat, GLsizei width,
+                            GLsizei height, GLboolean fixedsamplelocations)
+{
+   teximagemultisample(2, target, samples, internalformat,
+         width, height, 1, fixedsamplelocations);
+}
+
+void GLAPIENTRY
+_mesa_TexImage3DMultisample(GLenum target, GLsizei samples,
+                            GLint internalformat, GLsizei width,
+                            GLsizei height, GLsizei depth,
+                            GLboolean fixedsamplelocations)
+{
+   teximagemultisample(3, target, samples, internalformat,
+         width, height, depth, fixedsamplelocations);
 }