meta: Use sampler object in framebuffer blit
[mesa.git] / src / mesa / drivers / common / meta.c
index be1953ab2f9524564904418909bc2405b244d385..af89d8180d1c1d869be49b67518c8828ad5c1d77 100644 (file)
 #include "main/depth.h"
 #include "main/enable.h"
 #include "main/fbobject.h"
+#include "main/feedback.h"
 #include "main/formats.h"
+#include "main/glformats.h"
 #include "main/image.h"
 #include "main/macros.h"
 #include "main/matrix.h"
 #include "main/mipmap.h"
+#include "main/pixel.h"
 #include "main/pbo.h"
 #include "main/polygon.h"
 #include "main/readpix.h"
@@ -63,6 +66,7 @@
 #include "main/teximage.h"
 #include "main/texparam.h"
 #include "main/texstate.h"
+#include "main/transformfeedback.h"
 #include "main/uniforms.h"
 #include "main/varray.h"
 #include "main/viewport.h"
@@ -171,11 +175,24 @@ struct save_state
    struct gl_query_object *CondRenderQuery;
    GLenum CondRenderMode;
 
+#if FEATURE_feedback
+   /** MESA_META_SELECT_FEEDBACK */
+   GLenum RenderMode;
+   struct gl_selection Select;
+   struct gl_feedback Feedback;
+#endif
+
+   /** MESA_META_MULTISAMPLE */
+   GLboolean MultisampleEnabled;
+
    /** Miscellaneous (always disabled) */
    GLboolean Lighting;
+   GLboolean RasterDiscard;
+#if FEATURE_EXT_transform_feedback
+   GLboolean TransformFeedbackNeedsResume;
+#endif
 };
 
-
 /**
  * Temporary texture used for glBlitFramebuffer, glDrawPixels, etc.
  * This is currently shared by all the meta ops.  But we could create a
@@ -214,6 +231,9 @@ struct clear_state
    GLuint VBO;
    GLuint ShaderProg;
    GLint ColorLocation;
+
+   GLuint IntegerShaderProg;
+   GLint IntegerColorLocation;
 };
 
 
@@ -267,12 +287,20 @@ struct gen_mipmap_state
 struct decompress_state
 {
    GLuint ArrayObj;
-   GLuint VBO, FBO, RBO;
+   GLuint VBO, FBO, RBO, Sampler;
    GLint Width, Height;
 };
 
+/**
+ * State for glDrawTex()
+ */
+struct drawtex_state
+{
+   GLuint ArrayObj;
+   GLuint VBO;
+};
 
-#define MAX_META_OPS_DEPTH      2
+#define MAX_META_OPS_DEPTH      8
 /**
  * All per-context meta state.
  */
@@ -292,8 +320,79 @@ struct gl_meta_state
    struct bitmap_state Bitmap;    /**< For _mesa_meta_Bitmap() */
    struct gen_mipmap_state Mipmap;    /**< For _mesa_meta_GenerateMipmap() */
    struct decompress_state Decompress;  /**< For texture decompression */
+   struct drawtex_state DrawTex;  /**< For _mesa_meta_DrawTex() */
 };
 
+static void meta_glsl_blit_cleanup(struct gl_context *ctx, struct blit_state *blit);
+static void cleanup_temp_texture(struct gl_context *ctx, struct temp_texture *tex);
+static void meta_glsl_clear_cleanup(struct gl_context *ctx, struct clear_state *clear);
+
+static GLuint
+compile_shader_with_debug(struct gl_context *ctx, GLenum target, const GLcharARB *source)
+{
+   GLuint shader;
+   GLint ok, size;
+   GLchar *info;
+
+   shader = _mesa_CreateShaderObjectARB(target);
+   _mesa_ShaderSourceARB(shader, 1, &source, NULL);
+   _mesa_CompileShaderARB(shader);
+
+   _mesa_GetShaderiv(shader, GL_COMPILE_STATUS, &ok);
+   if (ok)
+      return shader;
+
+   _mesa_GetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
+   if (size == 0) {
+      _mesa_DeleteObjectARB(shader);
+      return 0;
+   }
+
+   info = malloc(size);
+   if (!info) {
+      _mesa_DeleteObjectARB(shader);
+      return 0;
+   }
+
+   _mesa_GetProgramInfoLog(shader, size, NULL, info);
+   _mesa_problem(ctx,
+                "meta program compile failed:\n%s\n"
+                "source:\n%s\n",
+                info, source);
+
+   free(info);
+   _mesa_DeleteObjectARB(shader);
+
+   return 0;
+}
+
+static GLuint
+link_program_with_debug(struct gl_context *ctx, GLuint program)
+{
+   GLint ok, size;
+   GLchar *info;
+
+   _mesa_LinkProgramARB(program);
+
+   _mesa_GetProgramiv(program, GL_LINK_STATUS, &ok);
+   if (ok)
+      return program;
+
+   _mesa_GetProgramiv(program, GL_INFO_LOG_LENGTH, &size);
+   if (size == 0)
+      return 0;
+
+   info = malloc(size);
+   if (!info)
+      return 0;
+
+   _mesa_GetProgramInfoLog(program, size, NULL, info);
+   _mesa_problem(ctx, "meta program link failed:\n%s", info);
+
+   free(info);
+
+   return 0;
+}
 
 /**
  * Initialize meta-ops for a context.
@@ -315,10 +414,15 @@ _mesa_meta_init(struct gl_context *ctx)
 void
 _mesa_meta_free(struct gl_context *ctx)
 {
-   /* Note: Any textures, VBOs, etc, that we allocate should get
-    * freed by the normal context destruction code.  But this would be
-    * the place to free other meta data someday.
-    */
+   GET_CURRENT_CONTEXT(old_context);
+   _mesa_make_current(ctx, NULL, NULL);
+   meta_glsl_blit_cleanup(ctx, &ctx->Meta->Blit);
+   meta_glsl_clear_cleanup(ctx, &ctx->Meta->Clear);
+   cleanup_temp_texture(ctx, &ctx->Meta->TempTex);
+   if (old_context)
+      _mesa_make_current(old_context, old_context->WinSysDrawBuffer, old_context->WinSysReadBuffer);
+   else
+      _mesa_make_current(NULL, NULL, NULL);
    free(ctx->Meta);
    ctx->Meta = NULL;
 }
@@ -343,6 +447,17 @@ _mesa_meta_begin(struct gl_context *ctx, GLbitfield state)
    memset(save, 0, sizeof(*save));
    save->SavedState = state;
 
+#if FEATURE_EXT_transform_feedback
+   /* Pausing transform feedback needs to be done early, or else we won't be
+    * able to change other state.
+    */
+   save->TransformFeedbackNeedsResume =
+      ctx->TransformFeedback.CurrentObject->Active &&
+      !ctx->TransformFeedback.CurrentObject->Paused;
+   if (save->TransformFeedbackNeedsResume)
+      _mesa_PauseTransformFeedback();
+#endif
+
    if (state & MESA_META_ALPHA_TEST) {
       save->AlphaEnabled = ctx->Color.AlphaEnabled;
       save->AlphaFunc = ctx->Color.AlphaFunc;
@@ -463,7 +578,7 @@ _mesa_meta_begin(struct gl_context *ctx, GLbitfield state)
         _mesa_reference_shader_program(ctx, &save->FragmentShader,
                                        ctx->Shader.CurrentFragmentProgram);
         _mesa_reference_shader_program(ctx, &save->ActiveShader,
-                                       ctx->Shader.CurrentFragmentProgram);
+                                       ctx->Shader.ActiveProgram);
 
          _mesa_UseProgramObjectARB(0);
       }
@@ -495,7 +610,10 @@ _mesa_meta_begin(struct gl_context *ctx, GLbitfield state)
             _mesa_set_enable(ctx, GL_TEXTURE_3D, GL_FALSE);
             if (ctx->Extensions.ARB_texture_cube_map)
                _mesa_set_enable(ctx, GL_TEXTURE_CUBE_MAP, GL_FALSE);
-            _mesa_set_enable(ctx, GL_TEXTURE_RECTANGLE, GL_FALSE);
+            if (ctx->Extensions.NV_texture_rectangle)
+               _mesa_set_enable(ctx, GL_TEXTURE_RECTANGLE, GL_FALSE);
+            if (ctx->Extensions.OES_EGL_image_external)
+               _mesa_set_enable(ctx, GL_TEXTURE_EXTERNAL_OES, GL_FALSE);
             _mesa_set_enable(ctx, GL_TEXTURE_GEN_S, GL_FALSE);
             _mesa_set_enable(ctx, GL_TEXTURE_GEN_T, GL_FALSE);
             _mesa_set_enable(ctx, GL_TEXTURE_GEN_R, GL_FALSE);
@@ -606,11 +724,33 @@ _mesa_meta_begin(struct gl_context *ctx, GLbitfield state)
         _mesa_EndConditionalRender();
    }
 
+#if FEATURE_feedback
+   if (state & MESA_META_SELECT_FEEDBACK) {
+      save->RenderMode = ctx->RenderMode;
+      if (ctx->RenderMode == GL_SELECT) {
+        save->Select = ctx->Select; /* struct copy */
+        _mesa_RenderMode(GL_RENDER);
+      } else if (ctx->RenderMode == GL_FEEDBACK) {
+        save->Feedback = ctx->Feedback; /* struct copy */
+        _mesa_RenderMode(GL_RENDER);
+      }
+   }
+#endif
+
+   if (state & MESA_META_MULTISAMPLE) {
+      save->MultisampleEnabled = ctx->Multisample.Enabled;
+      if (ctx->Multisample.Enabled)
+         _mesa_set_enable(ctx, GL_MULTISAMPLE, GL_FALSE);
+   }
+
    /* misc */
    {
       save->Lighting = ctx->Light.Enabled;
       if (ctx->Light.Enabled)
          _mesa_set_enable(ctx, GL_LIGHTING, GL_FALSE);
+      save->RasterDiscard = ctx->RasterDiscard;
+      if (ctx->RasterDiscard)
+         _mesa_set_enable(ctx, GL_RASTERIZER_DISCARD, GL_FALSE);
    }
 }
 
@@ -740,6 +880,11 @@ _mesa_meta_end(struct gl_context *ctx)
 
       _mesa_reference_shader_program(ctx, &ctx->Shader.ActiveProgram,
                                     save->ActiveShader);
+
+      _mesa_reference_shader_program(ctx, &save->VertexShader, NULL);
+      _mesa_reference_shader_program(ctx, &save->GeometryShader, NULL);
+      _mesa_reference_shader_program(ctx, &save->FragmentShader, NULL);
+      _mesa_reference_shader_program(ctx, &save->ActiveShader, NULL);
    }
 
    if (state & MESA_META_STENCIL_TEST) {
@@ -783,40 +928,25 @@ _mesa_meta_end(struct gl_context *ctx)
 
       /* restore texture objects for unit[0] only */
       for (tgt = 0; tgt < NUM_TEXTURE_TARGETS; tgt++) {
-         _mesa_reference_texobj(&ctx->Texture.Unit[0].CurrentTex[tgt],
-                                save->CurrentTexture[tgt]);
+        if (ctx->Texture.Unit[0].CurrentTex[tgt] != save->CurrentTexture[tgt]) {
+           FLUSH_VERTICES(ctx, _NEW_TEXTURE);
+           _mesa_reference_texobj(&ctx->Texture.Unit[0].CurrentTex[tgt],
+                                  save->CurrentTexture[tgt]);
+        }
          _mesa_reference_texobj(&save->CurrentTexture[tgt], NULL);
       }
 
-      /* Re-enable textures, texgen */
+      /* Restore fixed function texture enables, texgen */
       for (u = 0; u < ctx->Const.MaxTextureUnits; u++) {
-         if (save->TexEnabled[u]) {
-            _mesa_ActiveTextureARB(GL_TEXTURE0 + u);
-
-            if (save->TexEnabled[u] & TEXTURE_1D_BIT)
-               _mesa_set_enable(ctx, GL_TEXTURE_1D, GL_TRUE);
-            if (save->TexEnabled[u] & TEXTURE_2D_BIT)
-               _mesa_set_enable(ctx, GL_TEXTURE_2D, GL_TRUE);
-            if (save->TexEnabled[u] & TEXTURE_3D_BIT)
-               _mesa_set_enable(ctx, GL_TEXTURE_3D, GL_TRUE);
-            if (save->TexEnabled[u] & TEXTURE_CUBE_BIT)
-               _mesa_set_enable(ctx, GL_TEXTURE_CUBE_MAP, GL_TRUE);
-            if (save->TexEnabled[u] & TEXTURE_RECT_BIT)
-               _mesa_set_enable(ctx, GL_TEXTURE_RECTANGLE, GL_TRUE);
-         }
-
-         if (save->TexGenEnabled[u]) {
-            _mesa_ActiveTextureARB(GL_TEXTURE0 + u);
+        if (ctx->Texture.Unit[u].Enabled != save->TexEnabled[u]) {
+           FLUSH_VERTICES(ctx, _NEW_TEXTURE);
+           ctx->Texture.Unit[u].Enabled = save->TexEnabled[u];
+        }
 
-            if (save->TexGenEnabled[u] & S_BIT)
-               _mesa_set_enable(ctx, GL_TEXTURE_GEN_S, GL_TRUE);
-            if (save->TexGenEnabled[u] & T_BIT)
-               _mesa_set_enable(ctx, GL_TEXTURE_GEN_T, GL_TRUE);
-            if (save->TexGenEnabled[u] & R_BIT)
-               _mesa_set_enable(ctx, GL_TEXTURE_GEN_R, GL_TRUE);
-            if (save->TexGenEnabled[u] & Q_BIT)
-               _mesa_set_enable(ctx, GL_TEXTURE_GEN_Q, GL_TRUE);
-         }
+        if (ctx->Texture.Unit[u].TexGenEnabled != save->TexGenEnabled[u]) {
+           FLUSH_VERTICES(ctx, _NEW_TEXTURE);
+           ctx->Texture.Unit[u].TexGenEnabled = save->TexGenEnabled[u];
+        }
       }
 
       /* restore current unit state */
@@ -886,10 +1016,44 @@ _mesa_meta_end(struct gl_context *ctx)
                                      save->CondRenderMode);
    }
 
+#if FEATURE_feedback
+   if (state & MESA_META_SELECT_FEEDBACK) {
+      if (save->RenderMode == GL_SELECT) {
+        _mesa_RenderMode(GL_SELECT);
+        ctx->Select = save->Select;
+      } else if (save->RenderMode == GL_FEEDBACK) {
+        _mesa_RenderMode(GL_FEEDBACK);
+        ctx->Feedback = save->Feedback;
+      }
+   }
+#endif
+
+   if (state & MESA_META_MULTISAMPLE) {
+      if (ctx->Multisample.Enabled != save->MultisampleEnabled)
+         _mesa_set_enable(ctx, GL_MULTISAMPLE, save->MultisampleEnabled);
+   }
+
    /* misc */
    if (save->Lighting) {
       _mesa_set_enable(ctx, GL_LIGHTING, GL_TRUE);
    }
+   if (save->RasterDiscard) {
+      _mesa_set_enable(ctx, GL_RASTERIZER_DISCARD, GL_TRUE);
+   }
+#if FEATURE_EXT_transform_feedback
+   if (save->TransformFeedbackNeedsResume)
+      _mesa_ResumeTransformFeedback();
+#endif
+}
+
+
+/**
+ * Determine whether Mesa is currently in a meta state.
+ */
+GLboolean
+_mesa_meta_in_progress(struct gl_context *ctx)
+{
+   return ctx->Meta->SaveStackDepth != 0;
 }
 
 
@@ -903,7 +1067,7 @@ _mesa_meta_end(struct gl_context *ctx)
 static INLINE GLfloat
 invert_z(GLfloat normZ)
 {
-   GLfloat objZ = 1.0 - 2.0 * normZ;
+   GLfloat objZ = 1.0f - 2.0f * normZ;
    return objZ;
 }
 
@@ -933,6 +1097,15 @@ init_temp_texture(struct gl_context *ctx, struct temp_texture *tex)
    _mesa_GenTextures(1, &tex->TexObj);
 }
 
+static void
+cleanup_temp_texture(struct gl_context *ctx, struct temp_texture *tex)
+{
+   if (!tex->TexObj)
+     return;
+   _mesa_DeleteTextures(1, &tex->TexObj);
+   tex->TexObj = 0;
+}
+
 
 /**
  * Return pointer to temp_texture info for non-bitmap ops.
@@ -1177,15 +1350,13 @@ blitframebuffer_texture(struct gl_context *ctx,
       if (readAtt && readAtt->Texture) {
          const struct gl_texture_object *texObj = readAtt->Texture;
          const GLuint srcLevel = readAtt->TextureLevel;
-         const GLenum minFilterSave = texObj->Sampler.MinFilter;
-         const GLenum magFilterSave = texObj->Sampler.MagFilter;
          const GLint baseLevelSave = texObj->BaseLevel;
          const GLint maxLevelSave = texObj->MaxLevel;
-         const GLenum wrapSSave = texObj->Sampler.WrapS;
-         const GLenum wrapTSave = texObj->Sampler.WrapT;
-         const GLenum srgbSave = texObj->Sampler.sRGBDecode;
          const GLenum fbo_srgb_save = ctx->Color.sRGBEnabled;
          const GLenum target = texObj->Target;
+         GLuint sampler, samplerSave =
+            ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler ?
+            ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler->Name : 0;
 
          if (drawAtt->Texture == readAtt->Texture) {
             /* Can't use same texture as both the source and dest.  We need
@@ -1200,6 +1371,9 @@ blitframebuffer_texture(struct gl_context *ctx,
             return mask;
          }
 
+         _mesa_GenSamplers(1, &sampler);
+         _mesa_BindSampler(ctx->Texture.CurrentUnit, sampler);
+
          /*
          printf("Blit from texture!\n");
          printf("  srcAtt %p  dstAtt %p\n", readAtt, drawAtt);
@@ -1208,22 +1382,22 @@ blitframebuffer_texture(struct gl_context *ctx,
 
          /* Prepare src texture state */
          _mesa_BindTexture(target, texObj->Name);
-         _mesa_TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
-         _mesa_TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
+         _mesa_SamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, filter);
+         _mesa_SamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, filter);
          if (target != GL_TEXTURE_RECTANGLE_ARB) {
             _mesa_TexParameteri(target, GL_TEXTURE_BASE_LEVEL, srcLevel);
             _mesa_TexParameteri(target, GL_TEXTURE_MAX_LEVEL, srcLevel);
          }
-         _mesa_TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-         _mesa_TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+         _mesa_SamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+         _mesa_SamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
         /* Always do our blits with no sRGB decode or encode.*/
         if (ctx->Extensions.EXT_texture_sRGB_decode) {
-           _mesa_TexParameteri(target, GL_TEXTURE_SRGB_DECODE_EXT,
+           _mesa_SamplerParameteri(sampler, GL_TEXTURE_SRGB_DECODE_EXT,
                                GL_SKIP_DECODE_EXT);
         }
          if (ctx->Extensions.EXT_framebuffer_sRGB) {
-            _mesa_Disable(GL_FRAMEBUFFER_SRGB_EXT);
+            _mesa_set_enable(ctx, GL_FRAMEBUFFER_SRGB_EXT, GL_FALSE);
          }
 
          _mesa_TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
@@ -1279,21 +1453,17 @@ blitframebuffer_texture(struct gl_context *ctx,
          /* Restore texture object state, the texture binding will
           * be restored by _mesa_meta_end().
           */
-         _mesa_TexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilterSave);
-         _mesa_TexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilterSave);
          if (target != GL_TEXTURE_RECTANGLE_ARB) {
             _mesa_TexParameteri(target, GL_TEXTURE_BASE_LEVEL, baseLevelSave);
             _mesa_TexParameteri(target, GL_TEXTURE_MAX_LEVEL, maxLevelSave);
          }
-         _mesa_TexParameteri(target, GL_TEXTURE_WRAP_S, wrapSSave);
-         _mesa_TexParameteri(target, GL_TEXTURE_WRAP_T, wrapTSave);
-        if (ctx->Extensions.EXT_texture_sRGB_decode) {
-           _mesa_TexParameteri(target, GL_TEXTURE_SRGB_DECODE_EXT, srgbSave);
-        }
         if (ctx->Extensions.EXT_framebuffer_sRGB && fbo_srgb_save) {
-           _mesa_Enable(GL_FRAMEBUFFER_SRGB_EXT);
+           _mesa_set_enable(ctx, GL_FRAMEBUFFER_SRGB_EXT, GL_TRUE);
         }
 
+         _mesa_BindSampler(ctx->Texture.CurrentUnit, samplerSave);
+         _mesa_DeleteSamplers(1, &sampler);
+
          /* Done with color buffer */
          mask &= ~GL_COLOR_BUFFER_BIT;
       }
@@ -1328,7 +1498,12 @@ _mesa_meta_BlitFramebuffer(struct gl_context *ctx,
    struct vertex verts[4];
    GLboolean newTex;
 
-   if (srcW > maxTexSize || srcH > maxTexSize) {
+   /* In addition to falling back if the blit size is larger than the maximum
+    * texture size, fallback if the source is multisampled.  This fallback can
+    * be removed once Mesa gets support ARB_texture_multisample.
+    */
+   if (srcW > maxTexSize || srcH > maxTexSize
+       || ctx->ReadBuffer->Visual.samples > 0) {
       /* XXX avoid this fallback */
       _swrast_BlitFramebuffer(ctx, srcX0, srcY0, srcX1, srcY1,
                               dstX0, dstY0, dstX1, dstY1, mask, filter);
@@ -1464,6 +1639,21 @@ _mesa_meta_BlitFramebuffer(struct gl_context *ctx,
    }
 }
 
+static void
+meta_glsl_blit_cleanup(struct gl_context *ctx, struct blit_state *blit)
+{
+   if (blit->ArrayObj) {
+      _mesa_DeleteVertexArraysAPPLE(1, &blit->ArrayObj);
+      blit->ArrayObj = 0;
+      _mesa_DeleteBuffersARB(1, &blit->VBO);
+      blit->VBO = 0;
+   }
+   if (blit->DepthFP) {
+      _mesa_DeletePrograms(1, &blit->DepthFP);
+      blit->DepthFP = 0;
+   }
+}
+
 
 /**
  * Meta implementation of ctx->Driver.Clear() in terms of polygon rendering.
@@ -1603,6 +1793,22 @@ meta_glsl_clear_init(struct gl_context *ctx, struct clear_state *clear)
       "{\n"
       "   gl_FragColor = color;\n"
       "}\n";
+   const char *vs_int_source =
+      "#version 130\n"
+      "attribute vec4 position;\n"
+      "void main()\n"
+      "{\n"
+      "   gl_Position = position;\n"
+      "}\n";
+   const char *fs_int_source =
+      "#version 130\n"
+      "uniform ivec4 color;\n"
+      "out ivec4 out_color;\n"
+      "\n"
+      "void main()\n"
+      "{\n"
+      "   out_color = color;\n"
+      "}\n";
    GLuint vs, fs;
 
    if (clear->ArrayObj != 0)
@@ -1630,12 +1836,54 @@ meta_glsl_clear_init(struct gl_context *ctx, struct clear_state *clear)
 
    clear->ShaderProg = _mesa_CreateProgramObjectARB();
    _mesa_AttachShader(clear->ShaderProg, fs);
+   _mesa_DeleteObjectARB(fs);
    _mesa_AttachShader(clear->ShaderProg, vs);
+   _mesa_DeleteObjectARB(vs);
    _mesa_BindAttribLocationARB(clear->ShaderProg, 0, "position");
    _mesa_LinkProgramARB(clear->ShaderProg);
 
    clear->ColorLocation = _mesa_GetUniformLocationARB(clear->ShaderProg,
                                                      "color");
+
+   if (_mesa_is_desktop_gl(ctx) && ctx->Const.GLSLVersion >= 130) {
+      vs = compile_shader_with_debug(ctx, GL_VERTEX_SHADER, vs_int_source);
+      fs = compile_shader_with_debug(ctx, GL_FRAGMENT_SHADER, fs_int_source);
+
+      clear->IntegerShaderProg = _mesa_CreateProgramObjectARB();
+      _mesa_AttachShader(clear->IntegerShaderProg, fs);
+      _mesa_DeleteObjectARB(fs);
+      _mesa_AttachShader(clear->IntegerShaderProg, vs);
+      _mesa_DeleteObjectARB(vs);
+      _mesa_BindAttribLocationARB(clear->IntegerShaderProg, 0, "position");
+
+      /* Note that user-defined out attributes get automatically assigned
+       * locations starting from 0, so we don't need to explicitly
+       * BindFragDataLocation to 0.
+       */
+
+      link_program_with_debug(ctx, clear->IntegerShaderProg);
+
+      clear->IntegerColorLocation =
+        _mesa_GetUniformLocationARB(clear->IntegerShaderProg, "color");
+   }
+}
+
+static void
+meta_glsl_clear_cleanup(struct gl_context *ctx, struct clear_state *clear)
+{
+   if (clear->ArrayObj == 0)
+      return;
+   _mesa_DeleteVertexArraysAPPLE(1, &clear->ArrayObj);
+   clear->ArrayObj = 0;
+   _mesa_DeleteBuffersARB(1, &clear->VBO);
+   clear->VBO = 0;
+   _mesa_DeleteObjectARB(clear->ShaderProg);
+   clear->ShaderProg = 0;
+
+   if (clear->IntegerShaderProg) {
+      _mesa_DeleteObjectARB(clear->IntegerShaderProg);
+      clear->IntegerShaderProg = 0;
+   }
 }
 
 /**
@@ -1666,7 +1914,8 @@ _mesa_meta_glsl_Clear(struct gl_context *ctx, GLbitfield buffers)
               MESA_META_VERTEX |
               MESA_META_VIEWPORT |
               MESA_META_CLIP |
-              MESA_META_CLAMP_FRAGMENT_COLOR);
+              MESA_META_CLAMP_FRAGMENT_COLOR |
+               MESA_META_MULTISAMPLE);
 
    if (!(buffers & BUFFER_BITS_COLOR)) {
       /* We'll use colormask to disable color writes.  Otherwise,
@@ -1679,9 +1928,15 @@ _mesa_meta_glsl_Clear(struct gl_context *ctx, GLbitfield buffers)
 
    meta_glsl_clear_init(ctx, clear);
 
-   _mesa_UseProgramObjectARB(clear->ShaderProg);
-   _mesa_Uniform4fvARB(clear->ColorLocation, 1,
-                      ctx->Color.ClearColor.f);
+   if (fb->_IntegerColor) {
+      _mesa_UseProgramObjectARB(clear->IntegerShaderProg);
+      _mesa_Uniform4ivARB(clear->IntegerColorLocation, 1,
+                         ctx->Color.ClearColor.i);
+   } else {
+      _mesa_UseProgramObjectARB(clear->ShaderProg);
+      _mesa_Uniform4fvARB(clear->ColorLocation, 1,
+                         ctx->Color.ClearColor.f);
+   }
 
    _mesa_BindVertexArray(clear->ArrayObj);
    _mesa_BindBufferARB(GL_ARRAY_BUFFER_ARB, clear->VBO);
@@ -2023,8 +2278,7 @@ _mesa_meta_DrawPixels(struct gl_context *ctx,
     * Determine if we can do the glDrawPixels with texture mapping.
     */
    fallback = GL_FALSE;
-   if (ctx->_ImageTransferState ||
-       ctx->Fog.Enabled) {
+   if (ctx->Fog.Enabled) {
       fallback = GL_TRUE;
    }
 
@@ -2059,6 +2313,7 @@ _mesa_meta_DrawPixels(struct gl_context *ctx,
          texIntFormat = GL_ALPHA;
          metaExtraSave = (MESA_META_COLOR_MASK |
                           MESA_META_DEPTH_TEST |
+                          MESA_META_PIXEL_TRANSFER |
                           MESA_META_SHADER |
                           MESA_META_STENCIL_TEST);
       }
@@ -2105,7 +2360,6 @@ _mesa_meta_DrawPixels(struct gl_context *ctx,
                           MESA_META_CLIP |
                           MESA_META_VERTEX |
                           MESA_META_VIEWPORT |
-                         MESA_META_CLAMP_FRAGMENT_COLOR |
                           metaExtraSave));
 
    newTex = alloc_texture(tex, width, height, texIntFormat);
@@ -2748,15 +3002,15 @@ _mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target,
                           GL_SKIP_DECODE_EXT);
    }
    if (ctx->Extensions.EXT_framebuffer_sRGB) {
-      _mesa_Disable(GL_FRAMEBUFFER_SRGB_EXT);
+      _mesa_set_enable(ctx, GL_FRAMEBUFFER_SRGB_EXT, GL_FALSE);
    }
 
    _mesa_set_enable(ctx, target, GL_TRUE);
 
    /* setup texcoords (XXX what about border?) */
    setup_texture_coords(faceTarget,
-                        0.0, 0.0, /* width, height never used here */
                         slice,
+                        0, 0, /* width, height never used here */
                         verts[0].tex,
                         verts[1].tex,
                         verts[2].tex,
@@ -2810,43 +3064,20 @@ _mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target,
          break;
       }
 
-      /* Set MaxLevel large enough to hold the new level when we allocate it  */
+      /* Allocate storage for the destination mipmap image(s) */
+
+      /* Set MaxLevel large enough to hold the new level when we allocate it */
       _mesa_TexParameteri(target, GL_TEXTURE_MAX_LEVEL, dstLevel);
 
-      /* Create empty dest image */
-      if (target == GL_TEXTURE_1D) {
-         _mesa_TexImage1D(target, dstLevel, srcImage->InternalFormat,
-                          dstWidth, border,
-                          GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-      }
-      else if (target == GL_TEXTURE_3D) {
-         _mesa_TexImage3D(target, dstLevel, srcImage->InternalFormat,
-                          dstWidth, dstHeight, dstDepth, border,
-                          GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-      }
-      else {
-         /* 2D or cube */
-         _mesa_TexImage2D(faceTarget, dstLevel, srcImage->InternalFormat,
-                          dstWidth, dstHeight, border,
-                          GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-
-         if (target == GL_TEXTURE_CUBE_MAP) {
-            /* If texturing from a cube, we need to make sure all src faces
-             * have been defined (even if we're not sampling from them.)
-             * Otherwise the texture object will be 'incomplete' and
-             * texturing from it will not be allowed.
-             */
-            GLuint face;
-            for (face = 0; face < 6; face++) {
-               if (!texObj->Image[face][srcLevel] ||
-                   texObj->Image[face][srcLevel]->Width != srcWidth) {
-                  _mesa_TexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
-                                   srcLevel, srcImage->InternalFormat,
-                                   srcWidth, srcHeight, border,
-                                   GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-               }
-            }
-         }
+      if (!_mesa_prepare_mipmap_level(ctx, texObj, dstLevel,
+                                      dstWidth, dstHeight, dstDepth,
+                                      srcImage->Border,
+                                      srcImage->InternalFormat,
+                                      srcImage->TexFormat)) {
+         /* All done.  We either ran out of memory or we would go beyond the
+          * last valid level of an immutable texture if we continued.
+          */
+         break;
       }
 
       /* limit minification to src level */
@@ -2882,7 +3113,8 @@ _mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target,
       /* sanity check */
       status = _mesa_CheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
       if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
-         abort();
+         _mesa_problem(ctx, "Unexpected incomplete framebuffer in "
+                       "_mesa_meta_GenerateMipmap()");
          break;
       }
 
@@ -2900,7 +3132,7 @@ _mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target,
                           srgbDecodeSave);
    }
    if (ctx->Extensions.EXT_framebuffer_sRGB && srgbBufferSave) {
-      _mesa_Enable(GL_FRAMEBUFFER_SRGB_EXT);
+      _mesa_set_enable(ctx, GL_FRAMEBUFFER_SRGB_EXT, GL_TRUE);
    }
 
    _mesa_lock_texture(ctx, texObj); /* relock */
@@ -2929,13 +3161,15 @@ get_temp_image_type(struct gl_context *ctx, GLenum baseFormat)
    switch (baseFormat) {
    case GL_RGBA:
    case GL_RGB:
+   case GL_RG:
+   case GL_RED:
    case GL_ALPHA:
    case GL_LUMINANCE:
    case GL_LUMINANCE_ALPHA:
    case GL_INTENSITY:
       if (ctx->DrawBuffer->Visual.redBits <= 8)
          return GL_UNSIGNED_BYTE;
-      else if (ctx->DrawBuffer->Visual.redBits <= 8)
+      else if (ctx->DrawBuffer->Visual.redBits <= 16)
          return GL_UNSIGNED_SHORT;
       else
          return GL_FLOAT;
@@ -2944,7 +3178,8 @@ get_temp_image_type(struct gl_context *ctx, GLenum baseFormat)
    case GL_DEPTH_STENCIL:
       return GL_UNSIGNED_INT_24_8;
    default:
-      _mesa_problem(ctx, "Unexpected format in get_temp_image_type()");
+      _mesa_problem(ctx, "Unexpected format %d in get_temp_image_type()",
+                   baseFormat);
       return 0;
    }
 }
@@ -2954,22 +3189,19 @@ get_temp_image_type(struct gl_context *ctx, GLenum baseFormat)
  * Helper for _mesa_meta_CopyTexSubImage1/2/3D() functions.
  * Have to be careful with locking and meta state for pixel transfer.
  */
-static void
-copy_tex_sub_image(struct gl_context *ctx,
-                   GLuint dims, GLenum target, GLint level,
-                   GLint xoffset, GLint yoffset, GLint zoffset,
-                   GLint x, GLint y,
-                   GLsizei width, GLsizei height)
+void
+_mesa_meta_CopyTexSubImage(struct gl_context *ctx, GLuint dims,
+                           struct gl_texture_image *texImage,
+                           GLint xoffset, GLint yoffset, GLint zoffset,
+                           struct gl_renderbuffer *rb,
+                           GLint x, GLint y,
+                           GLsizei width, GLsizei height)
 {
-   struct gl_texture_object *texObj;
-   struct gl_texture_image *texImage;
+   struct gl_texture_object *texObj = texImage->TexObject;
    GLenum format, type;
    GLint bpp;
    void *buf;
 
-   texObj = _mesa_get_current_tex_object(ctx, target);
-   texImage = _mesa_select_tex_image(ctx, texObj, target, level);
-
    /* Choose format/type for temporary image buffer */
    format = _mesa_get_format_base_format(texImage->TexFormat);
    if (format == GL_LUMINANCE ||
@@ -2982,10 +3214,15 @@ copy_tex_sub_image(struct gl_context *ctx,
       format = GL_RGBA;
    }
 
+   if (_mesa_is_format_integer_color(texImage->TexFormat)) {
+      _mesa_problem(ctx, "unsupported integer color copyteximage");
+      return;
+   }
+
    type = get_temp_image_type(ctx, format);
    bpp = _mesa_bytes_per_pixel(format, type);
    if (bpp <= 0) {
-      _mesa_problem(ctx, "Bad bpp in meta copy_tex_sub_image()");
+      _mesa_problem(ctx, "Bad bpp in _mesa_meta_CopyTexSubImage()");
       return;
    }
 
@@ -3014,21 +3251,11 @@ copy_tex_sub_image(struct gl_context *ctx,
     * Store texture data (with pixel transfer ops)
     */
    _mesa_meta_begin(ctx, MESA_META_PIXEL_STORE);
-   if (target == GL_TEXTURE_1D) {
-      ctx->Driver.TexSubImage1D(ctx, target, level, xoffset,
-                                width, format, type, buf,
-                                &ctx->Unpack, texObj, texImage);
-   }
-   else if (target == GL_TEXTURE_3D) {
-      ctx->Driver.TexSubImage3D(ctx, target, level, xoffset, yoffset, zoffset,
-                                width, height, 1, format, type, buf,
-                                &ctx->Unpack, texObj, texImage);
-   }
-   else {
-      ctx->Driver.TexSubImage2D(ctx, target, level, xoffset, yoffset,
-                                width, height, format, type, buf,
-                                &ctx->Unpack, texObj, texImage);
-   }
+
+   ctx->Driver.TexSubImage(ctx, dims, texImage,
+                           xoffset, yoffset, zoffset, width, height, 1,
+                           format, type, buf, &ctx->Unpack);
+
    _mesa_meta_end(ctx);
 
    _mesa_lock_texture(ctx, texObj); /* re-lock */
@@ -3037,38 +3264,6 @@ copy_tex_sub_image(struct gl_context *ctx,
 }
 
 
-void
-_mesa_meta_CopyTexSubImage1D(struct gl_context *ctx, GLenum target, GLint level,
-                             GLint xoffset,
-                             GLint x, GLint y, GLsizei width)
-{
-   copy_tex_sub_image(ctx, 1, target, level, xoffset, 0, 0,
-                      x, y, width, 1);
-}
-
-
-void
-_mesa_meta_CopyTexSubImage2D(struct gl_context *ctx, GLenum target, GLint level,
-                             GLint xoffset, GLint yoffset,
-                             GLint x, GLint y,
-                             GLsizei width, GLsizei height)
-{
-   copy_tex_sub_image(ctx, 2, target, level, xoffset, yoffset, 0,
-                      x, y, width, height);
-}
-
-
-void
-_mesa_meta_CopyTexSubImage3D(struct gl_context *ctx, GLenum target, GLint level,
-                             GLint xoffset, GLint yoffset, GLint zoffset,
-                             GLint x, GLint y,
-                             GLsizei width, GLsizei height)
-{
-   copy_tex_sub_image(ctx, 3, target, level, xoffset, yoffset, zoffset,
-                      x, y, width, height);
-}
-
-
 /**
  * Decompress a texture image by drawing a quad with the compressed
  * texture and reading the pixels out of the color buffer.
@@ -3083,7 +3278,7 @@ decompress_texture_image(struct gl_context *ctx,
                          struct gl_texture_image *texImage,
                          GLuint slice,
                          GLenum destFormat, GLenum destType,
-                         GLvoid *dest, GLint destRowLength)
+                         GLvoid *dest)
 {
    struct decompress_state *decompress = &ctx->Meta->Decompress;
    struct gl_texture_object *texObj = texImage->TexObject;
@@ -3096,6 +3291,8 @@ decompress_texture_image(struct gl_context *ctx,
    };
    struct vertex verts[4];
    GLuint fboDrawSave, fboReadSave;
+   GLuint rbSave;
+   GLuint samplerSave;
 
    if (slice > 0) {
       assert(target == GL_TEXTURE_3D ||
@@ -3112,8 +3309,12 @@ decompress_texture_image(struct gl_context *ctx,
    /* save fbo bindings (not saved by _mesa_meta_begin()) */
    fboDrawSave = ctx->DrawBuffer->Name;
    fboReadSave = ctx->ReadBuffer->Name;
+   rbSave = ctx->CurrentRenderbuffer ? ctx->CurrentRenderbuffer->Name : 0;
 
-   _mesa_meta_begin(ctx, MESA_META_ALL);
+   _mesa_meta_begin(ctx, MESA_META_ALL & ~MESA_META_PIXEL_STORE);
+
+   samplerSave = ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler ?
+         ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler->Name : 0;
 
    /* Create/bind FBO/renderbuffer */
    if (decompress->FBO == 0) {
@@ -3131,7 +3332,8 @@ decompress_texture_image(struct gl_context *ctx,
    }
 
    /* alloc dest surface */
-   if (width != decompress->Width || height != decompress->Height) {
+   if (width > decompress->Width || height > decompress->Height) {
+      _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, decompress->RBO);
       _mesa_RenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA,
                                    width, height);
       decompress->Width = width;
@@ -3161,6 +3363,22 @@ decompress_texture_image(struct gl_context *ctx,
       _mesa_BindBufferARB(GL_ARRAY_BUFFER_ARB, decompress->VBO);
    }
 
+   if (!decompress->Sampler) {
+      _mesa_GenSamplers(1, &decompress->Sampler);
+      _mesa_BindSampler(ctx->Texture.CurrentUnit, decompress->Sampler);
+      /* nearest filtering */
+      _mesa_SamplerParameteri(decompress->Sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+      _mesa_SamplerParameteri(decompress->Sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+      /* No sRGB decode or encode.*/
+      if (ctx->Extensions.EXT_texture_sRGB_decode) {
+         _mesa_SamplerParameteri(decompress->Sampler, GL_TEXTURE_SRGB_DECODE_EXT,
+                             GL_SKIP_DECODE_EXT);
+      }
+
+   } else {
+      _mesa_BindSampler(ctx->Texture.CurrentUnit, decompress->Sampler);
+   }
+
    setup_texture_coords(faceTarget, slice, width, height,
                         verts[0].tex,
                         verts[1].tex,
@@ -3186,28 +3404,16 @@ decompress_texture_image(struct gl_context *ctx,
 
    {
       /* save texture object state */
-      const GLenum minFilterSave = texObj->Sampler.MinFilter;
-      const GLenum magFilterSave = texObj->Sampler.MagFilter;
       const GLint baseLevelSave = texObj->BaseLevel;
       const GLint maxLevelSave = texObj->MaxLevel;
-      const GLenum wrapSSave = texObj->Sampler.WrapS;
-      const GLenum wrapTSave = texObj->Sampler.WrapT;
-      const GLenum srgbSave = texObj->Sampler.sRGBDecode;
 
       /* restrict sampling to the texture level of interest */
       _mesa_TexParameteri(target, GL_TEXTURE_BASE_LEVEL, texImage->Level);
       _mesa_TexParameteri(target, GL_TEXTURE_MAX_LEVEL, texImage->Level);
-      /* nearest filtering */
-      _mesa_TexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-      _mesa_TexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 
       /* No sRGB decode or encode.*/
-      if (ctx->Extensions.EXT_texture_sRGB_decode) {
-         _mesa_TexParameteri(target, GL_TEXTURE_SRGB_DECODE_EXT,
-                             GL_SKIP_DECODE_EXT);
-      }
       if (ctx->Extensions.EXT_framebuffer_sRGB) {
-         _mesa_Disable(GL_FRAMEBUFFER_SRGB_EXT);
+         _mesa_set_enable(ctx, GL_FRAMEBUFFER_SRGB_EXT, GL_FALSE);
       }
 
       /* render quad w/ texture into renderbuffer */
@@ -3216,26 +3422,40 @@ decompress_texture_image(struct gl_context *ctx,
       /* Restore texture object state, the texture binding will
        * be restored by _mesa_meta_end().
        */
-      _mesa_TexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilterSave);
-      _mesa_TexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilterSave);
       if (target != GL_TEXTURE_RECTANGLE_ARB) {
          _mesa_TexParameteri(target, GL_TEXTURE_BASE_LEVEL, baseLevelSave);
          _mesa_TexParameteri(target, GL_TEXTURE_MAX_LEVEL, maxLevelSave);
       }
-      _mesa_TexParameteri(target, GL_TEXTURE_WRAP_S, wrapSSave);
-      _mesa_TexParameteri(target, GL_TEXTURE_WRAP_T, wrapTSave);
-      if (ctx->Extensions.EXT_texture_sRGB_decode) {
-         _mesa_TexParameteri(target, GL_TEXTURE_SRGB_DECODE_EXT, srgbSave);
-      }
+
    }
 
    /* read pixels from renderbuffer */
-   ctx->Pack.RowLength = destRowLength;
-   _mesa_ReadPixels(0, 0, width, height, destFormat, destType, dest);
+   {
+      GLenum baseTexFormat = texImage->_BaseFormat;
+
+      /* The pixel transfer state will be set to default values at this point
+       * (see MESA_META_PIXEL_TRANSFER) so pixel transfer ops are effectively
+       * turned off (as required by glGetTexImage) but we need to handle some
+       * special cases.  In particular, single-channel texture values are
+       * returned as red and two-channel texture values are returned as
+       * red/alpha.
+       */
+      if (baseTexFormat == GL_LUMINANCE ||
+          baseTexFormat == GL_LUMINANCE_ALPHA ||
+          baseTexFormat == GL_INTENSITY) {
+         /* Green and blue must be zero */
+         _mesa_PixelTransferf(GL_GREEN_SCALE, 0.0f);
+         _mesa_PixelTransferf(GL_BLUE_SCALE, 0.0f);
+      }
+
+      _mesa_ReadPixels(0, 0, width, height, destFormat, destType, dest);
+   }
 
    /* disable texture unit */
    _mesa_set_enable(ctx, target, GL_FALSE);
 
+   _mesa_BindSampler(ctx->Texture.CurrentUnit, samplerSave);
+
    _mesa_meta_end(ctx);
 
    /* restore fbo bindings */
@@ -3246,6 +3466,7 @@ decompress_texture_image(struct gl_context *ctx,
       _mesa_BindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fboDrawSave);
       _mesa_BindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, fboReadSave);
    }
+   _mesa_BindRenderbufferEXT(GL_RENDERBUFFER_EXT, rbSave);
 }
 
 
@@ -3255,9 +3476,8 @@ decompress_texture_image(struct gl_context *ctx,
  * from core Mesa.
  */
 void
-_mesa_meta_GetTexImage(struct gl_context *ctx, GLenum target, GLint level,
+_mesa_meta_GetTexImage(struct gl_context *ctx,
                        GLenum format, GLenum type, GLvoid *pixels,
-                       struct gl_texture_object *texObj,
                        struct gl_texture_image *texImage)
 {
    /* We can only use the decompress-with-blit method here if the texels are
@@ -3267,16 +3487,143 @@ _mesa_meta_GetTexImage(struct gl_context *ctx, GLenum target, GLint level,
    if (_mesa_is_format_compressed(texImage->TexFormat) &&
        _mesa_get_format_datatype(texImage->TexFormat)
        == GL_UNSIGNED_NORMALIZED) {
+      struct gl_texture_object *texObj = texImage->TexObject;
       const GLuint slice = 0; /* only 2D compressed textures for now */
       /* Need to unlock the texture here to prevent deadlock... */
       _mesa_unlock_texture(ctx, texObj);
-      decompress_texture_image(ctx, texImage, slice, format, type, pixels,
-                               ctx->Pack.RowLength);
+      decompress_texture_image(ctx, texImage, slice, format, type, pixels);
       /* ... and relock it */
       _mesa_lock_texture(ctx, texObj);
    }
    else {
-      _mesa_get_teximage(ctx, target, level, format, type, pixels,
-                          texObj, texImage);
+      _mesa_get_teximage(ctx, format, type, pixels, texImage);
    }
 }
+
+
+/**
+ * Meta implementation of ctx->Driver.DrawTex() in terms
+ * of polygon rendering.
+ */
+void
+_mesa_meta_DrawTex(struct gl_context *ctx, GLfloat x, GLfloat y, GLfloat z,
+                   GLfloat width, GLfloat height)
+{
+#if FEATURE_OES_draw_texture
+   struct drawtex_state *drawtex = &ctx->Meta->DrawTex;
+   struct vertex {
+      GLfloat x, y, z, st[MAX_TEXTURE_UNITS][2];
+   };
+   struct vertex verts[4];
+   GLuint i;
+
+   _mesa_meta_begin(ctx, (MESA_META_RASTERIZATION |
+                          MESA_META_SHADER |
+                          MESA_META_TRANSFORM |
+                          MESA_META_VERTEX |
+                          MESA_META_VIEWPORT));
+
+   if (drawtex->ArrayObj == 0) {
+      /* one-time setup */
+      GLint active_texture;
+
+      /* create vertex array object */
+      _mesa_GenVertexArrays(1, &drawtex->ArrayObj);
+      _mesa_BindVertexArray(drawtex->ArrayObj);
+
+      /* create vertex array buffer */
+      _mesa_GenBuffersARB(1, &drawtex->VBO);
+      _mesa_BindBufferARB(GL_ARRAY_BUFFER_ARB, drawtex->VBO);
+      _mesa_BufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(verts),
+                          NULL, GL_DYNAMIC_DRAW_ARB);
+
+      /* client active texture is not part of the array object */
+      active_texture = ctx->Array.ActiveTexture;
+
+      /* setup vertex arrays */
+      _mesa_VertexPointer(3, GL_FLOAT, sizeof(struct vertex), OFFSET(x));
+      _mesa_EnableClientState(GL_VERTEX_ARRAY);
+      for (i = 0; i < ctx->Const.MaxTextureUnits; i++) {
+         _mesa_ClientActiveTextureARB(GL_TEXTURE0 + i);
+         _mesa_TexCoordPointer(2, GL_FLOAT, sizeof(struct vertex), OFFSET(st[i]));
+         _mesa_EnableClientState(GL_TEXTURE_COORD_ARRAY);
+      }
+
+      /* restore client active texture */
+      _mesa_ClientActiveTextureARB(GL_TEXTURE0 + active_texture);
+   }
+   else {
+      _mesa_BindVertexArray(drawtex->ArrayObj);
+      _mesa_BindBufferARB(GL_ARRAY_BUFFER_ARB, drawtex->VBO);
+   }
+
+   /* vertex positions, texcoords */
+   {
+      const GLfloat x1 = x + width;
+      const GLfloat y1 = y + height;
+
+      z = CLAMP(z, 0.0, 1.0);
+      z = invert_z(z);
+
+      verts[0].x = x;
+      verts[0].y = y;
+      verts[0].z = z;
+
+      verts[1].x = x1;
+      verts[1].y = y;
+      verts[1].z = z;
+
+      verts[2].x = x1;
+      verts[2].y = y1;
+      verts[2].z = z;
+
+      verts[3].x = x;
+      verts[3].y = y1;
+      verts[3].z = z;
+
+      for (i = 0; i < ctx->Const.MaxTextureUnits; i++) {
+         const struct gl_texture_object *texObj;
+         const struct gl_texture_image *texImage;
+         GLfloat s, t, s1, t1;
+         GLuint tw, th;
+
+         if (!ctx->Texture.Unit[i]._ReallyEnabled) {
+            GLuint j;
+            for (j = 0; j < 4; j++) {
+               verts[j].st[i][0] = 0.0f;
+               verts[j].st[i][1] = 0.0f;
+            }
+            continue;
+         }
+
+         texObj = ctx->Texture.Unit[i]._Current;
+         texImage = texObj->Image[0][texObj->BaseLevel];
+         tw = texImage->Width2;
+         th = texImage->Height2;
+
+         s = (GLfloat) texObj->CropRect[0] / tw;
+         t = (GLfloat) texObj->CropRect[1] / th;
+         s1 = (GLfloat) (texObj->CropRect[0] + texObj->CropRect[2]) / tw;
+         t1 = (GLfloat) (texObj->CropRect[1] + texObj->CropRect[3]) / th;
+
+         verts[0].st[i][0] = s;
+         verts[0].st[i][1] = t;
+
+         verts[1].st[i][0] = s1;
+         verts[1].st[i][1] = t;
+
+         verts[2].st[i][0] = s1;
+         verts[2].st[i][1] = t1;
+
+         verts[3].st[i][0] = s;
+         verts[3].st[i][1] = t1;
+      }
+
+      _mesa_BufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(verts), verts);
+   }
+
+   _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+   _mesa_meta_end(ctx);
+#endif /* FEATURE_OES_draw_texture */
+}