mesa: Move declaration to top of block.
[mesa.git] / src / mesa / drivers / common / meta_blit.c
index d1c40f50c52ec5227ab554ca8588fb21cfc062f7..707269dd687073bad3dcc1c0307f70e38d2e99ab 100644 (file)
 #include "main/enable.h"
 #include "main/enums.h"
 #include "main/fbobject.h"
+#include "main/image.h"
 #include "main/macros.h"
 #include "main/matrix.h"
 #include "main/multisample.h"
 #include "main/objectlabel.h"
 #include "main/readpix.h"
+#include "main/scissor.h"
 #include "main/shaderapi.h"
 #include "main/texobj.h"
 #include "main/texenv.h"
@@ -60,13 +62,14 @@ setup_glsl_msaa_blit_shader(struct gl_context *ctx,
 {
    const char *vs_source;
    char *fs_source;
-   GLuint vs, fs;
    void *mem_ctx;
    enum blit_msaa_shader shader_index;
    bool dst_is_msaa = false;
    GLenum src_datatype;
    const char *vec4_prefix;
+   const char *sampler_array_suffix = "";
    char *name;
+   const char *texcoord_type = "vec2";
 
    if (src_rb) {
       src_datatype = _mesa_get_format_datatype(src_rb->Format);
@@ -94,6 +97,7 @@ setup_glsl_msaa_blit_shader(struct gl_context *ctx,
 
    switch (target) {
    case GL_TEXTURE_2D_MULTISAMPLE:
+   case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
       if (src_rb->_BaseFormat == GL_DEPTH_COMPONENT ||
           src_rb->_BaseFormat == GL_DEPTH_STENCIL) {
          if (dst_is_msaa)
@@ -106,6 +110,13 @@ setup_glsl_msaa_blit_shader(struct gl_context *ctx,
          else
             shader_index = BLIT_MSAA_SHADER_2D_MULTISAMPLE_RESOLVE;
       }
+
+      if (target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) {
+         shader_index += (BLIT_MSAA_SHADER_2D_MULTISAMPLE_ARRAY_RESOLVE -
+                          BLIT_MSAA_SHADER_2D_MULTISAMPLE_RESOLVE);
+         sampler_array_suffix = "Array";
+         texcoord_type = "vec3";
+      }
       break;
    default:
       _mesa_problem(ctx, "Unkown texture target %s\n",
@@ -136,6 +147,8 @@ setup_glsl_msaa_blit_shader(struct gl_context *ctx,
    mem_ctx = ralloc_context(NULL);
 
    if (shader_index == BLIT_MSAA_SHADER_2D_MULTISAMPLE_DEPTH_RESOLVE ||
+       shader_index == BLIT_MSAA_SHADER_2D_MULTISAMPLE_ARRAY_DEPTH_RESOLVE ||
+       shader_index == BLIT_MSAA_SHADER_2D_MULTISAMPLE_ARRAY_DEPTH_COPY ||
        shader_index == BLIT_MSAA_SHADER_2D_MULTISAMPLE_DEPTH_COPY) {
       char *sample_index;
       const char *arb_sample_shading_extension_string;
@@ -166,26 +179,31 @@ setup_glsl_msaa_blit_shader(struct gl_context *ctx,
       vs_source = ralloc_asprintf(mem_ctx,
                                   "#version 130\n"
                                   "in vec2 position;\n"
-                                  "in vec2 textureCoords;\n"
-                                  "out vec2 texCoords;\n"
+                                  "in %s textureCoords;\n"
+                                  "out %s texCoords;\n"
                                   "void main()\n"
                                   "{\n"
                                   "   texCoords = textureCoords;\n"
                                   "   gl_Position = vec4(position, 0.0, 1.0);\n"
-                                  "}\n");
+                                  "}\n",
+                                  texcoord_type,
+                                  texcoord_type);
       fs_source = ralloc_asprintf(mem_ctx,
                                   "#version 130\n"
                                   "#extension GL_ARB_texture_multisample : enable\n"
                                   "%s\n"
-                                  "uniform sampler2DMS texSampler;\n"
-                                  "in vec2 texCoords;\n"
+                                  "uniform sampler2DMS%s texSampler;\n"
+                                  "in %s texCoords;\n"
                                   "out vec4 out_color;\n"
                                   "\n"
                                   "void main()\n"
                                   "{\n"
-                                  "   gl_FragDepth = texelFetch(texSampler, ivec2(texCoords), %s).r;\n"
+                                  "   gl_FragDepth = texelFetch(texSampler, i%s(texCoords), %s).r;\n"
                                   "}\n",
                                   arb_sample_shading_extension_string,
+                                  sampler_array_suffix,
+                                  texcoord_type,
+                                  texcoord_type,
                                   sample_index);
    } else {
       /* You can create 2D_MULTISAMPLE textures with 0 sample count (meaning 1
@@ -203,7 +221,7 @@ setup_glsl_msaa_blit_shader(struct gl_context *ctx,
 
       if (dst_is_msaa) {
          arb_sample_shading_extension_string = "#extension GL_ARB_sample_shading : enable";
-         sample_resolve = ralloc_asprintf(mem_ctx, "   out_color = texelFetch(texSampler, ivec2(texCoords), gl_SampleID);");
+         sample_resolve = ralloc_asprintf(mem_ctx, "   out_color = texelFetch(texSampler, i%s(texCoords), gl_SampleID);", texcoord_type);
          merge_function = "";
       } else {
          int i;
@@ -232,8 +250,8 @@ setup_glsl_msaa_blit_shader(struct gl_context *ctx,
          sample_resolve = rzalloc_size(mem_ctx, 1);
          for (i = 0; i < samples; i++) {
             ralloc_asprintf_append(&sample_resolve,
-                                   "   gvec4 sample_1_%d = texelFetch(texSampler, ivec2(texCoords), %d);\n",
-                                   i, i);
+                                   "   gvec4 sample_1_%d = texelFetch(texSampler, i%s(texCoords), %d);\n",
+                                   i, texcoord_type, i);
          }
          /* Now, merge each pair of samples, then merge each pair of those,
           * etc.
@@ -263,20 +281,22 @@ setup_glsl_msaa_blit_shader(struct gl_context *ctx,
       vs_source = ralloc_asprintf(mem_ctx,
                                   "#version 130\n"
                                   "in vec2 position;\n"
-                                  "in vec2 textureCoords;\n"
-                                  "out vec2 texCoords;\n"
+                                  "in %s textureCoords;\n"
+                                  "out %s texCoords;\n"
                                   "void main()\n"
                                   "{\n"
                                   "   texCoords = textureCoords;\n"
                                   "   gl_Position = vec4(position, 0.0, 1.0);\n"
-                                  "}\n");
+                                  "}\n",
+                                  texcoord_type,
+                                  texcoord_type);
       fs_source = ralloc_asprintf(mem_ctx,
                                   "#version 130\n"
                                   "#extension GL_ARB_texture_multisample : enable\n"
                                   "%s\n"
                                   "#define gvec4 %svec4\n"
-                                  "uniform %ssampler2DMS texSampler;\n"
-                                  "in vec2 texCoords;\n"
+                                  "uniform %ssampler2DMS%s texSampler;\n"
+                                  "in %s texCoords;\n"
                                   "out gvec4 out_color;\n"
                                   "\n"
                                   "%s" /* merge_function */
@@ -287,25 +307,16 @@ setup_glsl_msaa_blit_shader(struct gl_context *ctx,
                                   arb_sample_shading_extension_string,
                                   vec4_prefix,
                                   vec4_prefix,
+                                  sampler_array_suffix,
+                                  texcoord_type,
                                   merge_function,
                                   sample_resolve);
    }
 
-   vs = _mesa_meta_compile_shader_with_debug(ctx, GL_VERTEX_SHADER, vs_source);
-   fs = _mesa_meta_compile_shader_with_debug(ctx, GL_FRAGMENT_SHADER, fs_source);
-
-   blit->msaa_shaders[shader_index] = _mesa_CreateProgram();
-   _mesa_AttachShader(blit->msaa_shaders[shader_index], fs);
-   _mesa_DeleteShader(fs);
-   _mesa_AttachShader(blit->msaa_shaders[shader_index], vs);
-   _mesa_DeleteShader(vs);
-   _mesa_BindAttribLocation(blit->msaa_shaders[shader_index], 0, "position");
-   _mesa_BindAttribLocation(blit->msaa_shaders[shader_index], 1, "texcoords");
-   _mesa_meta_link_program_with_debug(ctx, blit->msaa_shaders[shader_index]);
-   _mesa_ObjectLabel(GL_PROGRAM, blit->msaa_shaders[shader_index], -1, name);
-   ralloc_free(mem_ctx);
+   _mesa_meta_compile_and_link_program(ctx, vs_source, fs_source, name,
+                                       &blit->msaa_shaders[shader_index]);
 
-   _mesa_UseProgram(blit->msaa_shaders[shader_index]);
+   ralloc_free(mem_ctx);
 }
 
 static void
@@ -314,12 +325,18 @@ setup_glsl_blit_framebuffer(struct gl_context *ctx,
                             struct gl_renderbuffer *src_rb,
                             GLenum target)
 {
+   unsigned texcoord_size;
+
    /* target = GL_TEXTURE_RECTANGLE is not supported in GLES 3.0 */
    assert(_mesa_is_desktop_gl(ctx) || target == GL_TEXTURE_2D);
 
-   _mesa_meta_setup_vertex_objects(&blit->VAO, &blit->VBO, true, 2, 2, 0);
+   texcoord_size = 2 + (src_rb->Depth > 1 ? 1 : 0);
+
+   _mesa_meta_setup_vertex_objects(&blit->VAO, &blit->VBO, true,
+                                   2, texcoord_size, 0);
 
-   if (target == GL_TEXTURE_2D_MULTISAMPLE) {
+   if (target == GL_TEXTURE_2D_MULTISAMPLE ||
+       target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) {
       setup_glsl_msaa_blit_shader(ctx, blit, src_rb, target);
    } else {
       _mesa_meta_setup_blit_shader(ctx, target, &blit->shaders);
@@ -344,19 +361,14 @@ blitframebuffer_texture(struct gl_context *ctx,
    const struct gl_renderbuffer_attachment *readAtt =
       &readFb->Attachment[att_index];
    struct blit_state *blit = &ctx->Meta->Blit;
+   struct fb_tex_blit_state fb_tex_blit;
    const GLint dstX = MIN2(dstX0, dstX1);
    const GLint dstY = MIN2(dstY0, dstY1);
    const GLint dstW = abs(dstX1 - dstX0);
    const GLint dstH = abs(dstY1 - dstY0);
    struct gl_texture_object *texObj;
    GLuint srcLevel;
-   GLint baseLevelSave;
-   GLint maxLevelSave;
    GLenum target;
-   GLuint sampler, samplerSave =
-      ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler ?
-      ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler->Name : 0;
-   GLuint tempTex = 0;
    struct gl_renderbuffer *rb = readAtt->Renderbuffer;
    struct temp_texture *meta_temp_texture;
 
@@ -368,10 +380,13 @@ blitframebuffer_texture(struct gl_context *ctx,
       filter = GL_LINEAR;
    }
 
+   _mesa_meta_fb_tex_blit_begin(ctx, &fb_tex_blit);
+
    if (readAtt->Texture &&
        (readAtt->Texture->Target == GL_TEXTURE_2D ||
         readAtt->Texture->Target == GL_TEXTURE_RECTANGLE ||
-        readAtt->Texture->Target == GL_TEXTURE_2D_MULTISAMPLE)) {
+        readAtt->Texture->Target == GL_TEXTURE_2D_MULTISAMPLE ||
+        readAtt->Texture->Target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)) {
       /* If there's a texture attached of a type we can handle, then just use
        * it directly.
        */
@@ -379,38 +394,16 @@ blitframebuffer_texture(struct gl_context *ctx,
       texObj = readAtt->Texture;
       target = texObj->Target;
    } else if (!readAtt->Texture && ctx->Driver.BindRenderbufferTexImage) {
-      /* Otherwise, we need the driver to be able to bind a renderbuffer as
-       * a texture image.
-       */
-      struct gl_texture_image *texImage;
-
-      if (rb->NumSamples > 1)
-         target = GL_TEXTURE_2D_MULTISAMPLE;
-      else
-         target = GL_TEXTURE_2D;
-
-      _mesa_GenTextures(1, &tempTex);
-      _mesa_BindTexture(target, tempTex);
-      srcLevel = 0;
-      texObj = _mesa_lookup_texture(ctx, tempTex);
-      texImage = _mesa_get_tex_image(ctx, texObj, target, srcLevel);
-
-      if (!ctx->Driver.BindRenderbufferTexImage(ctx, rb, texImage)) {
-         _mesa_DeleteTextures(1, &tempTex);
+      if (!_mesa_meta_bind_rb_as_tex_image(ctx, rb, &fb_tex_blit.tempTex,
+                                           &texObj, &target))
          return false;
-      } else {
-         if (ctx->Driver.FinishRenderTexture &&
-             !rb->NeedsFinishRenderTexture) {
-            rb->NeedsFinishRenderTexture = true;
-            ctx->Driver.FinishRenderTexture(ctx, rb);
-         }
 
-         if (_mesa_is_winsys_fbo(readFb)) {
-            GLint temp = srcY0;
-            srcY0 = rb->Height - srcY1;
-            srcY1 = rb->Height - temp;
-            flipY = -flipY;
-         }
+      srcLevel = 0;
+      if (_mesa_is_winsys_fbo(readFb)) {
+         GLint temp = srcY0;
+         srcY0 = rb->Height - srcY1;
+         srcY1 = rb->Height - temp;
+         flipY = -flipY;
       }
    } else {
       GLenum tex_base_format;
@@ -419,6 +412,9 @@ blitframebuffer_texture(struct gl_context *ctx,
       /* Fall back to doing a CopyTexSubImage to get the destination
        * renderbuffer into a texture.
        */
+      if (ctx->Meta->Blit.no_ctsi_fallback)
+         return false;
+
       if (rb->NumSamples > 1)
          return false;
 
@@ -448,8 +444,8 @@ blitframebuffer_texture(struct gl_context *ctx,
       srcY1 = srcH;
    }
 
-   baseLevelSave = texObj->BaseLevel;
-   maxLevelSave = texObj->MaxLevel;
+   fb_tex_blit.baseLevelSave = texObj->BaseLevel;
+   fb_tex_blit.maxLevelSave = texObj->MaxLevel;
 
    if (glsl_version) {
       setup_glsl_blit_framebuffer(ctx, blit, rb, target);
@@ -460,25 +456,14 @@ blitframebuffer_texture(struct gl_context *ctx,
                                        2);
    }
 
-   _mesa_GenSamplers(1, &sampler);
-   _mesa_BindSampler(ctx->Texture.CurrentUnit, sampler);
-
    /*
      printf("Blit from texture!\n");
      printf("  srcAtt %p  dstAtt %p\n", readAtt, drawAtt);
      printf("  srcTex %p  dstText %p\n", texObj, drawAtt->Texture);
    */
 
-   /* Prepare src texture state */
-   _mesa_BindTexture(target, texObj->Name);
-   _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_SamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-   _mesa_SamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+   fb_tex_blit.sampler = _mesa_meta_setup_sampler(ctx, texObj, target, filter,
+                                                  srcLevel);
 
    /* Always do our blits with no net sRGB decode or encode.
     *
@@ -499,11 +484,12 @@ blitframebuffer_texture(struct gl_context *ctx,
    if (ctx->Extensions.EXT_texture_sRGB_decode) {
       if (_mesa_get_format_color_encoding(rb->Format) == GL_SRGB &&
           ctx->DrawBuffer->Visual.sRGBCapable) {
-         _mesa_SamplerParameteri(sampler, GL_TEXTURE_SRGB_DECODE_EXT,
-                                 GL_DECODE_EXT);
+         _mesa_SamplerParameteri(fb_tex_blit.sampler,
+                                 GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT);
          _mesa_set_framebuffer_srgb(ctx, GL_TRUE);
       } else {
-         _mesa_SamplerParameteri(sampler, GL_TEXTURE_SRGB_DECODE_EXT,
+         _mesa_SamplerParameteri(fb_tex_blit.sampler,
+                                 GL_TEXTURE_SRGB_DECODE_EXT,
                                  GL_SKIP_DECODE_EXT);
          /* set_framebuffer_srgb was set by _mesa_meta_begin(). */
       }
@@ -529,7 +515,8 @@ blitframebuffer_texture(struct gl_context *ctx,
       }
       else {
          assert(target == GL_TEXTURE_RECTANGLE_ARB ||
-                target == GL_TEXTURE_2D_MULTISAMPLE);
+                target == GL_TEXTURE_2D_MULTISAMPLE ||
+                target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY);
          s0 = (float) srcX0;
          s1 = (float) srcX1;
          t0 = (float) srcY0;
@@ -551,12 +538,16 @@ blitframebuffer_texture(struct gl_context *ctx,
 
       verts[0].tex[0] = s0;
       verts[0].tex[1] = t0;
+      verts[0].tex[2] = readAtt->Zoffset;
       verts[1].tex[0] = s1;
       verts[1].tex[1] = t0;
+      verts[1].tex[2] = readAtt->Zoffset;
       verts[2].tex[0] = s1;
       verts[2].tex[1] = t1;
+      verts[2].tex[2] = readAtt->Zoffset;
       verts[3].tex[0] = s0;
       verts[3].tex[1] = t1;
+      verts[3].tex[2] = readAtt->Zoffset;
 
       _mesa_BufferSubData(GL_ARRAY_BUFFER_ARB, 0, sizeof(verts), verts);
    }
@@ -569,28 +560,100 @@ blitframebuffer_texture(struct gl_context *ctx,
    _mesa_DepthFunc(GL_ALWAYS);
 
    _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
+   _mesa_meta_fb_tex_blit_end(ctx, target, &fb_tex_blit);
 
+   return true;
+}
+
+void
+_mesa_meta_fb_tex_blit_begin(const struct gl_context *ctx,
+                             struct fb_tex_blit_state *blit)
+{
+   blit->samplerSave =
+      ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler ?
+      ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler->Name : 0;
+   blit->tempTex = 0;
+}
+
+void
+_mesa_meta_fb_tex_blit_end(const struct gl_context *ctx, GLenum target,
+                           struct fb_tex_blit_state *blit)
+{
    /* Restore texture object state, the texture binding will
     * be restored by _mesa_meta_end().
     */
    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_BASE_LEVEL, blit->baseLevelSave);
+      _mesa_TexParameteri(target, GL_TEXTURE_MAX_LEVEL, blit->maxLevelSave);
    }
 
-   _mesa_BindSampler(ctx->Texture.CurrentUnit, samplerSave);
-   _mesa_DeleteSamplers(1, &sampler);
-   if (tempTex)
-      _mesa_DeleteTextures(1, &tempTex);
+   _mesa_BindSampler(ctx->Texture.CurrentUnit, blit->samplerSave);
+   _mesa_DeleteSamplers(1, &blit->sampler);
+   if (blit->tempTex)
+      _mesa_DeleteTextures(1, &blit->tempTex);
+}
+
+GLboolean
+_mesa_meta_bind_rb_as_tex_image(struct gl_context *ctx,
+                                struct gl_renderbuffer *rb,
+                                GLuint *tex,
+                                struct gl_texture_object **texObj,
+                                GLenum *target)
+{
+   struct gl_texture_image *texImage;
+
+   if (rb->NumSamples > 1)
+      *target = GL_TEXTURE_2D_MULTISAMPLE;
+   else
+      *target = GL_TEXTURE_2D;
+
+   _mesa_GenTextures(1, tex);
+   _mesa_BindTexture(*target, *tex);
+   *texObj = _mesa_lookup_texture(ctx, *tex);
+   texImage = _mesa_get_tex_image(ctx, *texObj, *target, 0);
+
+   if (!ctx->Driver.BindRenderbufferTexImage(ctx, rb, texImage)) {
+      _mesa_DeleteTextures(1, tex);
+      return false;
+   }
+
+   if (ctx->Driver.FinishRenderTexture && !rb->NeedsFinishRenderTexture) {
+      rb->NeedsFinishRenderTexture = true;
+      ctx->Driver.FinishRenderTexture(ctx, rb);
+   }
 
    return true;
 }
 
+GLuint
+_mesa_meta_setup_sampler(struct gl_context *ctx,
+                         const struct gl_texture_object *texObj,
+                         GLenum target, GLenum filter, GLuint srcLevel)
+{
+   GLuint sampler;
+
+   _mesa_GenSamplers(1, &sampler);
+   _mesa_BindSampler(ctx->Texture.CurrentUnit, sampler);
+
+   /* Prepare src texture state */
+   _mesa_BindTexture(target, texObj->Name);
+   _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_SamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+   _mesa_SamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+   return sampler;
+}
+
 /**
  * Meta implementation of ctx->Driver.BlitFramebuffer() in terms
  * of texture mapping and polygon rendering.
  */
-void
+GLbitfield
 _mesa_meta_BlitFramebuffer(struct gl_context *ctx,
                            GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                            GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
@@ -600,17 +663,49 @@ _mesa_meta_BlitFramebuffer(struct gl_context *ctx,
    const GLint dstH = abs(dstY1 - dstY0);
    const GLint dstFlipX = (dstX1 - dstX0) / dstW;
    const GLint dstFlipY = (dstY1 - dstY0) / dstH;
+
+   struct {
+      GLint srcX0, srcY0, srcX1, srcY1;
+      GLint dstX0, dstY0, dstX1, dstY1;
+   } clip = {
+      srcX0, srcY0, srcX1, srcY1,
+      dstX0, dstY0, dstX1, dstY1
+   };
+
    const GLboolean use_glsl_version = ctx->Extensions.ARB_vertex_shader &&
                                       ctx->Extensions.ARB_fragment_shader;
 
    /* Multisample texture blit support requires texture multisample. */
    if (ctx->ReadBuffer->Visual.samples > 0 &&
        !ctx->Extensions.ARB_texture_multisample) {
-      goto fallback;
+      return mask;
+   }
+
+   /* Clip a copy of the blit coordinates. If these differ from the input
+    * coordinates, then we'll set the scissor.
+    */
+   if (!_mesa_clip_blit(ctx, &clip.srcX0, &clip.srcY0, &clip.srcX1, &clip.srcY1,
+                        &clip.dstX0, &clip.dstY0, &clip.dstX1, &clip.dstY1)) {
+      /* clipped/scissored everything away */
+      return 0;
    }
 
-   /* only scissor effects blit so save/clear all other relevant state */
-   _mesa_meta_begin(ctx, ~MESA_META_SCISSOR);
+   /* Only scissor affects blit, but we're doing to set a custom scissor if
+    * necessary anyway, so save/clear state.
+    */
+   _mesa_meta_begin(ctx, MESA_META_ALL & ~MESA_META_DRAW_BUFFERS);
+
+   /* If the clipping earlier changed the destination rect at all, then
+    * enable the scissor to clip to it.
+    */
+   if (clip.dstX0 != dstX0 || clip.dstY0 != dstY0 ||
+       clip.dstX1 != dstX1 || clip.dstY1 != dstY1) {
+      _mesa_set_enable(ctx, GL_SCISSOR_TEST, GL_TRUE);
+      _mesa_Scissor(MIN2(clip.dstX0, clip.dstX1),
+                    MIN2(clip.dstY0, clip.dstY1),
+                    abs(clip.dstX0 - clip.dstX1),
+                    abs(clip.dstY0 - clip.dstY1));
+   }
 
    /* Try faster, direct texture approach first */
    if (mask & GL_COLOR_BUFFER_BIT) {
@@ -619,10 +714,6 @@ _mesa_meta_BlitFramebuffer(struct gl_context *ctx,
                                   filter, dstFlipX, dstFlipY,
                                   use_glsl_version, false)) {
          mask &= ~GL_COLOR_BUFFER_BIT;
-         if (mask == 0x0) {
-            _mesa_meta_end(ctx);
-            return;
-         }
       }
    }
 
@@ -632,10 +723,6 @@ _mesa_meta_BlitFramebuffer(struct gl_context *ctx,
                                   filter, dstFlipX, dstFlipY,
                                   use_glsl_version, true)) {
          mask &= ~GL_DEPTH_BUFFER_BIT;
-         if (mask == 0x0) {
-            _mesa_meta_end(ctx);
-            return;
-         }
       }
    }
 
@@ -645,11 +732,7 @@ _mesa_meta_BlitFramebuffer(struct gl_context *ctx,
 
    _mesa_meta_end(ctx);
 
-fallback:
-   if (mask) {
-      _swrast_BlitFramebuffer(ctx, srcX0, srcY0, srcX1, srcY1,
-                              dstX0, dstY0, dstX1, dstY1, mask, filter);
-   }
+   return mask;
 }
 
 void
@@ -667,3 +750,24 @@ _mesa_meta_glsl_blit_cleanup(struct blit_state *blit)
    _mesa_DeleteTextures(1, &blit->depthTex.TexObj);
    blit->depthTex.TexObj = 0;
 }
+
+void
+_mesa_meta_and_swrast_BlitFramebuffer(struct gl_context *ctx,
+                                      GLint srcX0, GLint srcY0,
+                                      GLint srcX1, GLint srcY1,
+                                      GLint dstX0, GLint dstY0,
+                                      GLint dstX1, GLint dstY1,
+                                      GLbitfield mask, GLenum filter)
+{
+   mask = _mesa_meta_BlitFramebuffer(ctx,
+                                     srcX0, srcY0, srcX1, srcY1,
+                                     dstX0, dstY0, dstX1, dstY1,
+                                     mask, filter);
+   if (mask == 0x0)
+      return;
+
+   _swrast_BlitFramebuffer(ctx,
+                           srcX0, srcY0, srcX1, srcY1,
+                           dstX0, dstY0, dstX1, dstY1,
+                           mask, filter);
+}