i965/gen6_surface_state.c: Remove (gen < 6) code path
[mesa.git] / src / mesa / drivers / common / meta.c
index 3ef3f79714e12f82990adb8dcce3be1003d1e313..485128696c5ae476f2ca7f4ce7a2c610210c5520 100644 (file)
@@ -40,6 +40,7 @@
 #include "main/blit.h"
 #include "main/bufferobj.h"
 #include "main/buffers.h"
+#include "main/clear.h"
 #include "main/colortab.h"
 #include "main/condrender.h"
 #include "main/depth.h"
@@ -47,6 +48,7 @@
 #include "main/fbobject.h"
 #include "main/feedback.h"
 #include "main/formats.h"
+#include "main/format_unpack.h"
 #include "main/glformats.h"
 #include "main/image.h"
 #include "main/macros.h"
@@ -71,6 +73,7 @@
 #include "main/teximage.h"
 #include "main/texparam.h"
 #include "main/texstate.h"
+#include "main/texstore.h"
 #include "main/transformfeedback.h"
 #include "main/uniforms.h"
 #include "main/varray.h"
@@ -81,7 +84,7 @@
 #include "drivers/common/meta.h"
 #include "main/enums.h"
 #include "main/glformats.h"
-#include "../glsl/ralloc.h"
+#include "util/ralloc.h"
 
 /** Return offset in bytes of the field within a vertex struct */
 #define OFFSET(FIELD) ((void *) offsetof(struct vertex, FIELD))
@@ -98,18 +101,18 @@ static void meta_decompress_cleanup(struct decompress_state *decompress);
 static void meta_drawpix_cleanup(struct drawpix_state *drawpix);
 
 void
-_mesa_meta_bind_fbo_image(GLenum attachment,
+_mesa_meta_bind_fbo_image(GLenum fboTarget, GLenum attachment,
                           struct gl_texture_image *texImage, GLuint layer)
 {
    struct gl_texture_object *texObj = texImage->TexObject;
    int level = texImage->Level;
-   GLenum target = texObj->Target;
+   GLenum texTarget = texObj->Target;
 
-   switch (target) {
+   switch (texTarget) {
    case GL_TEXTURE_1D:
-      _mesa_FramebufferTexture1D(GL_FRAMEBUFFER,
+      _mesa_FramebufferTexture1D(fboTarget,
                                  attachment,
-                                 target,
+                                 texTarget,
                                  texObj->Name,
                                  level);
       break;
@@ -118,19 +121,19 @@ _mesa_meta_bind_fbo_image(GLenum attachment,
    case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
    case GL_TEXTURE_CUBE_MAP_ARRAY:
    case GL_TEXTURE_3D:
-      _mesa_FramebufferTextureLayer(GL_FRAMEBUFFER,
+      _mesa_FramebufferTextureLayer(fboTarget,
                                     attachment,
                                     texObj->Name,
                                     level,
                                     layer);
       break;
    default: /* 2D / cube */
-      if (target == GL_TEXTURE_CUBE_MAP)
-         target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + texImage->Face;
+      if (texTarget == GL_TEXTURE_CUBE_MAP)
+         texTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + texImage->Face;
 
-      _mesa_FramebufferTexture2D(GL_FRAMEBUFFER,
+      _mesa_FramebufferTexture2D(fboTarget,
                                  attachment,
-                                 target,
+                                 texTarget,
                                  texObj->Name,
                                  level);
    }
@@ -217,6 +220,7 @@ _mesa_meta_compile_and_link_program(struct gl_context *ctx,
                                                     fs_source);
 
    *program = _mesa_CreateProgram();
+   _mesa_ObjectLabel(GL_PROGRAM, *program, -1, name);
    _mesa_AttachShader(*program, fs);
    _mesa_DeleteShader(fs);
    _mesa_AttachShader(*program, vs);
@@ -224,7 +228,6 @@ _mesa_meta_compile_and_link_program(struct gl_context *ctx,
    _mesa_BindAttribLocation(*program, 0, "position");
    _mesa_BindAttribLocation(*program, 1, "texcoords");
    _mesa_meta_link_program_with_debug(ctx, *program);
-   _mesa_ObjectLabel(GL_PROGRAM, *program, -1, name);
 
    _mesa_UseProgram(*program);
 }
@@ -242,10 +245,25 @@ _mesa_meta_setup_blit_shader(struct gl_context *ctx,
                              GLenum target,
                              struct blit_shader_table *table)
 {
-   const char *vs_source;
-   char *fs_source;
+   char *vs_source, *fs_source;
    void *const mem_ctx = ralloc_context(NULL);
    struct blit_shader *shader = choose_blit_shader(target, table);
+   const char *vs_input, *vs_output, *fs_input, *vs_preprocess, *fs_preprocess;
+
+   if (ctx->Const.GLSLVersion < 130) {
+      vs_preprocess = "";
+      vs_input = "attribute";
+      vs_output = "varying";
+      fs_preprocess = "#extension GL_EXT_texture_array : enable";
+      fs_input = "varying";
+   } else {
+      vs_preprocess = "#version 130";
+      vs_input = "in";
+      vs_output = "out";
+      fs_preprocess = "#version 130";
+      fs_input = "in";
+      shader->func = "texture";
+   }
 
    assert(shader != NULL);
 
@@ -254,57 +272,30 @@ _mesa_meta_setup_blit_shader(struct gl_context *ctx,
       return;
    }
 
-   if (ctx->Const.GLSLVersion < 130) {
-      vs_source =
-         "attribute vec2 position;\n"
-         "attribute vec4 textureCoords;\n"
-         "varying vec4 texCoords;\n"
-         "void main()\n"
-         "{\n"
-         "   texCoords = textureCoords;\n"
-         "   gl_Position = vec4(position, 0.0, 1.0);\n"
-         "}\n";
-
-      fs_source = ralloc_asprintf(mem_ctx,
-                                  "#extension GL_EXT_texture_array : enable\n"
-                                  "#extension GL_ARB_texture_cube_map_array: enable\n"
-                                  "uniform %s texSampler;\n"
-                                  "varying vec4 texCoords;\n"
-                                  "void main()\n"
-                                  "{\n"
-                                  "   gl_FragColor = %s(texSampler, %s);\n"
-                                  "   gl_FragDepth = gl_FragColor.x;\n"
-                                  "}\n",
-                                  shader->type,
-                                  shader->func, shader->texcoords);
-   }
-   else {
-      vs_source = ralloc_asprintf(mem_ctx,
-                                  "#version 130\n"
-                                  "in vec2 position;\n"
-                                  "in vec4 textureCoords;\n"
-                                  "out vec4 texCoords;\n"
-                                  "void main()\n"
-                                  "{\n"
-                                  "   texCoords = textureCoords;\n"
-                                  "   gl_Position = vec4(position, 0.0, 1.0);\n"
-                                  "}\n");
-      fs_source = ralloc_asprintf(mem_ctx,
-                                  "#version 130\n"
-                                  "#extension GL_ARB_texture_cube_map_array: enable\n"
-                                  "uniform %s texSampler;\n"
-                                  "in vec4 texCoords;\n"
-                                  "out vec4 out_color;\n"
-                                  "\n"
-                                  "void main()\n"
-                                  "{\n"
-                                  "   out_color = texture(texSampler, %s);\n"
-                                  "   gl_FragDepth = out_color.x;\n"
-                                  "}\n",
-                                  shader->type,
-                                  shader->texcoords);
-   }
-
+   vs_source = ralloc_asprintf(mem_ctx,
+                "%s\n"
+                "%s vec2 position;\n"
+                "%s vec4 textureCoords;\n"
+                "%s vec4 texCoords;\n"
+                "void main()\n"
+                "{\n"
+                "   texCoords = textureCoords;\n"
+                "   gl_Position = vec4(position, 0.0, 1.0);\n"
+                "}\n",
+                vs_preprocess, vs_input, vs_input, vs_output);
+
+   fs_source = ralloc_asprintf(mem_ctx,
+                "%s\n"
+                "#extension GL_ARB_texture_cube_map_array: enable\n"
+                "uniform %s texSampler;\n"
+                "%s vec4 texCoords;\n"
+                "void main()\n"
+                "{\n"
+                "   gl_FragColor = %s(texSampler, %s);\n"
+                "   gl_FragDepth = gl_FragColor.x;\n"
+                "}\n",
+                fs_preprocess, shader->type, fs_input,
+                shader->func, shader->texcoords);
 
    _mesa_meta_compile_and_link_program(ctx, vs_source, fs_source,
                                        ralloc_asprintf(mem_ctx, "%s blit",
@@ -517,6 +508,11 @@ _mesa_meta_begin(struct gl_context *ctx, GLbitfield state)
          _mesa_set_enable(ctx, GL_COLOR_LOGIC_OP, GL_FALSE);
    }
 
+   if (state & MESA_META_DITHER) {
+      save->DitherFlag = ctx->Color.DitherFlag;
+      _mesa_set_enable(ctx, GL_DITHER, GL_TRUE);
+   }
+
    if (state & MESA_META_COLOR_MASK) {
       memcpy(save->ColorMask, ctx->Color.ColorMask,
              sizeof(ctx->Color.ColorMask));
@@ -813,7 +809,7 @@ _mesa_meta_begin(struct gl_context *ctx, GLbitfield state)
       int buf, real_color_buffers = 0;
       memset(save->ColorDrawBuffers, 0, sizeof(save->ColorDrawBuffers));
 
-      for (buf = 0; buf < MAX_DRAW_BUFFERS; buf++) {
+      for (buf = 0; buf < ctx->Const.MaxDrawBuffers; buf++) {
          int buf_index = ctx->DrawBuffer->_ColorDrawBufferIndexes[buf];
          if (buf_index == -1)
             continue;
@@ -887,6 +883,9 @@ _mesa_meta_end(struct gl_context *ctx)
          _mesa_set_enable(ctx, GL_COLOR_LOGIC_OP, save->ColorLogicOpEnabled);
    }
 
+   if (state & MESA_META_DITHER)
+      _mesa_set_enable(ctx, GL_DITHER, save->DitherFlag);
+
    if (state & MESA_META_COLOR_MASK) {
       GLuint i;
       for (i = 0; i < ctx->Const.MaxDrawBuffers; i++) {
@@ -1225,7 +1224,7 @@ _mesa_meta_end(struct gl_context *ctx)
       _mesa_BindRenderbuffer(GL_RENDERBUFFER, save->RenderbufferName);
 
    if (state & MESA_META_DRAW_BUFFERS) {
-      _mesa_DrawBuffers(MAX_DRAW_BUFFERS, save->ColorDrawBuffers);
+      _mesa_DrawBuffers(ctx->Const.MaxDrawBuffers, save->ColorDrawBuffers);
    }
 
    ctx->Meta->SaveStackDepth--;
@@ -1527,31 +1526,23 @@ static void
 meta_glsl_clear_init(struct gl_context *ctx, struct clear_state *clear)
 {
    const char *vs_source =
+      "#extension GL_AMD_vertex_shader_layer : enable\n"
+      "#extension GL_ARB_draw_instanced : enable\n"
       "attribute vec4 position;\n"
       "void main()\n"
       "{\n"
+      "#ifdef GL_AMD_vertex_shader_layer\n"
+      "   gl_Layer = gl_InstanceID;\n"
+      "#endif\n"
       "   gl_Position = position;\n"
       "}\n";
-   const char *gs_source =
-      "#version 150\n"
-      "layout(triangles) in;\n"
-      "layout(triangle_strip, max_vertices = 4) out;\n"
-      "uniform int layer;\n"
-      "void main()\n"
-      "{\n"
-      "  for (int i = 0; i < 3; i++) {\n"
-      "    gl_Layer = layer;\n"
-      "    gl_Position = gl_in[i].gl_Position;\n"
-      "    EmitVertex();\n"
-      "  }\n"
-      "}\n";
    const char *fs_source =
       "uniform vec4 color;\n"
       "void main()\n"
       "{\n"
       "   gl_FragColor = color;\n"
       "}\n";
-   GLuint vs, gs = 0, fs;
+   GLuint vs, fs;
    bool has_integer_textures;
 
    _mesa_meta_setup_vertex_objects(&clear->VAO, &clear->VBO, true, 3, 0, 0);
@@ -1563,12 +1554,6 @@ meta_glsl_clear_init(struct gl_context *ctx, struct clear_state *clear)
    _mesa_ShaderSource(vs, 1, &vs_source, NULL);
    _mesa_CompileShader(vs);
 
-   if (_mesa_has_geometry_shaders(ctx)) {
-      gs = _mesa_CreateShader(GL_GEOMETRY_SHADER);
-      _mesa_ShaderSource(gs, 1, &gs_source, NULL);
-      _mesa_CompileShader(gs);
-   }
-
    fs = _mesa_CreateShader(GL_FRAGMENT_SHADER);
    _mesa_ShaderSource(fs, 1, &fs_source, NULL);
    _mesa_CompileShader(fs);
@@ -1576,19 +1561,13 @@ meta_glsl_clear_init(struct gl_context *ctx, struct clear_state *clear)
    clear->ShaderProg = _mesa_CreateProgram();
    _mesa_AttachShader(clear->ShaderProg, fs);
    _mesa_DeleteShader(fs);
-   if (gs != 0)
-      _mesa_AttachShader(clear->ShaderProg, gs);
    _mesa_AttachShader(clear->ShaderProg, vs);
    _mesa_DeleteShader(vs);
    _mesa_BindAttribLocation(clear->ShaderProg, 0, "position");
+   _mesa_ObjectLabel(GL_PROGRAM, clear->ShaderProg, -1, "meta clear");
    _mesa_LinkProgram(clear->ShaderProg);
 
-   clear->ColorLocation = _mesa_GetUniformLocation(clear->ShaderProg,
-                                                     "color");
-   if (gs != 0) {
-      clear->LayerLocation = _mesa_GetUniformLocation(clear->ShaderProg,
-                                                     "layer");
-   }
+   clear->ColorLocation = _mesa_GetUniformLocation(clear->ShaderProg, "color");
 
    has_integer_textures = _mesa_is_gles3(ctx) ||
       (_mesa_is_desktop_gl(ctx) && ctx->Const.GLSLVersion >= 130);
@@ -1598,9 +1577,14 @@ meta_glsl_clear_init(struct gl_context *ctx, struct clear_state *clear)
       const char *vs_int_source =
          ralloc_asprintf(shader_source_mem_ctx,
                          "#version 130\n"
+                         "#extension GL_AMD_vertex_shader_layer : enable\n"
+                         "#extension GL_ARB_draw_instanced : enable\n"
                          "in vec4 position;\n"
                          "void main()\n"
                          "{\n"
+                         "#ifdef GL_AMD_vertex_shader_layer\n"
+                         "   gl_Layer = gl_InstanceID;\n"
+                         "#endif\n"
                          "   gl_Position = position;\n"
                          "}\n");
       const char *fs_int_source =
@@ -1623,8 +1607,6 @@ meta_glsl_clear_init(struct gl_context *ctx, struct clear_state *clear)
       clear->IntegerShaderProg = _mesa_CreateProgram();
       _mesa_AttachShader(clear->IntegerShaderProg, fs);
       _mesa_DeleteShader(fs);
-      if (gs != 0)
-         _mesa_AttachShader(clear->IntegerShaderProg, gs);
       _mesa_AttachShader(clear->IntegerShaderProg, vs);
       _mesa_DeleteShader(vs);
       _mesa_BindAttribLocation(clear->IntegerShaderProg, 0, "position");
@@ -1640,13 +1622,7 @@ meta_glsl_clear_init(struct gl_context *ctx, struct clear_state *clear)
 
       clear->IntegerColorLocation =
         _mesa_GetUniformLocation(clear->IntegerShaderProg, "color");
-      if (gs != 0) {
-         clear->IntegerLayerLocation =
-            _mesa_GetUniformLocation(clear->IntegerShaderProg, "layer");
-      }
    }
-   if (gs != 0)
-      _mesa_DeleteShader(gs);
 }
 
 static void
@@ -1674,8 +1650,8 @@ meta_glsl_clear_cleanup(struct clear_state *clear)
  * Since the bitfield has no associated order, the assignment of draw buffer
  * indices to color attachment indices is rather arbitrary.
  */
-static void
-drawbuffers_from_bitfield(GLbitfield bits)
+void
+_mesa_meta_drawbuffers_from_bitfield(GLbitfield bits)
 {
    GLenum enums[MAX_DRAW_BUFFERS];
    int i = 0;
@@ -1788,7 +1764,7 @@ meta_clear(struct gl_context *ctx, GLbitfield buffers, bool glsl)
    /* GL_COLOR_BUFFER_BIT */
    if (buffers & BUFFER_BITS_COLOR) {
       /* Only draw to the buffers we were asked to clear. */
-      drawbuffers_from_bitfield(buffers & BUFFER_BITS_COLOR);
+      _mesa_meta_drawbuffers_from_bitfield(buffers & BUFFER_BITS_COLOR);
 
       /* leave colormask state as-is */
 
@@ -1853,15 +1829,7 @@ meta_clear(struct gl_context *ctx, GLbitfield buffers, bool glsl)
 
    /* draw quad(s) */
    if (fb->MaxNumLayers > 0) {
-      unsigned layer;
-      assert(glsl);
-      for (layer = 0; layer < fb->MaxNumLayers; layer++) {
-         if (fb->_IntegerColor)
-            _mesa_Uniform1i(clear->IntegerLayerLocation, layer);
-         else
-            _mesa_Uniform1i(clear->LayerLocation, layer);
-         _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
-      }
+      _mesa_DrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, fb->MaxNumLayers);
    } else {
       _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
    }
@@ -2624,7 +2592,7 @@ _mesa_meta_setup_texture_coords(GLenum faceTarget,
             coord = coords3;
             break;
          default:
-            assert(0);
+            unreachable("not reached");
          }
 
          coord[3] = (float) (slice / 6);
@@ -2753,9 +2721,9 @@ _mesa_meta_blit_shader_table_cleanup(struct blit_shader_table *table)
 static GLenum
 get_temp_image_type(struct gl_context *ctx, mesa_format format)
 {
-   GLenum baseFormat;
-
-   baseFormat = _mesa_get_format_base_format(format);
+   const GLenum baseFormat = _mesa_get_format_base_format(format);
+   const GLenum datatype = _mesa_get_format_datatype(format);
+   const GLint format_red_bits = _mesa_get_format_bits(format, GL_RED_BITS);
 
    switch (baseFormat) {
    case GL_RGBA:
@@ -2766,30 +2734,24 @@ get_temp_image_type(struct gl_context *ctx, mesa_format format)
    case GL_LUMINANCE:
    case GL_LUMINANCE_ALPHA:
    case GL_INTENSITY:
-      if (ctx->DrawBuffer->Visual.redBits <= 8) {
+      if (datatype == GL_INT || datatype == GL_UNSIGNED_INT) {
+         return datatype;
+      } else if (format_red_bits <= 8) {
          return GL_UNSIGNED_BYTE;
-      } else if (ctx->DrawBuffer->Visual.redBits <= 16) {
+      } else if (format_red_bits <= 16) {
          return GL_UNSIGNED_SHORT;
-      } else {
-         GLenum datatype = _mesa_get_format_datatype(format);
-         if (datatype == GL_INT || datatype == GL_UNSIGNED_INT)
-            return datatype;
-         return GL_FLOAT;
       }
-   case GL_DEPTH_COMPONENT: {
-      GLenum datatype = _mesa_get_format_datatype(format);
+      return GL_FLOAT;
+   case GL_DEPTH_COMPONENT:
       if (datatype == GL_FLOAT)
          return GL_FLOAT;
       else
          return GL_UNSIGNED_INT;
-   }
-   case GL_DEPTH_STENCIL: {
-      GLenum datatype = _mesa_get_format_datatype(format);
+   case GL_DEPTH_STENCIL:
       if (datatype == GL_FLOAT)
          return GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
       else
          return GL_UNSIGNED_INT_24_8;
-   }
    default:
       _mesa_problem(ctx, "Unexpected format %d in get_temp_image_type()",
                    baseFormat);
@@ -2829,17 +2791,20 @@ copytexsubimage_using_blit_framebuffer(struct gl_context *ctx, GLuint dims,
 
    if (rb->_BaseFormat == GL_DEPTH_STENCIL ||
        rb->_BaseFormat == GL_DEPTH_COMPONENT) {
-      _mesa_meta_bind_fbo_image(GL_DEPTH_ATTACHMENT, texImage, zoffset);
+      _mesa_meta_bind_fbo_image(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                                texImage, zoffset);
       mask = GL_DEPTH_BUFFER_BIT;
 
       if (rb->_BaseFormat == GL_DEPTH_STENCIL &&
           texImage->_BaseFormat == GL_DEPTH_STENCIL) {
-         _mesa_meta_bind_fbo_image(GL_STENCIL_ATTACHMENT, texImage, zoffset);
+         _mesa_meta_bind_fbo_image(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+                                   texImage, zoffset);
          mask |= GL_STENCIL_BUFFER_BIT;
       }
       _mesa_DrawBuffer(GL_NONE);
    } else {
-      _mesa_meta_bind_fbo_image(GL_COLOR_ATTACHMENT0, texImage, zoffset);
+      _mesa_meta_bind_fbo_image(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                texImage, zoffset);
       mask = GL_COLOR_BUFFER_BIT;
       _mesa_DrawBuffer(GL_COLOR_ATTACHMENT0);
    }
@@ -2860,13 +2825,13 @@ copytexsubimage_using_blit_framebuffer(struct gl_context *ctx, GLuint dims,
     * are too strict for CopyTexImage.  We know meta will be fine with format
     * changes.
     */
-   _mesa_meta_BlitFramebuffer(ctx, x, y,
-                              x + width, y + height,
-                              xoffset, yoffset,
-                              xoffset + width, yoffset + height,
-                              mask, GL_NEAREST);
+   mask = _mesa_meta_BlitFramebuffer(ctx, x, y,
+                                     x + width, y + height,
+                                     xoffset, yoffset,
+                                     xoffset + width, yoffset + height,
+                                     mask, GL_NEAREST);
    ctx->Meta->Blit.no_ctsi_fallback = false;
-   success = true;
+   success = mask == 0x0;
 
  out:
    _mesa_lock_texture(ctx, texObj);
@@ -2967,14 +2932,22 @@ _mesa_meta_CopyTexSubImage(struct gl_context *ctx, GLuint dims,
    free(buf);
 }
 
+static void
+meta_decompress_fbo_cleanup(struct decompress_fbo_state *decompress_fbo)
+{
+   if (decompress_fbo->FBO != 0) {
+      _mesa_DeleteFramebuffers(1, &decompress_fbo->FBO);
+      _mesa_DeleteRenderbuffers(1, &decompress_fbo->RBO);
+   }
+
+   memset(decompress_fbo, 0, sizeof(*decompress_fbo));
+}
 
 static void
 meta_decompress_cleanup(struct decompress_state *decompress)
 {
-   if (decompress->FBO != 0) {
-      _mesa_DeleteFramebuffers(1, &decompress->FBO);
-      _mesa_DeleteRenderbuffers(1, &decompress->RBO);
-   }
+   meta_decompress_fbo_cleanup(&decompress->byteFBO);
+   meta_decompress_fbo_cleanup(&decompress->floatFBO);
 
    if (decompress->VAO != 0) {
       _mesa_DeleteVertexArrays(1, &decompress->VAO);
@@ -2996,7 +2969,7 @@ meta_decompress_cleanup(struct decompress_state *decompress)
  * \param dest  destination buffer
  * \param destRowLength  dest image rowLength (ala GL_PACK_ROW_LENGTH)
  */
-static void
+static bool
 decompress_texture_image(struct gl_context *ctx,
                          struct gl_texture_image *texImage,
                          GLuint slice,
@@ -3004,17 +2977,33 @@ decompress_texture_image(struct gl_context *ctx,
                          GLvoid *dest)
 {
    struct decompress_state *decompress = &ctx->Meta->Decompress;
+   struct decompress_fbo_state *decompress_fbo;
    struct gl_texture_object *texObj = texImage->TexObject;
    const GLint width = texImage->Width;
    const GLint height = texImage->Height;
    const GLint depth = texImage->Height;
    const GLenum target = texObj->Target;
+   GLenum rbFormat;
    GLenum faceTarget;
    struct vertex verts[4];
    GLuint samplerSave;
+   GLenum status;
    const bool use_glsl_version = ctx->Extensions.ARB_vertex_shader &&
                                       ctx->Extensions.ARB_fragment_shader;
 
+   switch (_mesa_get_format_datatype(texImage->TexFormat)) {
+   case GL_FLOAT:
+      decompress_fbo = &decompress->floatFBO;
+      rbFormat = GL_RGBA32F;
+      break;
+   case GL_UNSIGNED_NORMALIZED:
+      decompress_fbo = &decompress->byteFBO;
+      rbFormat = GL_RGBA;
+      break;
+   default:
+      return false;
+   }
+
    if (slice > 0) {
       assert(target == GL_TEXTURE_3D ||
              target == GL_TEXTURE_2D_ARRAY ||
@@ -3025,11 +3014,11 @@ decompress_texture_image(struct gl_context *ctx,
    case GL_TEXTURE_1D:
    case GL_TEXTURE_1D_ARRAY:
       assert(!"No compressed 1D textures.");
-      return;
+      return false;
 
    case GL_TEXTURE_3D:
       assert(!"No compressed 3D textures.");
-      return;
+      return false;
 
    case GL_TEXTURE_CUBE_MAP_ARRAY:
       faceTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + (slice % 6);
@@ -3051,27 +3040,35 @@ decompress_texture_image(struct gl_context *ctx,
          ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler->Name : 0;
 
    /* Create/bind FBO/renderbuffer */
-   if (decompress->FBO == 0) {
-      _mesa_GenFramebuffers(1, &decompress->FBO);
-      _mesa_GenRenderbuffers(1, &decompress->RBO);
-      _mesa_BindFramebuffer(GL_FRAMEBUFFER_EXT, decompress->FBO);
-      _mesa_BindRenderbuffer(GL_RENDERBUFFER_EXT, decompress->RBO);
+   if (decompress_fbo->FBO == 0) {
+      _mesa_GenFramebuffers(1, &decompress_fbo->FBO);
+      _mesa_GenRenderbuffers(1, &decompress_fbo->RBO);
+      _mesa_BindFramebuffer(GL_FRAMEBUFFER_EXT, decompress_fbo->FBO);
+      _mesa_BindRenderbuffer(GL_RENDERBUFFER_EXT, decompress_fbo->RBO);
       _mesa_FramebufferRenderbuffer(GL_FRAMEBUFFER_EXT,
                                        GL_COLOR_ATTACHMENT0_EXT,
                                        GL_RENDERBUFFER_EXT,
-                                       decompress->RBO);
+                                       decompress_fbo->RBO);
    }
    else {
-      _mesa_BindFramebuffer(GL_FRAMEBUFFER_EXT, decompress->FBO);
+      _mesa_BindFramebuffer(GL_FRAMEBUFFER_EXT, decompress_fbo->FBO);
    }
 
    /* alloc dest surface */
-   if (width > decompress->Width || height > decompress->Height) {
-      _mesa_BindRenderbuffer(GL_RENDERBUFFER_EXT, decompress->RBO);
-      _mesa_RenderbufferStorage(GL_RENDERBUFFER_EXT, GL_RGBA,
-                                   width, height);
-      decompress->Width = width;
-      decompress->Height = height;
+   if (width > decompress_fbo->Width || height > decompress_fbo->Height) {
+      _mesa_BindRenderbuffer(GL_RENDERBUFFER_EXT, decompress_fbo->RBO);
+      _mesa_RenderbufferStorage(GL_RENDERBUFFER_EXT, rbFormat,
+                                width, height);
+      status = _mesa_CheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+      if (status != GL_FRAMEBUFFER_COMPLETE) {
+         /* If the framebuffer isn't complete then we'll leave
+          * decompress_fbo->Width as zero so that it will fail again next time
+          * too */
+         _mesa_meta_end(ctx);
+         return false;
+      }
+      decompress_fbo->Width = width;
+      decompress_fbo->Height = height;
    }
 
    if (use_glsl_version) {
@@ -3193,6 +3190,8 @@ decompress_texture_image(struct gl_context *ctx,
    _mesa_BindSampler(ctx->Texture.CurrentUnit, samplerSave);
 
    _mesa_meta_end(ctx);
+
+   return true;
 }
 
 
@@ -3206,15 +3205,11 @@ _mesa_meta_GetTexImage(struct gl_context *ctx,
                        GLenum format, GLenum type, GLvoid *pixels,
                        struct gl_texture_image *texImage)
 {
-   /* We can only use the decompress-with-blit method here if the texels are
-    * unsigned, normalized values.  We could handle signed and unnormalized 
-    * with floating point renderbuffers...
-    */
-   if (_mesa_is_format_compressed(texImage->TexFormat) &&
-       _mesa_get_format_datatype(texImage->TexFormat)
-       == GL_UNSIGNED_NORMALIZED) {
+   if (_mesa_is_format_compressed(texImage->TexFormat)) {
       struct gl_texture_object *texObj = texImage->TexObject;
       GLuint slice;
+      bool result;
+
       /* Need to unlock the texture here to prevent deadlock... */
       _mesa_unlock_texture(ctx, texObj);
       for (slice = 0; slice < texImage->Depth; slice++) {
@@ -3236,14 +3231,19 @@ _mesa_meta_GetTexImage(struct gl_context *ctx,
          else {
             dst = pixels;
          }
-         decompress_texture_image(ctx, texImage, slice, format, type, dst);
+         result = decompress_texture_image(ctx, texImage, slice,
+                                           format, type, dst);
+         if (!result)
+            break;
       }
       /* ... and relock it */
       _mesa_lock_texture(ctx, texObj);
+
+      if (result)
+         return;
    }
-   else {
-      _mesa_get_teximage(ctx, format, type, pixels, texImage);
-   }
+
+   _mesa_get_teximage(ctx, format, type, pixels, texImage);
 }
 
 
@@ -3371,3 +3371,192 @@ _mesa_meta_DrawTex(struct gl_context *ctx, GLfloat x, GLfloat y, GLfloat z,
 
    _mesa_meta_end(ctx);
 }
+
+static bool
+cleartexsubimage_color(struct gl_context *ctx,
+                       struct gl_texture_image *texImage,
+                       const GLvoid *clearValue,
+                       GLint zoffset)
+{
+   mesa_format format;
+   union gl_color_union colorValue;
+   GLenum datatype;
+   GLenum status;
+
+   _mesa_meta_bind_fbo_image(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                             texImage, zoffset);
+
+   status = _mesa_CheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+   if (status != GL_FRAMEBUFFER_COMPLETE)
+      return false;
+
+   /* We don't want to apply an sRGB conversion so override the format */
+   format = _mesa_get_srgb_format_linear(texImage->TexFormat);
+   datatype = _mesa_get_format_datatype(format);
+
+   switch (datatype) {
+   case GL_UNSIGNED_INT:
+   case GL_INT:
+      if (clearValue)
+         _mesa_unpack_uint_rgba_row(format, 1, clearValue,
+                                    (GLuint (*)[4]) colorValue.ui);
+      else
+         memset(&colorValue, 0, sizeof colorValue);
+      if (datatype == GL_INT)
+         _mesa_ClearBufferiv(GL_COLOR, 0, colorValue.i);
+      else
+         _mesa_ClearBufferuiv(GL_COLOR, 0, colorValue.ui);
+      break;
+   default:
+      if (clearValue)
+         _mesa_unpack_rgba_row(format, 1, clearValue,
+                               (GLfloat (*)[4]) colorValue.f);
+      else
+         memset(&colorValue, 0, sizeof colorValue);
+      _mesa_ClearBufferfv(GL_COLOR, 0, colorValue.f);
+      break;
+   }
+
+   return true;
+}
+
+static bool
+cleartexsubimage_depth_stencil(struct gl_context *ctx,
+                               struct gl_texture_image *texImage,
+                               const GLvoid *clearValue,
+                               GLint zoffset)
+{
+   GLint stencilValue;
+   GLfloat depthValue;
+   GLenum status;
+
+   _mesa_meta_bind_fbo_image(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                             texImage, zoffset);
+
+   if (texImage->_BaseFormat == GL_DEPTH_STENCIL)
+      _mesa_meta_bind_fbo_image(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+                                texImage, zoffset);
+
+   status = _mesa_CheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+   if (status != GL_FRAMEBUFFER_COMPLETE)
+      return false;
+
+   if (clearValue) {
+      GLuint depthStencilValue[2];
+
+      /* Convert the clearValue from whatever format it's in to a floating
+       * point value for the depth and an integer value for the stencil index
+       */
+      _mesa_unpack_float_32_uint_24_8_depth_stencil_row(texImage->TexFormat,
+                                                        1, /* n */
+                                                        clearValue,
+                                                        depthStencilValue);
+      /* We need a memcpy here instead of a cast because we need to
+       * reinterpret the bytes as a float rather than converting it
+       */
+      memcpy(&depthValue, depthStencilValue, sizeof depthValue);
+      stencilValue = depthStencilValue[1] & 0xff;
+   } else {
+      depthValue = 0.0f;
+      stencilValue = 0;
+   }
+
+   if (texImage->_BaseFormat == GL_DEPTH_STENCIL)
+      _mesa_ClearBufferfi(GL_DEPTH_STENCIL, 0, depthValue, stencilValue);
+   else
+      _mesa_ClearBufferfv(GL_DEPTH, 0, &depthValue);
+
+   return true;
+}
+
+static bool
+cleartexsubimage_for_zoffset(struct gl_context *ctx,
+                             struct gl_texture_image *texImage,
+                             GLint zoffset,
+                             const GLvoid *clearValue)
+{
+   GLuint fbo;
+   bool success;
+
+   _mesa_GenFramebuffers(1, &fbo);
+   _mesa_BindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+
+   switch(texImage->_BaseFormat) {
+   case GL_DEPTH_STENCIL:
+   case GL_DEPTH_COMPONENT:
+      success = cleartexsubimage_depth_stencil(ctx, texImage,
+                                               clearValue, zoffset);
+      break;
+   default:
+      success = cleartexsubimage_color(ctx, texImage, clearValue, zoffset);
+      break;
+   }
+
+   _mesa_DeleteFramebuffers(1, &fbo);
+
+   return success;
+}
+
+static bool
+cleartexsubimage_using_fbo(struct gl_context *ctx,
+                           struct gl_texture_image *texImage,
+                           GLint xoffset, GLint yoffset, GLint zoffset,
+                           GLsizei width, GLsizei height, GLsizei depth,
+                           const GLvoid *clearValue)
+{
+   bool success = true;
+   GLint z;
+
+   _mesa_meta_begin(ctx,
+                    MESA_META_SCISSOR |
+                    MESA_META_COLOR_MASK |
+                    MESA_META_DITHER |
+                    MESA_META_FRAMEBUFFER_SRGB);
+
+   _mesa_set_enable(ctx, GL_DITHER, GL_FALSE);
+
+   _mesa_set_enable(ctx, GL_SCISSOR_TEST, GL_TRUE);
+   _mesa_Scissor(xoffset, yoffset, width, height);
+
+   for (z = zoffset; z < zoffset + depth; z++) {
+      if (!cleartexsubimage_for_zoffset(ctx, texImage, z, clearValue)) {
+         success = false;
+         break;
+      }
+   }
+
+   _mesa_meta_end(ctx);
+
+   return success;
+}
+
+extern void
+_mesa_meta_ClearTexSubImage(struct gl_context *ctx,
+                            struct gl_texture_image *texImage,
+                            GLint xoffset, GLint yoffset, GLint zoffset,
+                            GLsizei width, GLsizei height, GLsizei depth,
+                            const GLvoid *clearValue)
+{
+   bool res;
+
+   _mesa_unlock_texture(ctx, texImage->TexObject);
+
+   res = cleartexsubimage_using_fbo(ctx, texImage,
+                                    xoffset, yoffset, zoffset,
+                                    width, height, depth,
+                                    clearValue);
+
+   _mesa_lock_texture(ctx, texImage->TexObject);
+
+   if (res)
+      return;
+
+   _mesa_warning(ctx,
+                 "Falling back to mapping the texture in "
+                 "glClearTexSubImage\n");
+
+   _mesa_store_cleartexsubimage(ctx, texImage,
+                                xoffset, yoffset, zoffset,
+                                width, height, depth,
+                                clearValue);
+}