Refactored validation to glBlitFramebufferANGLE and implemented glBlitFramebuffer.

TRAC #23211

Signed-off-by: Jamie Madill
Signed-off-by: Shannon Woods
Author: Geoff Lang
diff --git a/src/libGLESv2/Context.cpp b/src/libGLESv2/Context.cpp
index 007e9f3..fae6008 100644
--- a/src/libGLESv2/Context.cpp
+++ b/src/libGLESv2/Context.cpp
@@ -3041,31 +3041,24 @@
     return mRendererString;
 }
 
-void Context::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, 
-                              GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
-                              GLbitfield mask)
+bool Context::clipBlitFramebufferCoordinates(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+                                             GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+                                             gl::Rectangle *outSourceRect, gl::Rectangle *outDestRect,
+                                             bool *outPartialCopy)
 {
     Framebuffer *readFramebuffer = getReadFramebuffer();
     Framebuffer *drawFramebuffer = getDrawFramebuffer();
-
     if (!readFramebuffer || readFramebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE ||
         !drawFramebuffer || drawFramebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
     {
-        return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION);
-    }
-
-    if (drawFramebuffer->getSamples() != 0)
-    {
-        return gl::error(GL_INVALID_OPERATION);
+        return false;
     }
 
     Renderbuffer *readColorBuffer = readFramebuffer->getReadColorbuffer();
     Renderbuffer *drawColorBuffer = drawFramebuffer->getFirstColorbuffer();
-
-    if (drawColorBuffer == NULL)
+    if (!readColorBuffer || !drawColorBuffer)
     {
-        ERR("Draw buffers formats don't match, which is not supported in this implementation of BlitFramebufferANGLE");
-        return gl::error(GL_INVALID_OPERATION);
+        return false;
     }
 
     int readBufferWidth = readColorBuffer->getWidth();
@@ -3073,8 +3066,8 @@
     int drawBufferWidth = drawColorBuffer->getWidth();
     int drawBufferHeight = drawColorBuffer->getHeight();
 
-    Rectangle sourceRect;
-    Rectangle destRect;
+    gl::Rectangle sourceRect;
+    gl::Rectangle destRect;
 
     if (srcX0 < srcX1)
     {
@@ -3101,7 +3094,7 @@
     else
     {
         sourceRect.height = srcY0 - srcY1;
-        destRect.height = dstY0 - srcY1;
+        destRect.height = dstY0 - dstY1;
         sourceRect.y = srcY1;
         destRect.y = dstY1;
     }
@@ -3119,7 +3112,6 @@
             destScissoredRect.width -= xDiff;
             sourceScissoredRect.x += xDiff;
             sourceScissoredRect.width -= xDiff;
-
         }
 
         if (destRect.x + destRect.width > mState.scissor.x + mState.scissor.width)
@@ -3146,9 +3138,6 @@
         }
     }
 
-    bool blitRenderTarget = false;
-    bool blitDepthStencil = false;
-
     Rectangle sourceTrimmedRect = sourceScissoredRect;
     Rectangle destTrimmedRect = destScissoredRect;
 
@@ -3218,111 +3207,52 @@
         sourceTrimmedRect.height -= yDiff;
     }
 
-    bool partialBufferCopy = false;
-    if (sourceTrimmedRect.height < readBufferHeight ||
-        sourceTrimmedRect.width < readBufferWidth || 
-        destTrimmedRect.height < drawBufferHeight ||
-        destTrimmedRect.width < drawBufferWidth ||
-        sourceTrimmedRect.y != 0 || destTrimmedRect.y != 0 || sourceTrimmedRect.x != 0 || destTrimmedRect.x != 0)
+    *outSourceRect = sourceTrimmedRect;
+    *outDestRect = destTrimmedRect;
+
+    *outPartialCopy = sourceTrimmedRect.height < readBufferHeight ||
+                      sourceTrimmedRect.width < readBufferWidth ||
+                      destTrimmedRect.height < drawBufferHeight ||
+                      destTrimmedRect.width < drawBufferWidth ||
+                      sourceTrimmedRect.x != 0 || destTrimmedRect.x != 0 ||
+                      sourceTrimmedRect.y != 0 || destTrimmedRect.y != 0;
+
+    return true;
+}
+
+void Context::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+                              GLbitfield mask, GLenum filter)
+{
+    Framebuffer *readFramebuffer = getReadFramebuffer();
+    Framebuffer *drawFramebuffer = getDrawFramebuffer();
+
+    bool blitRenderTarget = false;
+    bool blitDepthStencil = false;
+    if ((mask & GL_COLOR_BUFFER_BIT) && readFramebuffer->getReadColorbuffer() && drawFramebuffer->getFirstColorbuffer())
     {
-        partialBufferCopy = true;
-    }
-
-    if (mask & GL_COLOR_BUFFER_BIT)
-    {
-        const GLenum readColorbufferType = readFramebuffer->getReadColorbufferType();
-        const bool validReadType = (readColorbufferType == GL_TEXTURE_2D) || (readColorbufferType == GL_RENDERBUFFER);
-        bool validDrawType = true;
-        bool validDrawFormat = true;
-
-        for (unsigned int colorAttachment = 0; colorAttachment < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
-        {
-            if (drawFramebuffer->isEnabledColorAttachment(colorAttachment))
-            {
-                if (drawFramebuffer->getColorbufferType(colorAttachment) != GL_TEXTURE_2D &&
-                    drawFramebuffer->getColorbufferType(colorAttachment) != GL_RENDERBUFFER)
-                {
-                    validDrawType = false;
-                }
-
-                if (drawFramebuffer->getColorbuffer(colorAttachment)->getActualFormat() != readColorBuffer->getActualFormat())
-                {
-                    validDrawFormat = false;
-                }
-            }
-        }
-
-        if (!validReadType || !validDrawType || !validDrawFormat)
-        {
-            ERR("Color buffer format conversion in BlitFramebufferANGLE not supported by this implementation");
-            return gl::error(GL_INVALID_OPERATION);
-        }
-        
-        if (partialBufferCopy && readFramebuffer->getSamples() != 0)
-        {
-            return gl::error(GL_INVALID_OPERATION);
-        }
-
         blitRenderTarget = true;
-
+    }
+    if ((mask & GL_STENCIL_BUFFER_BIT) && readFramebuffer->getStencilbuffer() && drawFramebuffer->getStencilbuffer())
+    {
+        blitDepthStencil = true;
+    }
+    if ((mask & GL_DEPTH_BUFFER_BIT) && readFramebuffer->getDepthbuffer() && drawFramebuffer->getDepthbuffer())
+    {
+        blitDepthStencil = true;
     }
 
-    if (mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT))
+    gl::Rectangle sourceClippedRect, destClippedRect;
+    bool partialCopy;
+    if (!clipBlitFramebufferCoordinates(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1,
+                                        &sourceClippedRect, &destClippedRect, &partialCopy))
     {
-        Renderbuffer *readDSBuffer = NULL;
-        Renderbuffer *drawDSBuffer = NULL;
-
-        // We support OES_packed_depth_stencil, and do not support a separately attached depth and stencil buffer, so if we have
-        // both a depth and stencil buffer, it will be the same buffer.
-
-        if (mask & GL_DEPTH_BUFFER_BIT)
-        {
-            if (readFramebuffer->getDepthbuffer() && drawFramebuffer->getDepthbuffer())
-            {
-                if (readFramebuffer->getDepthbufferType() != drawFramebuffer->getDepthbufferType() ||
-                    readFramebuffer->getDepthbuffer()->getActualFormat() != drawFramebuffer->getDepthbuffer()->getActualFormat())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                blitDepthStencil = true;
-                readDSBuffer = readFramebuffer->getDepthbuffer();
-                drawDSBuffer = drawFramebuffer->getDepthbuffer();
-            }
-        }
-
-        if (mask & GL_STENCIL_BUFFER_BIT)
-        {
-            if (readFramebuffer->getStencilbuffer() && drawFramebuffer->getStencilbuffer())
-            {
-                if (readFramebuffer->getStencilbufferType() != drawFramebuffer->getStencilbufferType() ||
-                    readFramebuffer->getStencilbuffer()->getActualFormat() != drawFramebuffer->getStencilbuffer()->getActualFormat())
-                {
-                    return gl::error(GL_INVALID_OPERATION);
-                }
-
-                blitDepthStencil = true;
-                readDSBuffer = readFramebuffer->getStencilbuffer();
-                drawDSBuffer = drawFramebuffer->getStencilbuffer();
-            }
-        }
-
-        if (partialBufferCopy)
-        {
-            ERR("Only whole-buffer depth and stencil blits are supported by this implementation.");
-            return gl::error(GL_INVALID_OPERATION); // only whole-buffer copies are permitted
-        }
-
-        if ((drawDSBuffer && drawDSBuffer->getSamples() != 0) || 
-            (readDSBuffer && readDSBuffer->getSamples() != 0))
-        {
-            return gl::error(GL_INVALID_OPERATION);
-        }
+        return;
     }
 
     if (blitRenderTarget || blitDepthStencil)
     {
-        mRenderer->blitRect(readFramebuffer, sourceTrimmedRect, drawFramebuffer, destTrimmedRect, blitRenderTarget, blitDepthStencil);
+        mRenderer->blitRect(readFramebuffer, sourceClippedRect, drawFramebuffer, destClippedRect,
+                            blitRenderTarget, blitDepthStencil, filter);
     }
 }
 
diff --git a/src/libGLESv2/Context.h b/src/libGLESv2/Context.h
index fbacb8f..f001905 100644
--- a/src/libGLESv2/Context.h
+++ b/src/libGLESv2/Context.h
@@ -462,9 +462,13 @@
 
     float getTextureMaxAnisotropy() const;
 
-    void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, 
-                         GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
-                         GLbitfield mask);
+    bool clipBlitFramebufferCoordinates(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+                                        GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+                                        gl::Rectangle *outSourceRect, gl::Rectangle *outDestRect,
+                                        bool *outPartialCopy);
+
+    void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+                         GLbitfield mask, GLenum filter);
 
     void invalidateFrameBuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments,
                                GLint x, GLint y, GLsizei width, GLsizei height);
diff --git a/src/libGLESv2/libGLESv2.cpp b/src/libGLESv2/libGLESv2.cpp
index d5b5e5b..740c9b0 100644
--- a/src/libGLESv2/libGLESv2.cpp
+++ b/src/libGLESv2/libGLESv2.cpp
@@ -1680,6 +1680,238 @@
     return true;
 }
 
+bool validateBlitFramebufferParameters(gl::Context *context, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+                                       GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask,
+                                       GLenum filter, bool fromAngleExtension)
+{
+    switch (filter)
+    {
+      case GL_NEAREST:
+        break;
+      case GL_LINEAR:
+        if (fromAngleExtension)
+        {
+            return gl::error(GL_INVALID_ENUM, false);
+        }
+        break;
+      default:
+        return gl::error(GL_INVALID_ENUM, false);
+    }
+
+    if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)) != 0)
+    {
+        return gl::error(GL_INVALID_VALUE, false);
+    }
+
+    if (mask == 0)
+    {
+        // ES3.0 spec, section 4.3.2 specifies that a mask of zero is valid and no
+        // buffers are copied.
+        return false;
+    }
+
+    if (fromAngleExtension && (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0))
+    {
+        ERR("Scaling and flipping in BlitFramebufferANGLE not supported by this implementation.");
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    // ES3.0 spec, section 4.3.2 states that linear filtering is only available for the
+    // color buffer, leaving only nearest being unfiltered from above
+    if ((mask & ~GL_COLOR_BUFFER_BIT) != 0 && filter != GL_NEAREST)
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    if (context->getReadFramebufferHandle() == context->getDrawFramebufferHandle())
+    {
+        if (fromAngleExtension)
+        {
+            ERR("Blits with the same source and destination framebuffer are not supported by this "
+                "implementation.");
+        }
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    gl::Framebuffer *readFramebuffer = context->getReadFramebuffer();
+    gl::Framebuffer *drawFramebuffer = context->getDrawFramebuffer();
+    if (!readFramebuffer || readFramebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE ||
+        !drawFramebuffer || drawFramebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
+    {
+        return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false);
+    }
+
+    if (drawFramebuffer->getSamples() != 0)
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    gl::Rectangle sourceClippedRect, destClippedRect;
+    bool partialCopy;
+    if (!context->clipBlitFramebufferCoordinates(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1,
+                                                 &sourceClippedRect, &destClippedRect, &partialCopy))
+    {
+        return gl::error(GL_INVALID_OPERATION, false);
+    }
+
+    bool sameBounds = srcX0 == dstX0 && srcY0 == dstY0 && srcX1 == dstX1 && srcY1 == dstY1;
+
+    GLuint clientVersion = context->getClientVersion();
+
+    if (mask & GL_COLOR_BUFFER_BIT)
+    {
+        gl::Renderbuffer *readColorBuffer = readFramebuffer->getReadColorbuffer();
+        gl::Renderbuffer *drawColorBuffer = drawFramebuffer->getFirstColorbuffer();
+
+        if (readColorBuffer && drawColorBuffer)
+        {
+            GLint readInternalFormat = readColorBuffer->getActualFormat();
+            GLint drawInternalFormat = drawColorBuffer->getActualFormat();
+
+            for (unsigned int i = 0; i < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; i++)
+            {
+                if (drawFramebuffer->isEnabledColorAttachment(i))
+                {
+                    GLint drawbufferAttachmentFormat = drawFramebuffer->getColorbuffer(i)->getActualFormat();
+
+                    if (gl::IsNormalizedFixedPointFormat(readInternalFormat, clientVersion) &&
+                        !gl::IsNormalizedFixedPointFormat(drawbufferAttachmentFormat, clientVersion))
+                    {
+                        return gl::error(GL_INVALID_OPERATION, false);
+                    }
+
+                    if (gl::IsUnsignedIntegerFormat(readInternalFormat, clientVersion) &&
+                        !gl::IsUnsignedIntegerFormat(drawbufferAttachmentFormat, clientVersion))
+                    {
+                        return gl::error(GL_INVALID_OPERATION, false);
+                    }
+
+                    if (gl::IsSignedIntegerFormat(readInternalFormat, clientVersion) &&
+                        !gl::IsSignedIntegerFormat(drawbufferAttachmentFormat, clientVersion))
+                    {
+                        return gl::error(GL_INVALID_OPERATION, false);
+                    }
+
+                    if (readColorBuffer->getSamples() > 0 && (readInternalFormat != drawbufferAttachmentFormat || !sameBounds))
+                    {
+                        return gl::error(GL_INVALID_OPERATION, false);
+                    }
+                }
+            }
+
+            if (gl::IsIntegerFormat(readInternalFormat, clientVersion) && filter == GL_LINEAR)
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+
+            if (fromAngleExtension)
+            {
+                const GLenum readColorbufferType = readFramebuffer->getReadColorbufferType();
+                if (readColorbufferType != GL_TEXTURE_2D && readColorbufferType != GL_RENDERBUFFER)
+                {
+                    return gl::error(GL_INVALID_OPERATION, false);
+                }
+
+                for (unsigned int colorAttachment = 0; colorAttachment < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
+                {
+                    if (drawFramebuffer->isEnabledColorAttachment(colorAttachment))
+                    {
+                        if (drawFramebuffer->getColorbufferType(colorAttachment) != GL_TEXTURE_2D &&
+                            drawFramebuffer->getColorbufferType(colorAttachment) != GL_RENDERBUFFER)
+                        {
+                            return gl::error(GL_INVALID_OPERATION, false);
+                        }
+
+                        if (drawFramebuffer->getColorbuffer(colorAttachment)->getActualFormat() != readColorBuffer->getActualFormat())
+                        {
+                            return gl::error(GL_INVALID_OPERATION, false);
+                        }
+                    }
+                }
+
+                if (partialCopy && readFramebuffer->getSamples() != 0)
+                {
+                    return gl::error(GL_INVALID_OPERATION, false);
+                }
+            }
+        }
+    }
+
+    if (mask & GL_DEPTH_BUFFER_BIT)
+    {
+        gl::Renderbuffer *readDepthBuffer = readFramebuffer->getDepthbuffer();
+        gl::Renderbuffer *drawDepthBuffer = drawFramebuffer->getDepthbuffer();
+
+        if (readDepthBuffer && drawDepthBuffer)
+        {
+            if (readDepthBuffer->getActualFormat() != drawDepthBuffer->getActualFormat())
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+
+            if (readDepthBuffer->getSamples() > 0 && !sameBounds)
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+
+            if (fromAngleExtension)
+            {
+                if (partialCopy)
+                {
+                    ERR("Only whole-buffer depth and stencil blits are supported by this implementation.");
+                    return gl::error(GL_INVALID_OPERATION, false); // only whole-buffer copies are permitted
+                }
+
+                if (readDepthBuffer->getSamples() != 0 || drawDepthBuffer->getSamples() != 0)
+                {
+                    return gl::error(GL_INVALID_OPERATION, false);
+                }
+            }
+        }
+    }
+
+    if (mask & GL_STENCIL_BUFFER_BIT)
+    {
+        gl::Renderbuffer *readStencilBuffer = readFramebuffer->getStencilbuffer();
+        gl::Renderbuffer *drawStencilBuffer = drawFramebuffer->getStencilbuffer();
+
+        if (fromAngleExtension && partialCopy)
+        {
+            ERR("Only whole-buffer depth and stencil blits are supported by this implementation.");
+            return gl::error(GL_INVALID_OPERATION, false); // only whole-buffer copies are permitted
+        }
+
+        if (readStencilBuffer && drawStencilBuffer)
+        {
+            if (readStencilBuffer->getActualFormat() != drawStencilBuffer->getActualFormat())
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+
+            if (readStencilBuffer->getSamples() > 0 && !sameBounds)
+            {
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+
+            if (fromAngleExtension)
+            {
+                if (partialCopy)
+                {
+                    ERR("Only whole-buffer depth and stencil blits are supported by this implementation.");
+                    return gl::error(GL_INVALID_OPERATION, false); // only whole-buffer copies are permitted
+                }
+
+                if (readStencilBuffer->getSamples() != 0 || drawStencilBuffer->getSamples() != 0)
+                {
+                    return gl::error(GL_INVALID_OPERATION, false);
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
 extern "C"
 {
 
@@ -8796,7 +9028,15 @@
                 return gl::error(GL_INVALID_OPERATION);
             }
 
-            glBlitFramebufferANGLE(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+            if (!validateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1,
+                                                   dstX0, dstY0, dstX1, dstY1, mask, filter,
+                                                   false))
+            {
+                return;
+            }
+
+            context->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1,
+                                     mask, filter);
         }
     }
     catch(std::bad_alloc&)
@@ -11300,36 +11540,19 @@
 
     try
     {
-        switch (filter)
-        {
-          case GL_NEAREST:
-            break;
-          default:
-            return gl::error(GL_INVALID_ENUM);
-        }
-
-        if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)) != 0)
-        {
-            return gl::error(GL_INVALID_VALUE);
-        }
-
-        if (srcX1 - srcX0 != dstX1 - dstX0 || srcY1 - srcY0 != dstY1 - dstY0)
-        {
-            ERR("Scaling and flipping in BlitFramebufferANGLE not supported by this implementation");
-            return gl::error(GL_INVALID_OPERATION);
-        }
-
         gl::Context *context = gl::getNonLostContext();
 
         if (context)
         {
-            if (context->getReadFramebufferHandle() == context->getDrawFramebufferHandle())
+            if (!validateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1,
+                                                   dstX0, dstY0, dstX1, dstY1, mask, filter,
+                                                   true))
             {
-                ERR("Blits with the same source and destination framebuffer are not supported by this implementation.");
-                return gl::error(GL_INVALID_OPERATION);
+                return;
             }
 
-            context->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask);
+            context->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1,
+                                     mask, filter);
         }
     }
     catch(std::bad_alloc&)
diff --git a/src/libGLESv2/renderer/Renderer.h b/src/libGLESv2/renderer/Renderer.h
index eef8453..dadac4a 100644
--- a/src/libGLESv2/renderer/Renderer.h
+++ b/src/libGLESv2/renderer/Renderer.h
@@ -221,7 +221,7 @@
                            GLint xoffset, GLint yoffset, GLint zOffset, TextureStorageInterface2DArray *storage, GLint level) = 0;
 
     virtual bool blitRect(gl::Framebuffer *readTarget, const gl::Rectangle &readRect, gl::Framebuffer *drawTarget, const gl::Rectangle &drawRect,
-                          bool blitRenderTarget, bool blitDepthStencil) = 0;
+                          bool blitRenderTarget, bool blitDepthStencil, GLenum filter) = 0;
     virtual void readPixels(gl::Framebuffer *framebuffer, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
                             GLsizei outputPitch, bool packReverseRowOrder, GLint packAlignment, void* pixels) = 0;
 
diff --git a/src/libGLESv2/renderer/Renderer11.cpp b/src/libGLESv2/renderer/Renderer11.cpp
index 3f53111..05778f0 100644
--- a/src/libGLESv2/renderer/Renderer11.cpp
+++ b/src/libGLESv2/renderer/Renderer11.cpp
@@ -3004,7 +3004,7 @@
 }
 
 bool Renderer11::blitRect(gl::Framebuffer *readTarget, const gl::Rectangle &readRect, gl::Framebuffer *drawTarget, const gl::Rectangle &drawRect,
-                          bool blitRenderTarget, bool blitDepthStencil)
+                          bool blitRenderTarget, bool blitDepthStencil, GLenum filter)
 {
     if (blitRenderTarget)
     {
@@ -3032,7 +3032,7 @@
 
                 RenderTarget *drawRenderTarget = drawBuffer->getRenderTarget();
 
-                if (!blitRenderbufferRect(readRect, drawRect, readRenderTarget, drawRenderTarget, false))
+                if (!blitRenderbufferRect(readRect, drawRect, readRenderTarget, drawRenderTarget, filter))
                 {
                     return false;
                 }
@@ -3060,7 +3060,7 @@
         RenderTarget *readRenderTarget = readBuffer->getDepthStencil();
         RenderTarget *drawRenderTarget = drawBuffer->getDepthStencil();
 
-        if (!blitRenderbufferRect(readRect, drawRect, readRenderTarget, drawRenderTarget, true))
+        if (!blitRenderbufferRect(readRect, drawRect, readRenderTarget, drawRenderTarget, filter))
         {
             return false;
         }
@@ -3290,8 +3290,8 @@
     stagingTex = NULL;
 }
 
-bool Renderer11::blitRenderbufferRect(const gl::Rectangle &readRect, const gl::Rectangle &drawRect, RenderTarget *readRenderTarget, 
-                                      RenderTarget *drawRenderTarget, bool wholeBufferCopy)
+bool Renderer11::blitRenderbufferRect(const gl::Rectangle &readRect, const gl::Rectangle &drawRect, RenderTarget *readRenderTarget,
+                                      RenderTarget *drawRenderTarget, GLenum filter)
 {
     ASSERT(readRect.width == drawRect.width && readRect.height == drawRect.height);
 
@@ -3349,6 +3349,10 @@
     readBox.front = 0;
     readBox.back = 1;
 
+    bool wholeBufferCopy = readRect.x == 0 && readRect.y == 0 &&
+                           readRect.width == readRenderTarget->getWidth() &&
+                           readRect.height == readRenderTarget->getHeight();
+
     // D3D11 needs depth-stencil CopySubresourceRegions to have a NULL pSrcBox
     // We also require complete framebuffer copies for depth-stencil blit.
     D3D11_BOX *pSrcBox = wholeBufferCopy ? NULL : &readBox;
diff --git a/src/libGLESv2/renderer/Renderer11.h b/src/libGLESv2/renderer/Renderer11.h
index 756324d..ab1afce 100644
--- a/src/libGLESv2/renderer/Renderer11.h
+++ b/src/libGLESv2/renderer/Renderer11.h
@@ -165,7 +165,7 @@
                            GLint xoffset, GLint yoffset, GLint zOffset, TextureStorageInterface2DArray *storage, GLint level);
 
     virtual bool blitRect(gl::Framebuffer *readTarget, const gl::Rectangle &readRect, gl::Framebuffer *drawTarget, const gl::Rectangle &drawRect,
-                          bool blitRenderTarget, bool blitDepthStencil);
+                          bool blitRenderTarget, bool blitDepthStencil, GLenum filter);
     virtual void readPixels(gl::Framebuffer *framebuffer, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
                             GLsizei outputPitch, bool packReverseRowOrder, GLint packAlignment, void* pixels);
 
@@ -222,7 +222,7 @@
     rx::Range getViewportBounds() const;
 
     bool blitRenderbufferRect(const gl::Rectangle &readRect, const gl::Rectangle &drawRect, RenderTarget *readRenderTarget, 
-                              RenderTarget *drawRenderTarget, bool wholeBufferCopy);
+                              RenderTarget *drawRenderTarget, GLenum filter);
     ID3D11Texture2D *resolveMultisampledTexture(ID3D11Texture2D *source, unsigned int subresource);
 
     HMODULE mD3d11Module;
diff --git a/src/libGLESv2/renderer/Renderer9.cpp b/src/libGLESv2/renderer/Renderer9.cpp
index 7b6d97f..ac8a10f 100644
--- a/src/libGLESv2/renderer/Renderer9.cpp
+++ b/src/libGLESv2/renderer/Renderer9.cpp
@@ -2663,8 +2663,10 @@
 }
 
 bool Renderer9::blitRect(gl::Framebuffer *readFramebuffer, const gl::Rectangle &readRect, gl::Framebuffer *drawFramebuffer, const gl::Rectangle &drawRect,
-                         bool blitRenderTarget, bool blitDepthStencil)
+                         bool blitRenderTarget, bool blitDepthStencil, GLenum filter)
 {
+    ASSERT(filter == GL_NEAREST);
+
     endScene();
 
     if (blitRenderTarget)
diff --git a/src/libGLESv2/renderer/Renderer9.h b/src/libGLESv2/renderer/Renderer9.h
index 77ea29c..f26d229 100644
--- a/src/libGLESv2/renderer/Renderer9.h
+++ b/src/libGLESv2/renderer/Renderer9.h
@@ -183,7 +183,7 @@
                            GLint xoffset, GLint yoffset, GLint zOffset, TextureStorageInterface2DArray *storage, GLint level);
 
     virtual bool blitRect(gl::Framebuffer *readTarget, const gl::Rectangle &readRect, gl::Framebuffer *drawTarget, const gl::Rectangle &drawRect,
-                          bool blitRenderTarget, bool blitDepthStencil);
+                          bool blitRenderTarget, bool blitDepthStencil, GLenum filter);
     virtual void readPixels(gl::Framebuffer *framebuffer, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
                             GLsizei outputPitch, bool packReverseRowOrder, GLint packAlignment, void* pixels);