Moved the clipping of blit rectangles to bounds or scissors into the Renderers since rounding to integers can cause errors when stretching in ES3.

TRAC #23650

Author: Geoff Lang
Signed-off-by: Jamie Madill
Signed-off-by: Shannon Woods
diff --git a/src/build_angle.gypi b/src/build_angle.gypi
index 012a6a9..a26e684 100644
--- a/src/build_angle.gypi
+++ b/src/build_angle.gypi
@@ -254,6 +254,7 @@
             'libGLESv2/constants.h',
             'libGLESv2/Context.cpp',
             'libGLESv2/Context.h',
+            'libGLESv2/angletypes.cpp',
             'libGLESv2/angletypes.h',
             'libGLESv2/Fence.cpp',
             'libGLESv2/Fence.h',
diff --git a/src/libGLESv2/Context.cpp b/src/libGLESv2/Context.cpp
index 8fdac3f..eb38902 100644
--- a/src/libGLESv2/Context.cpp
+++ b/src/libGLESv2/Context.cpp
@@ -3613,185 +3613,6 @@
     return mRendererString;
 }
 
-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 false;
-    }
-
-    Renderbuffer *readColorBuffer = readFramebuffer->getReadColorbuffer();
-    Renderbuffer *drawColorBuffer = drawFramebuffer->getFirstColorbuffer();
-    if (!readColorBuffer || !drawColorBuffer)
-    {
-        return false;
-    }
-
-    int readBufferWidth = readColorBuffer->getWidth();
-    int readBufferHeight = readColorBuffer->getHeight();
-    int drawBufferWidth = drawColorBuffer->getWidth();
-    int drawBufferHeight = drawColorBuffer->getHeight();
-
-    gl::Rectangle sourceRect;
-    gl::Rectangle destRect;
-
-    if (srcX0 < srcX1)
-    {
-        sourceRect.x = srcX0;
-        destRect.x = dstX0;
-        sourceRect.width = srcX1 - srcX0;
-        destRect.width = dstX1 - dstX0;
-    }
-    else
-    {
-        sourceRect.x = srcX1;
-        destRect.x = dstX1;
-        sourceRect.width = srcX0 - srcX1;
-        destRect.width = dstX0 - dstX1;
-    }
-
-    if (srcY0 < srcY1)
-    {
-        sourceRect.height = srcY1 - srcY0;
-        destRect.height = dstY1 - dstY0;
-        sourceRect.y = srcY0;
-        destRect.y = dstY0;
-    }
-    else
-    {
-        sourceRect.height = srcY0 - srcY1;
-        destRect.height = dstY0 - dstY1;
-        sourceRect.y = srcY1;
-        destRect.y = dstY1;
-    }
-
-    Rectangle sourceScissoredRect = sourceRect;
-    Rectangle destScissoredRect = destRect;
-
-    if (mState.scissorTest)
-    {
-        // Only write to parts of the destination framebuffer which pass the scissor test.
-        if (destRect.x < mState.scissor.x)
-        {
-            int xDiff = mState.scissor.x - destRect.x;
-            destScissoredRect.x = mState.scissor.x;
-            destScissoredRect.width -= xDiff;
-            sourceScissoredRect.x += xDiff;
-            sourceScissoredRect.width -= xDiff;
-        }
-
-        if (destRect.x + destRect.width > mState.scissor.x + mState.scissor.width)
-        {
-            int xDiff = (destRect.x + destRect.width) - (mState.scissor.x + mState.scissor.width);
-            destScissoredRect.width -= xDiff;
-            sourceScissoredRect.width -= xDiff;
-        }
-
-        if (destRect.y < mState.scissor.y)
-        {
-            int yDiff = mState.scissor.y - destRect.y;
-            destScissoredRect.y = mState.scissor.y;
-            destScissoredRect.height -= yDiff;
-            sourceScissoredRect.y += yDiff;
-            sourceScissoredRect.height -= yDiff;
-        }
-
-        if (destRect.y + destRect.height > mState.scissor.y + mState.scissor.height)
-        {
-            int yDiff = (destRect.y + destRect.height) - (mState.scissor.y + mState.scissor.height);
-            destScissoredRect.height  -= yDiff;
-            sourceScissoredRect.height -= yDiff;
-        }
-    }
-
-    Rectangle sourceTrimmedRect = sourceScissoredRect;
-    Rectangle destTrimmedRect = destScissoredRect;
-
-    // The source & destination rectangles also may need to be trimmed if they fall out of the bounds of 
-    // the actual draw and read surfaces.
-    if (sourceTrimmedRect.x < 0)
-    {
-        int xDiff = 0 - sourceTrimmedRect.x;
-        sourceTrimmedRect.x = 0;
-        sourceTrimmedRect.width -= xDiff;
-        destTrimmedRect.x += xDiff;
-        destTrimmedRect.width -= xDiff;
-    }
-
-    if (sourceTrimmedRect.x + sourceTrimmedRect.width > readBufferWidth)
-    {
-        int xDiff = (sourceTrimmedRect.x + sourceTrimmedRect.width) - readBufferWidth;
-        sourceTrimmedRect.width -= xDiff;
-        destTrimmedRect.width -= xDiff;
-    }
-
-    if (sourceTrimmedRect.y < 0)
-    {
-        int yDiff = 0 - sourceTrimmedRect.y;
-        sourceTrimmedRect.y = 0;
-        sourceTrimmedRect.height -= yDiff;
-        destTrimmedRect.y += yDiff;
-        destTrimmedRect.height -= yDiff;
-    }
-
-    if (sourceTrimmedRect.y + sourceTrimmedRect.height > readBufferHeight)
-    {
-        int yDiff = (sourceTrimmedRect.y + sourceTrimmedRect.height) - readBufferHeight;
-        sourceTrimmedRect.height -= yDiff;
-        destTrimmedRect.height -= yDiff;
-    }
-
-    if (destTrimmedRect.x < 0)
-    {
-        int xDiff = 0 - destTrimmedRect.x;
-        destTrimmedRect.x = 0;
-        destTrimmedRect.width -= xDiff;
-        sourceTrimmedRect.x += xDiff;
-        sourceTrimmedRect.width -= xDiff;
-    }
-
-    if (destTrimmedRect.x + destTrimmedRect.width > drawBufferWidth)
-    {
-        int xDiff = (destTrimmedRect.x + destTrimmedRect.width) - drawBufferWidth;
-        destTrimmedRect.width -= xDiff;
-        sourceTrimmedRect.width -= xDiff;
-    }
-
-    if (destTrimmedRect.y < 0)
-    {
-        int yDiff = 0 - destTrimmedRect.y;
-        destTrimmedRect.y = 0;
-        destTrimmedRect.height -= yDiff;
-        sourceTrimmedRect.y += yDiff;
-        sourceTrimmedRect.height -= yDiff;
-    }
-
-    if (destTrimmedRect.y + destTrimmedRect.height > drawBufferHeight)
-    {
-        int yDiff = (destTrimmedRect.y + destTrimmedRect.height) - drawBufferHeight;
-        destTrimmedRect.height -= yDiff;
-        sourceTrimmedRect.height -= yDiff;
-    }
-
-    *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)
 {
@@ -3814,17 +3635,12 @@
         blitDepth = true;
     }
 
-    gl::Rectangle sourceClippedRect, destClippedRect;
-    bool partialCopy;
-    if (!clipBlitFramebufferCoordinates(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1,
-                                        &sourceClippedRect, &destClippedRect, &partialCopy))
-    {
-        return;
-    }
-
+    gl::Rectangle srcRect(srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0);
+    gl::Rectangle dstRect(dstX0, dstY0, dstX1 - dstX0, dstY1 - dstY0);
     if (blitRenderTarget || blitDepth || blitStencil)
     {
-        mRenderer->blitRect(readFramebuffer, sourceClippedRect, drawFramebuffer, destClippedRect,
+        const gl::Rectangle *scissor = mState.scissorTest ? &mState.scissor : NULL;
+        mRenderer->blitRect(readFramebuffer, srcRect, drawFramebuffer, dstRect, scissor,
                             blitRenderTarget, blitDepth, blitStencil, filter);
     }
 }
diff --git a/src/libGLESv2/Context.h b/src/libGLESv2/Context.h
index ea79da5..96bdd7e 100644
--- a/src/libGLESv2/Context.h
+++ b/src/libGLESv2/Context.h
@@ -432,11 +432,6 @@
 
     float getTextureMaxAnisotropy() const;
 
-    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);
 
diff --git a/src/libGLESv2/angletypes.cpp b/src/libGLESv2/angletypes.cpp
new file mode 100644
index 0000000..fdef4f4
--- /dev/null
+++ b/src/libGLESv2/angletypes.cpp
@@ -0,0 +1,65 @@
+#include "precompiled.h"
+//
+// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// angletypes.h : Defines a variety of structures and enum types that are used throughout libGLESv2
+
+#include "libGLESv2/angletypes.h"
+
+namespace gl
+{
+
+static void MinMax(int a, int b, int *minimum, int *maximum)
+{
+    if (a < b)
+    {
+        *minimum = a;
+        *maximum = b;
+    }
+    else
+    {
+        *minimum = b;
+        *maximum = a;
+    }
+}
+
+bool ClipRectangle(const Rectangle &source, const Rectangle &clip, Rectangle *intersection)
+{
+    int minSourceX, maxSourceX, minSourceY, maxSourceY;
+    MinMax(source.x, source.x + source.width, &minSourceX, &maxSourceX);
+    MinMax(source.y, source.y + source.height, &minSourceY, &maxSourceY);
+
+    int minClipX, maxClipX, minClipY, maxClipY;
+    MinMax(clip.x, clip.x + clip.width, &minClipX, &maxClipX);
+    MinMax(clip.y, clip.y + clip.height, &minClipY, &maxClipY);
+
+    if (minSourceX >= maxClipX || maxSourceX <= minClipX || minSourceY >= maxClipY || maxSourceY <= minClipY)
+    {
+        if (intersection)
+        {
+            intersection->x = minSourceX;
+            intersection->y = maxSourceY;
+            intersection->width = maxSourceX - minSourceX;
+            intersection->height = maxSourceY - minSourceY;
+        }
+
+        return false;
+    }
+    else
+    {
+        if (intersection)
+        {
+            intersection->x = std::max(minSourceX, minClipX);
+            intersection->y = std::max(minSourceY, minClipY);
+            intersection->width  = std::min(maxSourceX, maxClipX) - std::max(minSourceX, minClipX);
+            intersection->height = std::min(maxSourceY, maxClipY) - std::max(minSourceY, minClipY);
+        }
+
+        return true;
+    }
+}
+
+}
diff --git a/src/libGLESv2/angletypes.h b/src/libGLESv2/angletypes.h
index c377f4e..035c5f3 100644
--- a/src/libGLESv2/angletypes.h
+++ b/src/libGLESv2/angletypes.h
@@ -58,6 +58,8 @@
     Rectangle(int x_in, int y_in, int width_in, int height_in) : x(x_in), y(y_in), width(width_in), height(height_in) { }
 };
 
+bool ClipRectangle(const Rectangle &source, const Rectangle &clip, Rectangle *intersection);
+
 struct Box
 {
     int x;
diff --git a/src/libGLESv2/libGLESv2.vcxproj b/src/libGLESv2/libGLESv2.vcxproj
index 366824b..d1ba1cd 100644
--- a/src/libGLESv2/libGLESv2.vcxproj
+++ b/src/libGLESv2/libGLESv2.vcxproj
@@ -241,6 +241,7 @@
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>

       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>

     </ClCompile>

+    <ClCompile Include="angletypes.cpp" />

     <ClCompile Include="Buffer.cpp" />

     <ClCompile Include="Context.cpp" />

     <ClCompile Include="..\common\debug.cpp">

diff --git a/src/libGLESv2/libGLESv2.vcxproj.filters b/src/libGLESv2/libGLESv2.vcxproj.filters
index eb29d3a..0276ba3 100644
--- a/src/libGLESv2/libGLESv2.vcxproj.filters
+++ b/src/libGLESv2/libGLESv2.vcxproj.filters
@@ -263,6 +263,9 @@
     <ClCompile Include="renderer\Clear11.cpp">

       <Filter>Source Files\Renderer11</Filter>

     </ClCompile>

+    <ClCompile Include="angletypes.cpp">

+      <Filter>Source Files</Filter>

+    </ClCompile>

   </ItemGroup>

   <ItemGroup>

     <ClInclude Include="BinaryStream.h">

@@ -704,4 +707,4 @@
   <ItemGroup>

     <ResourceCompile Include="libGLESv2.rc" />

   </ItemGroup>

-</Project>
\ No newline at end of file
+</Project>
diff --git a/src/libGLESv2/renderer/Blit11.cpp b/src/libGLESv2/renderer/Blit11.cpp
index 2256117..7279f52 100644
--- a/src/libGLESv2/renderer/Blit11.cpp
+++ b/src/libGLESv2/renderer/Blit11.cpp
@@ -53,7 +53,8 @@
 
 Blit11::Blit11(rx::Renderer11 *renderer)
     : mRenderer(renderer), mShaderMap(compareBlitParameters), mVertexBuffer(NULL),
-      mPointSampler(NULL), mLinearSampler(NULL), mRasterizerState(NULL), mDepthStencilState(NULL),
+      mPointSampler(NULL), mLinearSampler(NULL), mScissorEnabledRasterizerState(NULL),
+      mScissorDisabledRasterizerState(NULL), mDepthStencilState(NULL),
       mQuad2DIL(NULL), mQuad2DVS(NULL), mDepthPS(NULL),
       mQuad3DIL(NULL), mQuad3DVS(NULL), mQuad3DGS(NULL)
 {
@@ -120,13 +121,18 @@
     rasterDesc.SlopeScaledDepthBias = 0.0f;
     rasterDesc.DepthBiasClamp = 0.0f;
     rasterDesc.DepthClipEnable = TRUE;
-    rasterDesc.ScissorEnable = FALSE;
     rasterDesc.MultisampleEnable = FALSE;
     rasterDesc.AntialiasedLineEnable = FALSE;
 
-    result = device->CreateRasterizerState(&rasterDesc, &mRasterizerState);
+    rasterDesc.ScissorEnable = TRUE;
+    result = device->CreateRasterizerState(&rasterDesc, &mScissorEnabledRasterizerState);
     ASSERT(SUCCEEDED(result));
-    d3d11::SetDebugName(mRasterizerState, "Blit11 rasterizer state");
+    d3d11::SetDebugName(mScissorEnabledRasterizerState, "Blit11 scissoring rasterizer state");
+
+    rasterDesc.ScissorEnable = FALSE;
+    result = device->CreateRasterizerState(&rasterDesc, &mScissorDisabledRasterizerState);
+    ASSERT(SUCCEEDED(result));
+    d3d11::SetDebugName(mScissorDisabledRasterizerState, "Blit11 no scissoring rasterizer state");
 
     D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
     depthStencilDesc.DepthEnable = true;
@@ -193,7 +199,8 @@
     SafeRelease(mVertexBuffer);
     SafeRelease(mPointSampler);
     SafeRelease(mLinearSampler);
-    SafeRelease(mRasterizerState);
+    SafeRelease(mScissorEnabledRasterizerState);
+    SafeRelease(mScissorDisabledRasterizerState);
     SafeRelease(mDepthStencilState);
 
     SafeRelease(mQuad2DIL);
@@ -209,18 +216,8 @@
 
 bool Blit11::copyTexture(ID3D11ShaderResourceView *source, const gl::Box &sourceArea, const gl::Extents &sourceSize,
                          ID3D11RenderTargetView *dest, const gl::Box &destArea, const gl::Extents &destSize,
-                         GLenum destFormat, GLenum filter)
+                         const gl::Rectangle *scissor, GLenum destFormat, GLenum filter)
 {
-    if(sourceArea.x < 0 || sourceArea.x + sourceArea.width  > sourceSize.width  ||
-       sourceArea.y < 0 || sourceArea.y + sourceArea.height > sourceSize.height ||
-       sourceArea.z < 0 || sourceArea.z + sourceArea.depth  > sourceSize.depth  ||
-       destArea.x   < 0 || destArea.x   + destArea.width    > destSize.width    ||
-       destArea.y   < 0 || destArea.y   + destArea.height   > destSize.height   ||
-       destArea.z   < 0 || destArea.z   + destArea.depth    > destSize.depth    )
-    {
-        return false;
-    }
-
     HRESULT result;
     ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
 
@@ -269,7 +266,22 @@
     // Apply state
     deviceContext->OMSetBlendState(NULL, NULL, 0xFFFFFFF);
     deviceContext->OMSetDepthStencilState(NULL, 0xFFFFFFFF);
-    deviceContext->RSSetState(mRasterizerState);
+
+    if (scissor)
+    {
+        D3D11_RECT scissorRect;
+        scissorRect.left = scissor->x;
+        scissorRect.right = scissor->x + scissor->width;
+        scissorRect.top = scissor->y;
+        scissorRect.bottom = scissor->y + scissor->height;
+
+        deviceContext->RSSetScissorRects(1, &scissorRect);
+        deviceContext->RSSetState(mScissorEnabledRasterizerState);
+    }
+    else
+    {
+        deviceContext->RSSetState(mScissorDisabledRasterizerState);
+    }
 
     // Apply shaders
     deviceContext->IASetInputLayout(shader.mInputLayout);
@@ -326,49 +338,6 @@
     return true;
 }
 
-static ID3D11Resource *createStagingTexture(ID3D11Device *device, ID3D11DeviceContext *context,
-                                            ID3D11Resource *source, unsigned int subresource,
-                                            const gl::Extents &size, unsigned int cpuAccessFlags)
-{
-    ID3D11Texture2D *sourceTexture = d3d11::DynamicCastComObject<ID3D11Texture2D>(source);
-    if (!sourceTexture)
-    {
-        return NULL;
-    }
-
-    D3D11_TEXTURE2D_DESC sourceDesc;
-    sourceTexture->GetDesc(&sourceDesc);
-
-    if (sourceDesc.SampleDesc.Count > 1)
-    {
-        // Creating a staging texture of a multisampled texture is not supported
-        SafeRelease(sourceTexture);
-        return NULL;
-    }
-
-    D3D11_TEXTURE2D_DESC stagingDesc;
-    stagingDesc.Width = size.width;
-    stagingDesc.Height = size.height;
-    stagingDesc.MipLevels = 1;
-    stagingDesc.ArraySize = 1;
-    stagingDesc.Format = sourceDesc.Format;
-    stagingDesc.SampleDesc.Count = 1;
-    stagingDesc.SampleDesc.Quality = 0;
-    stagingDesc.Usage = D3D11_USAGE_STAGING;
-    stagingDesc.CPUAccessFlags = cpuAccessFlags;
-    stagingDesc.MiscFlags = 0;
-    stagingDesc.BindFlags = 0;
-
-    SafeRelease(sourceTexture);
-
-    ID3D11Texture2D *stagingTexture = NULL;
-    HRESULT result = device->CreateTexture2D(&stagingDesc, NULL, &stagingTexture);
-
-    context->CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, source, subresource, NULL);
-
-    return stagingTexture;
-}
-
 static DXGI_FORMAT getTextureFormat(ID3D11Resource *resource)
 {
     ID3D11Texture2D *texture = d3d11::DynamicCastComObject<ID3D11Texture2D>(resource);
@@ -385,6 +354,36 @@
     return desc.Format;
 }
 
+static ID3D11Resource *createStagingTexture(ID3D11Device *device, ID3D11DeviceContext *context,
+                                            ID3D11Resource *source, unsigned int subresource,
+                                            const gl::Extents &size, unsigned int cpuAccessFlags)
+{
+    D3D11_TEXTURE2D_DESC stagingDesc;
+    stagingDesc.Width = size.width;
+    stagingDesc.Height = size.height;
+    stagingDesc.MipLevels = 1;
+    stagingDesc.ArraySize = 1;
+    stagingDesc.Format = getTextureFormat(source);
+    stagingDesc.SampleDesc.Count = 1;
+    stagingDesc.SampleDesc.Quality = 0;
+    stagingDesc.Usage = D3D11_USAGE_STAGING;
+    stagingDesc.CPUAccessFlags = cpuAccessFlags;
+    stagingDesc.MiscFlags = 0;
+    stagingDesc.BindFlags = 0;
+
+    ID3D11Texture2D *stagingTexture = NULL;
+    HRESULT result = device->CreateTexture2D(&stagingDesc, NULL, &stagingTexture);
+    if (FAILED(result))
+    {
+        ERR("Failed to create staging texture for depth stencil blit. HRESULT: 0x%X.", result);
+        return NULL;
+    }
+
+    context->CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, source, subresource, NULL);
+
+    return stagingTexture;
+}
+
 inline static void generateVertexCoords(const gl::Box &sourceArea, const gl::Extents &sourceSize,
                                         const gl::Box &destArea, const gl::Extents &destSize,
                                         float *x1, float *y1, float *x2, float *y2,
@@ -450,15 +449,17 @@
 }
 
 bool Blit11::copyStencil(ID3D11Resource *source, unsigned int sourceSubresource, const gl::Box &sourceArea, const gl::Extents &sourceSize,
-                         ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize)
+                         ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize,
+                         const gl::Rectangle *scissor)
 {
     return copyDepthStencil(source, sourceSubresource, sourceArea, sourceSize,
                             dest, destSubresource, destArea, destSize,
-                            true);
+                            scissor, true);
 }
 
 bool Blit11::copyDepth(ID3D11ShaderResourceView *source, const gl::Box &sourceArea, const gl::Extents &sourceSize,
-                       ID3D11DepthStencilView *dest, const gl::Box &destArea, const gl::Extents &destSize)
+                       ID3D11DepthStencilView *dest, const gl::Box &destArea, const gl::Extents &destSize,
+                       const gl::Rectangle *scissor)
 {
     HRESULT result;
     ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
@@ -488,7 +489,22 @@
     // Apply state
     deviceContext->OMSetBlendState(NULL, NULL, 0xFFFFFFF);
     deviceContext->OMSetDepthStencilState(mDepthStencilState, 0xFFFFFFFF);
-    deviceContext->RSSetState(mRasterizerState);
+
+    if (scissor)
+    {
+        D3D11_RECT scissorRect;
+        scissorRect.left = scissor->x;
+        scissorRect.right = scissor->x + scissor->width;
+        scissorRect.top = scissor->y;
+        scissorRect.bottom = scissor->y + scissor->height;
+
+        deviceContext->RSSetScissorRects(1, &scissorRect);
+        deviceContext->RSSetState(mScissorEnabledRasterizerState);
+    }
+    else
+    {
+        deviceContext->RSSetState(mScissorDisabledRasterizerState);
+    }
 
     // Apply shaders
     deviceContext->IASetInputLayout(mQuad2DIL);
@@ -539,16 +555,17 @@
 }
 
 bool Blit11::copyDepthStencil(ID3D11Resource *source, unsigned int sourceSubresource, const gl::Box &sourceArea, const gl::Extents &sourceSize,
-                              ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize)
+                              ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize,
+                              const gl::Rectangle *scissor)
 {
     return copyDepthStencil(source, sourceSubresource, sourceArea, sourceSize,
                             dest, destSubresource, destArea, destSize,
-                            false);
+                            scissor, false);
 }
 
 bool Blit11::copyDepthStencil(ID3D11Resource *source, unsigned int sourceSubresource, const gl::Box &sourceArea, const gl::Extents &sourceSize,
                               ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize,
-                              bool stencilOnly)
+                              const gl::Rectangle *scissor, bool stencilOnly)
 {
     ID3D11Device *device = mRenderer->getDevice();
     ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
@@ -556,6 +573,13 @@
     ID3D11Resource *sourceStaging = createStagingTexture(device, deviceContext, source, sourceSubresource, sourceSize, D3D11_CPU_ACCESS_READ);
     ID3D11Resource *destStaging = createStagingTexture(device, deviceContext, dest, destSubresource, destSize, D3D11_CPU_ACCESS_WRITE);
 
+    if (!sourceStaging || !destStaging)
+    {
+        SafeRelease(sourceStaging);
+        SafeRelease(destStaging);
+        return false;
+    }
+
     DXGI_FORMAT format = getTextureFormat(source);
     ASSERT(format == getTextureFormat(dest));
 
@@ -568,7 +592,7 @@
         copySize = d3d11::GetStencilBits(format) / 8;
 
         // It would be expensive to have non-byte sized stencil sizes since it would
-        // require reading from the destination, currently there arn't any though.
+        // require reading from the destination, currently there aren't any though.
         ASSERT(d3d11::GetStencilBits(format)   % 8 == 0 &&
                d3d11::GetStencilOffset(format) % 8 == 0);
     }
@@ -577,34 +601,66 @@
     deviceContext->Map(sourceStaging, 0, D3D11_MAP_READ, 0, &sourceMapping);
     deviceContext->Map(destStaging, 0, D3D11_MAP_WRITE, 0, &destMapping);
 
-    int startDestY = std::min(destArea.y, destArea.y + destArea.height);
-    int endDestY = std::max(destArea.y, destArea.y + destArea.height);
-
-    int startDestX = std::min(destArea.x, destArea.x + destArea.width);
-    int endDestX = std::max(destArea.x, destArea.x + destArea.width);
-
-    for (int y = startDestY; y < endDestY; y++)
+    if (!sourceMapping.pData || !destMapping.pData)
     {
-        float yPerc = static_cast<float>(y - startDestY) / (endDestY - startDestY - 1);
-        unsigned int readRow = sourceArea.y + floor(yPerc * (sourceArea.height - 1) + 0.5f);
+        if (!sourceMapping.pData)
+        {
+            deviceContext->Unmap(sourceStaging, 0);
+        }
+        if (!destMapping.pData)
+        {
+            deviceContext->Unmap(destStaging, 0);
+        }
+        SafeRelease(sourceStaging);
+        SafeRelease(destStaging);
+        return false;
+    }
+
+    gl::Rectangle clippedDestArea(destArea.x, destArea.y, destArea.width, destArea.height);
+
+    // Clip dest area to the destination size
+    gl::ClipRectangle(clippedDestArea, gl::Rectangle(0, 0, destSize.width, destSize.height), &clippedDestArea);
+
+    // Clip dest area to the scissor
+    if (scissor)
+    {
+        gl::ClipRectangle(clippedDestArea, *scissor, &clippedDestArea);
+    }
+
+    // Determine if entire rows can be copied at once instead of each individual pixel, requires that there is
+    // no out of bounds lookups required, the entire pixel is copied and no stretching
+    bool wholeRowCopy = sourceArea.width == clippedDestArea.width &&
+                        sourceArea.x >= 0 && sourceArea.x + sourceArea.width <= sourceSize.width &&
+                        copySize == pixelSize;
+
+    for (int y = clippedDestArea.y; y < clippedDestArea.y + clippedDestArea.height; y++)
+    {
+        float yPerc = static_cast<float>(y - destArea.y) / (destArea.height - 1);
+
+        // Interpolate using the original source rectangle to determine which row to sample from while clamping to the edges
+        unsigned int readRow = gl::clamp(sourceArea.y + floor(yPerc * (sourceArea.height - 1) + 0.5f), 0, sourceSize.height - 1);
         unsigned int writeRow = y;
 
-        if (sourceArea.width == destArea.width && copySize == pixelSize)
+        if (wholeRowCopy)
         {
             void *sourceRow = reinterpret_cast<char*>(sourceMapping.pData) +
-                              readRow * sourceMapping.RowPitch;
+                              readRow * sourceMapping.RowPitch +
+                              sourceArea.x * pixelSize;
 
             void *destRow = reinterpret_cast<char*>(destMapping.pData) +
-                            writeRow * destMapping.RowPitch;
+                            writeRow * destMapping.RowPitch +
+                            destArea.x * pixelSize;
 
             memcpy(destRow, sourceRow, pixelSize * destArea.width);
         }
         else
         {
-            for (int x = startDestX; x < endDestX; x++)
+            for (int x = clippedDestArea.x; x < clippedDestArea.x + clippedDestArea.width; x++)
             {
-                float xPerc = static_cast<float>(x - startDestX) / (endDestX - startDestX - 1);
-                unsigned int readColumn = sourceArea.x + floor(xPerc * (sourceArea.width - 1) + 0.5f);
+                float xPerc = static_cast<float>(x - destArea.x) / (destArea.width - 1);
+
+                // Interpolate the original source rectangle to determine which column to sample from while clamping to the edges
+                unsigned int readColumn = gl::clamp(sourceArea.x + floor(xPerc * (sourceArea.width - 1) + 0.5f), 0, sourceSize.width - 1);
                 unsigned int writeColumn = x;
 
                 void *sourcePixel = reinterpret_cast<char*>(sourceMapping.pData) +
diff --git a/src/libGLESv2/renderer/Blit11.h b/src/libGLESv2/renderer/Blit11.h
index 49468df..0e5eb77 100644
--- a/src/libGLESv2/renderer/Blit11.h
+++ b/src/libGLESv2/renderer/Blit11.h
@@ -30,16 +30,19 @@
 
     bool copyTexture(ID3D11ShaderResourceView *source, const gl::Box &sourceArea, const gl::Extents &sourceSize,
                      ID3D11RenderTargetView *dest, const gl::Box &destArea, const gl::Extents &destSize,
-                     GLenum destFormat, GLenum filter);
+                     const gl::Rectangle *scissor, GLenum destFormat, GLenum filter);
 
     bool copyStencil(ID3D11Resource *source, unsigned int sourceSubresource, const gl::Box &sourceArea, const gl::Extents &sourceSize,
-                     ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize);
+                     ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize,
+                     const gl::Rectangle *scissor);
 
     bool copyDepth(ID3D11ShaderResourceView *source, const gl::Box &sourceArea, const gl::Extents &sourceSize,
-                   ID3D11DepthStencilView *dest, const gl::Box &destArea, const gl::Extents &destSize);
+                   ID3D11DepthStencilView *dest, const gl::Box &destArea, const gl::Extents &destSize,
+                   const gl::Rectangle *scissor);
 
     bool copyDepthStencil(ID3D11Resource *source, unsigned int sourceSubresource, const gl::Box &sourceArea, const gl::Extents &sourceSize,
-                          ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize);
+                          ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize,
+                          const gl::Rectangle *scissor);
 
   private:
     rx::Renderer11 *mRenderer;
@@ -53,7 +56,7 @@
 
     bool copyDepthStencil(ID3D11Resource *source, unsigned int sourceSubresource, const gl::Box &sourceArea, const gl::Extents &sourceSize,
                           ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize,
-                          bool stencilOnly);
+                          const gl::Rectangle *scissor, bool stencilOnly);
 
     static bool compareBlitParameters(const BlitParameters &a, const BlitParameters &b);
 
@@ -84,7 +87,8 @@
     ID3D11Buffer *mVertexBuffer;
     ID3D11SamplerState *mPointSampler;
     ID3D11SamplerState *mLinearSampler;
-    ID3D11RasterizerState *mRasterizerState;
+    ID3D11RasterizerState *mScissorEnabledRasterizerState;
+    ID3D11RasterizerState *mScissorDisabledRasterizerState;
     ID3D11DepthStencilState *mDepthStencilState;
 
     ID3D11InputLayout *mQuad2DIL;
diff --git a/src/libGLESv2/renderer/Renderer.h b/src/libGLESv2/renderer/Renderer.h
index 41b28d5..d3c9dda 100644
--- a/src/libGLESv2/renderer/Renderer.h
+++ b/src/libGLESv2/renderer/Renderer.h
@@ -225,7 +225,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 blitDepth, bool blitStencil, GLenum filter) = 0;
+                          const gl::Rectangle *scissor, bool blitRenderTarget, bool blitDepth, bool blitStencil, 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 837be4b..3872e71 100644
--- a/src/libGLESv2/renderer/Renderer11.cpp
+++ b/src/libGLESv2/renderer/Renderer11.cpp
@@ -2416,7 +2416,7 @@
 
     // Use nearest filtering because source and destination are the same size for the direct
     // copy
-    bool ret = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize,
+    bool ret = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL,
                                   destFormat, GL_NEAREST);
 
     return ret;
@@ -2475,7 +2475,7 @@
 
     // Use nearest filtering because source and destination are the same size for the direct
     // copy
-    bool ret = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize,
+    bool ret = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL,
                                   destFormat, GL_NEAREST);
 
     return ret;
@@ -2534,7 +2534,7 @@
 
     // Use nearest filtering because source and destination are the same size for the direct
     // copy
-    bool ret = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize,
+    bool ret = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL,
                                   destFormat, GL_NEAREST);
 
     return ret;
@@ -2595,7 +2595,7 @@
 
     // Use nearest filtering because source and destination are the same size for the direct
     // copy
-    bool ret = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize,
+    bool ret = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL,
                                   destFormat, GL_NEAREST);
 
     return ret;
@@ -2794,7 +2794,7 @@
 }
 
 bool Renderer11::blitRect(gl::Framebuffer *readTarget, const gl::Rectangle &readRect, gl::Framebuffer *drawTarget, const gl::Rectangle &drawRect,
-                          bool blitRenderTarget, bool blitDepth, bool blitStencil, GLenum filter)
+                          const gl::Rectangle *scissor, bool blitRenderTarget, bool blitDepth, bool blitStencil, GLenum filter)
 {
     if (blitRenderTarget)
     {
@@ -2822,7 +2822,7 @@
 
                 RenderTarget *drawRenderTarget = drawBuffer->getRenderTarget();
 
-                if (!blitRenderbufferRect(readRect, drawRect, readRenderTarget, drawRenderTarget, filter,
+                if (!blitRenderbufferRect(readRect, drawRect, readRenderTarget, drawRenderTarget, filter, scissor,
                                           blitRenderTarget, false, false))
                 {
                     return false;
@@ -2851,7 +2851,7 @@
         RenderTarget *readRenderTarget = readBuffer->getDepthStencil();
         RenderTarget *drawRenderTarget = drawBuffer->getDepthStencil();
 
-        if (!blitRenderbufferRect(readRect, drawRect, readRenderTarget, drawRenderTarget, filter,
+        if (!blitRenderbufferRect(readRect, drawRect, readRenderTarget, drawRenderTarget, filter, scissor,
                                   false, blitDepth, blitStencil))
         {
             return false;
@@ -3080,7 +3080,8 @@
 }
 
 bool Renderer11::blitRenderbufferRect(const gl::Rectangle &readRect, const gl::Rectangle &drawRect, RenderTarget *readRenderTarget,
-                                      RenderTarget *drawRenderTarget, GLenum filter, bool colorBlit, bool depthBlit, bool stencilBlit)
+                                      RenderTarget *drawRenderTarget, GLenum filter, const gl::Rectangle *scissor,
+                                      bool colorBlit, bool depthBlit, bool stencilBlit)
 {
     // Since blitRenderbufferRect is called for each render buffer that needs to be blitted,
     // it should never be the case that both color and depth/stencil need to be blitted at
@@ -3148,16 +3149,37 @@
         return gl::error(GL_OUT_OF_MEMORY, false);
     }
 
-    bool wholeBufferCopy = readRect.x == 0 && readRect.width == readRenderTarget11->getWidth() &&
-                           readRect.y == 0 && readRect.height == readRenderTarget11->getHeight() &&
-                           drawRect.x == 0 && drawRect.width == drawRenderTarget->getWidth() &&
-                           drawRect.y == 0 && drawRect.height == drawRenderTarget->getHeight();
+    gl::Extents readSize(readRenderTarget->getWidth(), readRenderTarget->getHeight(), 1);
+    gl::Extents drawSize(drawRenderTarget->getWidth(), drawRenderTarget->getHeight(), 1);
+
+    bool scissorNeeded = scissor && gl::ClipRectangle(drawRect, *scissor, NULL);
+
+    bool wholeBufferCopy = !scissorNeeded &&
+                           readRect.x == 0 && readRect.width == readSize.width &&
+                           readRect.y == 0 && readRect.height == readSize.height &&
+                           drawRect.x == 0 && drawRect.width == drawSize.width &&
+                           drawRect.y == 0 && drawRect.height == drawSize.height;
 
     bool stretchRequired = readRect.width != drawRect.width || readRect.height != drawRect.height;
 
+    bool flipRequired = readRect.width < 0 || readRect.height < 0 || drawRect.width < 0 || readRect.height < 0;
+
+    bool outOfBounds = readRect.x < 0 || readRect.x + readRect.width > readSize.width ||
+                       readRect.y < 0 || readRect.y + readRect.height > readSize.height ||
+                       drawRect.x < 0 || drawRect.x + drawRect.width > drawSize.width ||
+                       drawRect.y < 0 || drawRect.y + drawRect.height > drawSize.height;
+
+    bool hasDepth = gl::GetDepthBits(drawRenderTarget11->getActualFormat(), getCurrentClientVersion()) > 0;
+    bool hasStencil = gl::GetStencilBits(drawRenderTarget11->getActualFormat(), getCurrentClientVersion()) > 0;
+    bool partialDSBlit = (hasDepth && depthBlit) != (hasStencil && stencilBlit);
+
     if (readRenderTarget11->getActualFormat() == drawRenderTarget->getActualFormat() &&
-        !stretchRequired && (!(depthBlit || stencilBlit) || wholeBufferCopy))
+        !stretchRequired && !outOfBounds && !flipRequired && !partialDSBlit &&
+        (!(depthBlit || stencilBlit) || wholeBufferCopy))
     {
+        UINT dstX = drawRect.x;
+        UINT dstY = drawRect.y;
+
         D3D11_BOX readBox;
         readBox.left = readRect.x;
         readBox.right = readRect.x + readRect.width;
@@ -3166,40 +3188,66 @@
         readBox.front = 0;
         readBox.back = 1;
 
+        if (scissorNeeded)
+        {
+            // drawRect is guaranteed to have positive width and height because stretchRequired is false.
+            ASSERT(drawRect.width >= 0 || drawRect.height >= 0);
+
+            if (drawRect.x < scissor->x)
+            {
+                dstX = scissor->x;
+                readBox.left += (scissor->x - drawRect.x);
+            }
+            if (drawRect.y < scissor->y)
+            {
+                dstY = scissor->y;
+                readBox.top += (scissor->y - drawRect.y);
+            }
+            if (drawRect.x + drawRect.width > scissor->x + scissor->width)
+            {
+                readBox.right -= ((drawRect.x + drawRect.width) - (scissor->x + scissor->width));
+            }
+            if (drawRect.y + drawRect.height > scissor->y + scissor->height)
+            {
+                readBox.bottom -= ((drawRect.y + drawRect.height) - (scissor->y + scissor->height));
+            }
+        }
+
         // 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;
 
-        mDeviceContext->CopySubresourceRegion(drawTexture, drawSubresource, drawRect.x, drawRect.y, 0,
+        mDeviceContext->CopySubresourceRegion(drawTexture, drawSubresource, dstX, dstY, 0,
                                               readTexture, readSubresource, pSrcBox);
         result = true;
     }
     else
     {
         gl::Box readArea(readRect.x, readRect.y, 0, readRect.width, readRect.height, 1);
-        gl::Extents readSize(readRenderTarget->getWidth(), readRenderTarget->getHeight(), 1);
-
         gl::Box drawArea(drawRect.x, drawRect.y, 0, drawRect.width, drawRect.height, 1);
-        gl::Extents drawSize(drawRenderTarget->getWidth(), drawRenderTarget->getHeight(), 1);
 
         if (depthBlit && stencilBlit)
         {
             result = mBlit->copyDepthStencil(readTexture, readSubresource, readArea, readSize,
-                                             drawTexture, drawSubresource, drawArea, drawSize);
+                                             drawTexture, drawSubresource, drawArea, drawSize,
+                                             scissor);
         }
         else if (depthBlit)
         {
-            result = mBlit->copyDepth(readSRV, readArea, readSize, drawDSV, drawArea, drawSize);
+            result = mBlit->copyDepth(readSRV, readArea, readSize, drawDSV, drawArea, drawSize,
+                                      scissor);
         }
         else if (stencilBlit)
         {
             result = mBlit->copyStencil(readTexture, readSubresource, readArea, readSize,
-                                        drawTexture, drawSubresource, drawArea, drawSize);
+                                        drawTexture, drawSubresource, drawArea, drawSize,
+                                        scissor);
         }
         else
         {
             GLenum format = gl::GetFormat(drawRenderTarget->getInternalFormat(), getCurrentClientVersion());
-            result = mBlit->copyTexture(readSRV, readArea, readSize, drawRTV, drawArea, drawSize, format, filter);
+            result = mBlit->copyTexture(readSRV, readArea, readSize, drawRTV, drawArea, drawSize,
+                                        scissor, format, filter);
         }
     }
 
diff --git a/src/libGLESv2/renderer/Renderer11.h b/src/libGLESv2/renderer/Renderer11.h
index e41b12a..c280f2e 100644
--- a/src/libGLESv2/renderer/Renderer11.h
+++ b/src/libGLESv2/renderer/Renderer11.h
@@ -169,7 +169,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 blitDepth, bool blitStencil, GLenum filter);
+                          const gl::Rectangle *scissor, bool blitRenderTarget, bool blitDepth, bool blitStencil, 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);
 
@@ -225,7 +225,8 @@
     rx::Range getViewportBounds() const;
 
     bool blitRenderbufferRect(const gl::Rectangle &readRect, const gl::Rectangle &drawRect, RenderTarget *readRenderTarget,
-                              RenderTarget *drawRenderTarget, GLenum filter, bool colorBlit, bool depthBlit, bool stencilBlit);
+                              RenderTarget *drawRenderTarget, GLenum filter, const gl::Rectangle *scissor,
+                              bool colorBlit, bool depthBlit, bool stencilBlit);
     ID3D11Texture2D *resolveMultisampledTexture(ID3D11Texture2D *source, unsigned int subresource);
 
     HMODULE mD3d11Module;
diff --git a/src/libGLESv2/renderer/Renderer9.cpp b/src/libGLESv2/renderer/Renderer9.cpp
index 522700b..c699fc9 100644
--- a/src/libGLESv2/renderer/Renderer9.cpp
+++ b/src/libGLESv2/renderer/Renderer9.cpp
@@ -2715,7 +2715,7 @@
 }
 
 bool Renderer9::blitRect(gl::Framebuffer *readFramebuffer, const gl::Rectangle &readRect, gl::Framebuffer *drawFramebuffer, const gl::Rectangle &drawRect,
-                         bool blitRenderTarget, bool blitDepth, bool blitStencil, GLenum filter)
+                         const gl::Rectangle *scissor, bool blitRenderTarget, bool blitDepth, bool blitStencil, GLenum filter)
 {
     ASSERT(filter == GL_NEAREST);
 
@@ -2754,6 +2754,9 @@
             return gl::error(GL_OUT_OF_MEMORY, false);
         }
 
+        gl::Extents srcSize(readRenderTarget->getWidth(), readRenderTarget->getHeight(), 1);
+        gl::Extents dstSize(drawRenderTarget->getWidth(), drawRenderTarget->getHeight(), 1);
+
         RECT srcRect;
         srcRect.left = readRect.x;
         srcRect.right = readRect.x + readRect.width;
@@ -2766,6 +2769,75 @@
         dstRect.top = drawRect.y;
         dstRect.bottom = drawRect.y + drawRect.height;
 
+        // Clip the rectangles to the scissor rectangle
+        if (scissor)
+        {
+            if (dstRect.left < scissor->x)
+            {
+                srcRect.left += (scissor->x - dstRect.left);
+                dstRect.left = scissor->x;
+            }
+            if (dstRect.top < scissor->y)
+            {
+                srcRect.top += (scissor->y - dstRect.top);
+                dstRect.top = scissor->y;
+            }
+            if (dstRect.right > scissor->x + scissor->width)
+            {
+                srcRect.right -= (dstRect.right - (scissor->x + scissor->width));
+                dstRect.right = scissor->x + scissor->width;
+            }
+            if (dstRect.bottom > scissor->y + scissor->height)
+            {
+                srcRect.bottom -= (dstRect.bottom - (scissor->y + scissor->height));
+                dstRect.bottom = scissor->y + scissor->height;
+            }
+        }
+
+        // Clip the rectangles to the destination size
+        if (dstRect.left < 0)
+        {
+            srcRect.left += -dstRect.left;
+            dstRect.left = 0;
+        }
+        if (dstRect.right > dstSize.width)
+        {
+            srcRect.right -= (dstRect.right - dstSize.width);
+            dstRect.right = dstSize.width;
+        }
+        if (dstRect.top < 0)
+        {
+            srcRect.top += -dstRect.top;
+            dstRect.top = 0;
+        }
+        if (dstRect.bottom > dstSize.height)
+        {
+            srcRect.bottom -= (dstRect.bottom - dstSize.height);
+            dstRect.bottom = dstSize.height;
+        }
+
+        // Clip the rectangles to the source size
+        if (srcRect.left < 0)
+        {
+            dstRect.left += -srcRect.left;
+            srcRect.left = 0;
+        }
+        if (srcRect.right > srcSize.width)
+        {
+            dstRect.right -= (srcRect.right - srcSize.width);
+            srcRect.right = srcSize.width;
+        }
+        if (srcRect.top < 0)
+        {
+            dstRect.top += -srcRect.top;
+            srcRect.top = 0;
+        }
+        if (srcRect.bottom > srcSize.height)
+        {
+            dstRect.bottom -= (srcRect.bottom - srcSize.height);
+            srcRect.bottom = srcSize.height;
+        }
+
         HRESULT result = mDevice->StretchRect(readSurface, &srcRect, drawSurface, &dstRect, D3DTEXF_NONE);
 
         SafeRelease(readSurface);
diff --git a/src/libGLESv2/renderer/Renderer9.h b/src/libGLESv2/renderer/Renderer9.h
index 99902d7..0a4c2b2 100644
--- a/src/libGLESv2/renderer/Renderer9.h
+++ b/src/libGLESv2/renderer/Renderer9.h
@@ -186,7 +186,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 blitDepth, bool blitStencil, GLenum filter);
+                          const gl::Rectangle *scissor, bool blitRenderTarget, bool blitDepth, bool blitStencil, 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);
 
diff --git a/src/libGLESv2/renderer/TextureStorage11.cpp b/src/libGLESv2/renderer/TextureStorage11.cpp
index 5a7054e..ed73525 100644
--- a/src/libGLESv2/renderer/TextureStorage11.cpp
+++ b/src/libGLESv2/renderer/TextureStorage11.cpp
@@ -140,7 +140,8 @@
             Blit11 *blitter = mRenderer->getBlitter();
 
             return blitter->copyDepthStencil(srcTexture, sourceSubresource, copyArea, texSize,
-                                             dstTexture, dstSubresource, copyArea, texSize);
+                                             dstTexture, dstSubresource, copyArea, texSize,
+                                             NULL);
         }
         else
         {
@@ -180,7 +181,7 @@
 
             Blit11 *blitter = mRenderer->getBlitter();
 
-            blitter->copyTexture(sourceSRV, sourceArea, sourceSize, destRTV, destArea, destSize,
+            blitter->copyTexture(sourceSRV, sourceArea, sourceSize, destRTV, destArea, destSize, NULL,
                                  gl::GetFormat(source->getInternalFormat(), mRenderer->getCurrentClientVersion()),
                                  GL_LINEAR);
         }
diff --git a/src/libGLESv2/validationES.cpp b/src/libGLESv2/validationES.cpp
index eacdd9b..93edd45 100644
--- a/src/libGLESv2/validationES.cpp
+++ b/src/libGLESv2/validationES.cpp
@@ -98,6 +98,31 @@
     return true;
 }
 
+static bool IsPartialBlit(gl::Context *context, gl::Renderbuffer *readBuffer, gl::Renderbuffer *writeBuffer,
+                          GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+                          GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1)
+{
+    if (srcX0 != 0 || srcY0 != 0 || dstX0 != 0 || dstY0 != 0 ||
+        dstX1 != writeBuffer->getWidth() || dstY1 != writeBuffer->getHeight() ||
+        srcX1 != readBuffer->getWidth() || srcY1 != readBuffer->getHeight())
+    {
+        return true;
+    }
+    else if (context->isScissorTestEnabled())
+    {
+        int scissorX, scissorY, scissorWidth, scissorHeight;
+        context->getScissorParams(&scissorX, &scissorY, &scissorWidth, &scissorHeight);
+
+        return scissorX > 0 || scissorY > 0 ||
+               scissorWidth < writeBuffer->getWidth() ||
+               scissorHeight < writeBuffer->getHeight();
+    }
+    else
+    {
+        return false;
+    }
+}
+
 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)
@@ -164,14 +189,6 @@
         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();
@@ -249,8 +266,9 @@
                         }
                     }
                 }
-
-                if (partialCopy && readFramebuffer->getSamples() != 0)
+                if (readFramebuffer->getSamples() != 0 && IsPartialBlit(context, readColorBuffer, drawColorBuffer,
+                                                                        srcX0, srcY0, srcX1, srcY1,
+                                                                        dstX0, dstY0, dstX1, dstY1))
                 {
                     return gl::error(GL_INVALID_OPERATION, false);
                 }
@@ -277,7 +295,8 @@
 
             if (fromAngleExtension)
             {
-                if (partialCopy)
+                if (IsPartialBlit(context, readDepthBuffer, drawDepthBuffer,
+                                  srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1))
                 {
                     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
@@ -296,12 +315,6 @@
         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())
@@ -316,7 +329,8 @@
 
             if (fromAngleExtension)
             {
-                if (partialCopy)
+                if (IsPartialBlit(context, readStencilBuffer, drawStencilBuffer,
+                                  srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1))
                 {
                     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