Refactored the Renderer11::copyTexture method into a Blit11 class.

TRAC #23256

Signed-off-by: Jamie Madill
Signed-off-by: Shannon Woods
Author: Geoff Lang
diff --git a/src/libGLESv2/renderer/Blit11.cpp b/src/libGLESv2/renderer/Blit11.cpp
new file mode 100644
index 0000000..d309022
--- /dev/null
+++ b/src/libGLESv2/renderer/Blit11.cpp
@@ -0,0 +1,404 @@
+#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.
+//
+
+// Blit11.cpp: Texture copy utility class.
+
+#include "libGLESv2/main.h"
+#include "libGLESv2/formatutils.h"
+#include "libGLESv2/renderer/Blit11.h"
+#include "libGLESv2/renderer/Renderer11.h"
+#include "libGLESv2/renderer/renderer11_utils.h"
+
+#include "libGLESv2/renderer/shaders/compiled/passthrough2d11vs.h"
+#include "libGLESv2/renderer/shaders/compiled/passthroughrgba2d11ps.h"
+#include "libGLESv2/renderer/shaders/compiled/passthroughrgb2d11ps.h"
+#include "libGLESv2/renderer/shaders/compiled/passthroughlum2d11ps.h"
+#include "libGLESv2/renderer/shaders/compiled/passthroughlumalpha2d11ps.h"
+
+#include "libGLESv2/renderer/shaders/compiled/passthrough3d11vs.h"
+#include "libGLESv2/renderer/shaders/compiled/passthrough3d11gs.h"
+#include "libGLESv2/renderer/shaders/compiled/passthroughrgba3d11ps.h"
+#include "libGLESv2/renderer/shaders/compiled/passthroughrgb3d11ps.h"
+#include "libGLESv2/renderer/shaders/compiled/passthroughlum3d11ps.h"
+#include "libGLESv2/renderer/shaders/compiled/passthroughlumalpha3d11ps.h"
+
+namespace rx
+{
+
+Blit11::Blit11(rx::Renderer11 *renderer)
+    : mRenderer(renderer), mShaderMap(compareBlitParameters), mVertexBuffer(NULL),
+      mPointSampler(NULL), mLinearSampler(NULL), mQuad2DIL(NULL), mQuad2DVS(NULL),
+      mQuad3DIL(NULL), mQuad3DVS(NULL), mQuad3DGS(NULL)
+{
+    HRESULT result;
+    ID3D11Device *device = mRenderer->getDevice();
+
+    D3D11_BUFFER_DESC vbDesc;
+    vbDesc.ByteWidth = std::max(sizeof(d3d11::PositionLayerTexCoord3DVertex) * 6 * renderer->getMaxTextureDepth(),
+                                sizeof(d3d11::PositionTexCoordVertex) * 4);
+    vbDesc.Usage = D3D11_USAGE_DYNAMIC;
+    vbDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+    vbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+    vbDesc.MiscFlags = 0;
+    vbDesc.StructureByteStride = 0;
+
+    result = device->CreateBuffer(&vbDesc, NULL, &mVertexBuffer);
+    ASSERT(SUCCEEDED(result));
+    d3d11::SetDebugName(mVertexBuffer, "Blit11 vertex buffer");
+
+    D3D11_SAMPLER_DESC pointSamplerDesc;
+    pointSamplerDesc.Filter = D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR;
+    pointSamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
+    pointSamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
+    pointSamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
+    pointSamplerDesc.MipLODBias = 0.0f;
+    pointSamplerDesc.MaxAnisotropy = 0;
+    pointSamplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
+    pointSamplerDesc.BorderColor[0] = 0.0f;
+    pointSamplerDesc.BorderColor[1] = 0.0f;
+    pointSamplerDesc.BorderColor[2] = 0.0f;
+    pointSamplerDesc.BorderColor[3] = 0.0f;
+    pointSamplerDesc.MinLOD = 0.0f;
+    pointSamplerDesc.MaxLOD = 0.0f;
+
+    result = device->CreateSamplerState(&pointSamplerDesc, &mPointSampler);
+    ASSERT(SUCCEEDED(result));
+    d3d11::SetDebugName(mPointSampler, "Blit11 point sampler");
+
+    D3D11_SAMPLER_DESC linearSamplerDesc;
+    linearSamplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+    linearSamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
+    linearSamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
+    linearSamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
+    linearSamplerDesc.MipLODBias = 0.0f;
+    linearSamplerDesc.MaxAnisotropy = 0;
+    linearSamplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
+    linearSamplerDesc.BorderColor[0] = 0.0f;
+    linearSamplerDesc.BorderColor[1] = 0.0f;
+    linearSamplerDesc.BorderColor[2] = 0.0f;
+    linearSamplerDesc.BorderColor[3] = 0.0f;
+    linearSamplerDesc.MinLOD = 0.0f;
+    linearSamplerDesc.MaxLOD = 0.0f;
+
+    result = device->CreateSamplerState(&linearSamplerDesc, &mLinearSampler);
+    ASSERT(SUCCEEDED(result));
+    d3d11::SetDebugName(mLinearSampler, "Blit11 linear sampler");
+
+    D3D11_INPUT_ELEMENT_DESC quad2DLayout[] =
+    {
+        { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+        { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+    };
+
+    result = device->CreateInputLayout(quad2DLayout, ArraySize(quad2DLayout), g_VS_Passthrough2D, ArraySize(g_VS_Passthrough2D), &mQuad2DIL);
+    ASSERT(SUCCEEDED(result));
+    d3d11::SetDebugName(mQuad2DIL, "Blit11 2D input layout");
+
+    result = device->CreateVertexShader(g_VS_Passthrough2D, ArraySize(g_VS_Passthrough2D), NULL, &mQuad2DVS);
+    ASSERT(SUCCEEDED(result));
+    d3d11::SetDebugName(mQuad2DVS, "Blit11 2D vertex shader");
+
+    D3D11_INPUT_ELEMENT_DESC quad3DLayout[] =
+    {
+        { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT,    0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+        { "LAYER",    0, DXGI_FORMAT_R32_UINT,        0,  8, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+        { "TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+    };
+
+    result = device->CreateInputLayout(quad3DLayout, ArraySize(quad3DLayout), g_VS_Passthrough3D, ArraySize(g_VS_Passthrough3D), &mQuad3DIL);
+    ASSERT(SUCCEEDED(result));
+    d3d11::SetDebugName(mQuad3DIL, "Blit11 3D input layout");
+
+    result = device->CreateVertexShader(g_VS_Passthrough3D, ArraySize(g_VS_Passthrough3D), NULL, &mQuad3DVS);
+    ASSERT(SUCCEEDED(result));
+    d3d11::SetDebugName(mQuad3DVS, "Blit11 3D vertex shader");
+
+    result = device->CreateGeometryShader(g_GS_Passthrough3D, ArraySize(g_GS_Passthrough3D), NULL, &mQuad3DGS);
+    ASSERT(SUCCEEDED(result));
+    d3d11::SetDebugName(mQuad3DGS, "Renderer11 copy 3D texture geometry shader");
+
+    buildShaderMap();
+}
+
+Blit11::~Blit11()
+{
+    SafeRelease(mVertexBuffer);
+    SafeRelease(mPointSampler);
+    SafeRelease(mLinearSampler);
+
+    SafeRelease(mQuad2DIL);
+    SafeRelease(mQuad2DVS);
+
+    SafeRelease(mQuad3DIL);
+    SafeRelease(mQuad3DVS);
+    SafeRelease(mQuad3DGS);
+
+    clearShaderMap();
+}
+
+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)
+{
+    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();
+
+    BlitParameters parameters = { 0 };
+    parameters.mDestinationFormat = destFormat;
+    parameters.mSignedInteger = false;
+    parameters.m3DBlit = sourceArea.depth > 1;
+
+    BlitShaderMap::const_iterator i = mShaderMap.find(parameters);
+    if (i == mShaderMap.end())
+    {
+        UNREACHABLE();
+        return false;
+    }
+
+    const BlitShader& shader = i->second;
+
+    // Set vertices
+    D3D11_MAPPED_SUBRESOURCE mappedResource;
+    result = deviceContext->Map(mVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
+    if (FAILED(result))
+    {
+        ERR("Failed to map vertex buffer for texture copy, HRESULT: 0x%X.", result);
+        return false;
+    }
+
+    UINT stride = 0;
+    UINT startIdx = 0;
+    UINT drawCount = 0;
+    D3D11_PRIMITIVE_TOPOLOGY topology;
+
+    shader.mVertexWriteFunction(sourceArea, sourceSize, destArea, destSize, mappedResource.pData,
+                                &stride, &drawCount, &topology);
+
+    deviceContext->Unmap(mVertexBuffer, 0);
+
+    // Apply vertex buffer
+    deviceContext->IASetVertexBuffers(0, 1, &mVertexBuffer, &stride, &startIdx);
+
+    // Apply state
+    deviceContext->OMSetBlendState(NULL, NULL, 0xFFFFFFF);
+    deviceContext->OMSetDepthStencilState(NULL, 0xFFFFFFFF);
+    deviceContext->RSSetState(NULL);
+
+    // Apply shaders
+    deviceContext->IASetInputLayout(shader.mInputLayout);
+    deviceContext->IASetPrimitiveTopology(topology);
+    deviceContext->VSSetShader(shader.mVertexShader, NULL, 0);
+
+    deviceContext->PSSetShader(shader.mPixelShader, NULL, 0);
+    deviceContext->GSSetShader(shader.mGeometryShader, NULL, 0);
+
+    // Unset the currently bound shader resource to avoid conflicts
+    ID3D11ShaderResourceView *const nullSRV = NULL;
+    deviceContext->PSSetShaderResources(0, 1, &nullSRV);
+
+    // Apply render target
+    mRenderer->setOneTimeRenderTarget(dest);
+
+    // Set the viewport
+    D3D11_VIEWPORT viewport;
+    viewport.TopLeftX = 0;
+    viewport.TopLeftY = 0;
+    viewport.Width = destSize.width;
+    viewport.Height = destSize.height;
+    viewport.MinDepth = 0.0f;
+    viewport.MaxDepth = 1.0f;
+    deviceContext->RSSetViewports(1, &viewport);
+
+    // Apply textures
+    deviceContext->PSSetShaderResources(0, 1, &source);
+
+    // Apply samplers
+    ID3D11SamplerState *sampler = NULL;
+    switch (filter)
+    {
+      case GL_NEAREST: sampler = mPointSampler;  break;
+      case GL_LINEAR:  sampler = mLinearSampler; break;
+      default:         UNREACHABLE(); return false;
+    }
+    deviceContext->PSSetSamplers(0, 1, &sampler);
+
+    // Draw the quad
+    deviceContext->Draw(drawCount, 0);
+
+    // Unbind textures and render targets and vertex buffer
+    deviceContext->PSSetShaderResources(0, 1, &nullSRV);
+
+    mRenderer->unapplyRenderTargets();
+
+    UINT zero = 0;
+    ID3D11Buffer *const nullBuffer = NULL;
+    deviceContext->IASetVertexBuffers(0, 1, &nullBuffer, &zero, &zero);
+
+    mRenderer->markAllStateDirty();
+
+    return true;
+}
+
+bool Blit11::compareBlitParameters(const Blit11::BlitParameters &a, const Blit11::BlitParameters &b)
+{
+    return memcmp(&a, &b, sizeof(Blit11::BlitParameters)) < 0;
+}
+
+template <unsigned int N>
+static ID3D11PixelShader *compilePS(ID3D11Device *device, const BYTE (&byteCode)[N], const char *name)
+{
+    ID3D11PixelShader *ps = NULL;
+    HRESULT result = device->CreatePixelShader(byteCode, N, NULL, &ps);
+    ASSERT(SUCCEEDED(result));
+    d3d11::SetDebugName(ps, name);
+    return ps;
+}
+
+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,
+                                        float *u1, float *v1, float *u2, float *v2)
+{
+    *x1 = (destArea.x / float(destSize.width)) * 2.0f - 1.0f;
+    *y1 = ((destSize.height - destArea.y - destArea.height) / float(destSize.height)) * 2.0f - 1.0f;
+    *x2 = ((destArea.x + destArea.width) / float(destSize.width)) * 2.0f - 1.0f;
+    *y2 = ((destSize.height - destArea.y) / float(destSize.height)) * 2.0f - 1.0f;
+
+    *u1 = sourceArea.x / float(sourceSize.width);
+    *v1 = sourceArea.y / float(sourceSize.height);
+    *u2 = (sourceArea.x + sourceArea.width) / float(sourceSize.width);
+    *v2 = (sourceArea.y + sourceArea.height) / float(sourceSize.height);
+}
+
+static void write2DVertices(const gl::Box &sourceArea, const gl::Extents &sourceSize,
+                            const gl::Box &destArea, const gl::Extents &destSize,
+                            void *outVertices, unsigned int *outStride, unsigned int *outVertexCount,
+                            D3D11_PRIMITIVE_TOPOLOGY *outTopology)
+{
+    float x1, y1, x2, y2, u1, v1, u2, v2;
+    generateVertexCoords(sourceArea, sourceSize, destArea, destSize, &x1, &y1, &x2, &y2, &u1, &v1, &u2, &v2);
+
+    d3d11::PositionTexCoordVertex *vertices = static_cast<d3d11::PositionTexCoordVertex*>(outVertices);
+
+    d3d11::SetPositionTexCoordVertex(&vertices[0], x1, y1, u1, v2);
+    d3d11::SetPositionTexCoordVertex(&vertices[1], x1, y2, u1, v1);
+    d3d11::SetPositionTexCoordVertex(&vertices[2], x2, y1, u2, v2);
+    d3d11::SetPositionTexCoordVertex(&vertices[3], x2, y2, u2, v1);
+
+    *outStride = sizeof(d3d11::PositionTexCoordVertex);
+    *outVertexCount = 4;
+    *outTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+}
+
+static void write3DVertices(const gl::Box &sourceArea, const gl::Extents &sourceSize,
+                            const gl::Box &destArea, const gl::Extents &destSize,
+                            void *outVertices, unsigned int *outStride, unsigned int *outVertexCount,
+                            D3D11_PRIMITIVE_TOPOLOGY *outTopology)
+{
+    float x1, y1, x2, y2, u1, v1, u2, v2;
+    generateVertexCoords(sourceArea, sourceSize, destArea, destSize, &x1, &y1, &x2, &y2, &u1, &v1, &u2, &v2);
+
+    d3d11::PositionLayerTexCoord3DVertex *vertices = static_cast<d3d11::PositionLayerTexCoord3DVertex*>(outVertices);
+
+    for (int i = 0; i < destSize.depth; i++)
+    {
+        float readDepth = ((i * 2) + 0.5f) / (sourceSize.depth - 1);
+
+        d3d11::SetPositionLayerTexCoord3DVertex(&vertices[i * 6 + 0], x1, y1, i, u1, v2, readDepth);
+        d3d11::SetPositionLayerTexCoord3DVertex(&vertices[i * 6 + 1], x1, y2, i, u1, v1, readDepth);
+        d3d11::SetPositionLayerTexCoord3DVertex(&vertices[i * 6 + 2], x2, y1, i, u2, v2, readDepth);
+
+        d3d11::SetPositionLayerTexCoord3DVertex(&vertices[i * 6 + 3], x1, y2, i, u1, v1, readDepth);
+        d3d11::SetPositionLayerTexCoord3DVertex(&vertices[i * 6 + 4], x2, y2, i, u2, v1, readDepth);
+        d3d11::SetPositionLayerTexCoord3DVertex(&vertices[i * 6 + 5], x2, y1, i, u2, v2, readDepth);
+    }
+
+    *outStride = sizeof(d3d11::PositionLayerTexCoord3DVertex);
+    *outVertexCount = destSize.depth * 6;
+    *outTopology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+}
+
+void Blit11::add2DShaderToMap(GLenum destFormat, bool signedInteger, ID3D11PixelShader *ps)
+{
+    BlitParameters params = { 0 };
+    params.mDestinationFormat = destFormat;
+    params.mSignedInteger = signedInteger;
+    params.m3DBlit = false;
+
+    ASSERT(mShaderMap.find(params) == mShaderMap.end());
+    ASSERT(ps);
+
+    BlitShader shader;
+    shader.mVertexWriteFunction = write2DVertices;
+    shader.mInputLayout = mQuad2DIL;
+    shader.mVertexShader = mQuad2DVS;
+    shader.mGeometryShader = NULL;
+    shader.mPixelShader = ps;
+
+    mShaderMap[params] = shader;
+}
+
+void Blit11::add3DShaderToMap(GLenum destFormat, bool signedInteger, ID3D11PixelShader *ps)
+{
+    BlitParameters params = { 0 };
+    params.mDestinationFormat = destFormat;
+    params.mSignedInteger = signedInteger;
+    params.m3DBlit = true;
+
+    ASSERT(mShaderMap.find(params) == mShaderMap.end());
+    ASSERT(ps);
+
+    BlitShader shader;
+    shader.mVertexWriteFunction = write3DVertices;
+    shader.mInputLayout = mQuad3DIL;
+    shader.mVertexShader = mQuad3DVS;
+    shader.mGeometryShader = mQuad3DGS;
+    shader.mPixelShader = ps;
+
+    mShaderMap[params] = shader;
+}
+
+void Blit11::buildShaderMap()
+{
+    ID3D11Device *device = mRenderer->getDevice();
+
+    add2DShaderToMap(GL_RGBA,            false, compilePS(device, g_PS_PassthroughRGBA2D,     "Blit11 2D RGBA pixel shader"           ));
+    add2DShaderToMap(GL_BGRA_EXT,        false, compilePS(device, g_PS_PassthroughRGBA2D,     "Blit11 2D BGRA pixel shader"           ));
+    add2DShaderToMap(GL_RGB,             false, compilePS(device, g_PS_PassthroughRGB2D,      "Blit11 2D RGB pixel shader"            ));
+    add2DShaderToMap(GL_ALPHA,           false, compilePS(device, g_PS_PassthroughRGBA2D,     "Blit11 2D alpha pixel shader"          ));
+    add2DShaderToMap(GL_LUMINANCE,       false, compilePS(device, g_PS_PassthroughLum2D,      "Blit11 2D lum pixel shader"            ));
+    add2DShaderToMap(GL_LUMINANCE_ALPHA, false, compilePS(device, g_PS_PassthroughLumAlpha2D, "Blit11 2D luminance alpha pixel shader"));
+
+    add3DShaderToMap(GL_RGBA,            false, compilePS(device, g_PS_PassthroughRGBA3D,     "Blit11 3D RGBA pixel shader"           ));
+    add3DShaderToMap(GL_BGRA_EXT,        false, compilePS(device, g_PS_PassthroughRGBA3D,     "Blit11 3D BGRA pixel shader"           ));
+    add3DShaderToMap(GL_RGB,             false, compilePS(device, g_PS_PassthroughRGB3D,      "Blit11 3D RGB pixel shader"            ));
+    add3DShaderToMap(GL_ALPHA,           false, compilePS(device, g_PS_PassthroughRGBA3D,     "Blit11 3D alpha pixel shader"          ));
+    add3DShaderToMap(GL_LUMINANCE,       false, compilePS(device, g_PS_PassthroughLum3D,      "Blit11 3D luminance pixel shader"      ));
+    add3DShaderToMap(GL_LUMINANCE_ALPHA, false, compilePS(device, g_PS_PassthroughLumAlpha3D, "Blit11 3D luminance alpha pixel shader"));
+}
+
+void Blit11::clearShaderMap()
+{
+    for (BlitShaderMap::iterator i = mShaderMap.begin(); i != mShaderMap.end(); ++i)
+    {
+        BlitShader &shader = i->second;
+        SafeRelease(shader.mPixelShader);
+    }
+    mShaderMap.clear();
+}
+
+}
diff --git a/src/libGLESv2/renderer/Blit11.h b/src/libGLESv2/renderer/Blit11.h
new file mode 100644
index 0000000..a3a3977
--- /dev/null
+++ b/src/libGLESv2/renderer/Blit11.h
@@ -0,0 +1,86 @@
+//
+// 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.
+//
+
+// Blit11.cpp: Texture copy utility class.
+
+#ifndef LIBGLESV2_BLIT11_H_
+#define LIBGLESV2_BLIT11_H_
+
+#include "common/angleutils.h"
+#include "libGLESv2/angletypes.h"
+
+namespace rx
+{
+class Renderer11;
+
+enum Filter
+{
+    Point,
+    Linear,
+};
+
+class Blit11
+{
+  public:
+    explicit Blit11(Renderer11 *renderer);
+    ~Blit11();
+
+    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);
+
+  private:
+    rx::Renderer11 *mRenderer;
+
+    struct BlitParameters
+    {
+        GLenum mDestinationFormat;
+        bool mSignedInteger;
+        bool m3DBlit;
+    };
+
+    static bool compareBlitParameters(const BlitParameters &a, const BlitParameters &b);
+
+    typedef void (*WriteVertexFunction)(const gl::Box &sourceArea, const gl::Extents &sourceSize,
+                                        const gl::Box &destArea, const gl::Extents &destSize,
+                                        void *outVertices, unsigned int *outStride, unsigned int *outVertexCount,
+                                        D3D11_PRIMITIVE_TOPOLOGY *outTopology);
+
+    struct BlitShader
+    {
+        WriteVertexFunction mVertexWriteFunction;
+        ID3D11InputLayout *mInputLayout;
+        ID3D11VertexShader *mVertexShader;
+        ID3D11GeometryShader *mGeometryShader;
+        ID3D11PixelShader *mPixelShader;
+    };
+
+    typedef bool (*BlitParametersComparisonFunction)(const BlitParameters&, const BlitParameters &);
+    typedef std::map<BlitParameters, BlitShader, BlitParametersComparisonFunction> BlitShaderMap;
+    BlitShaderMap mShaderMap;
+
+    void add2DShaderToMap(GLenum destFormat, bool signedInteger, ID3D11PixelShader *ps);
+    void add3DShaderToMap(GLenum destFormat, bool signedInteger, ID3D11PixelShader *ps);
+
+    void buildShaderMap();
+    void clearShaderMap();
+
+    ID3D11Buffer *mVertexBuffer;
+    ID3D11SamplerState *mPointSampler;
+    ID3D11SamplerState *mLinearSampler;
+
+    ID3D11InputLayout *mQuad2DIL;
+    ID3D11VertexShader *mQuad2DVS;
+
+    ID3D11InputLayout *mQuad3DIL;
+    ID3D11VertexShader *mQuad3DVS;
+    ID3D11GeometryShader *mQuad3DGS;
+
+    DISALLOW_COPY_AND_ASSIGN(Blit11);
+};
+}
+
+#endif   // LIBGLESV2_BLIT11_H_
diff --git a/src/libGLESv2/renderer/Renderer11.cpp b/src/libGLESv2/renderer/Renderer11.cpp
index 6a380cf..f093b81 100644
--- a/src/libGLESv2/renderer/Renderer11.cpp
+++ b/src/libGLESv2/renderer/Renderer11.cpp
@@ -28,19 +28,7 @@
 #include "libGLESv2/renderer/TextureStorage11.h"
 #include "libGLESv2/renderer/Query11.h"
 #include "libGLESv2/renderer/Fence11.h"
-
-#include "libGLESv2/renderer/shaders/compiled/passthrough2d11vs.h"
-#include "libGLESv2/renderer/shaders/compiled/passthroughrgba2d11ps.h"
-#include "libGLESv2/renderer/shaders/compiled/passthroughrgb2d11ps.h"
-#include "libGLESv2/renderer/shaders/compiled/passthroughlum2d11ps.h"
-#include "libGLESv2/renderer/shaders/compiled/passthroughlumalpha2d11ps.h"
-
-#include "libGLESv2/renderer/shaders/compiled/passthrough3d11vs.h"
-#include "libGLESv2/renderer/shaders/compiled/passthrough3d11gs.h"
-#include "libGLESv2/renderer/shaders/compiled/passthroughrgba3d11ps.h"
-#include "libGLESv2/renderer/shaders/compiled/passthroughrgb3d11ps.h"
-#include "libGLESv2/renderer/shaders/compiled/passthroughlum3d11ps.h"
-#include "libGLESv2/renderer/shaders/compiled/passthroughlumalpha3d11ps.h"
+#include "libGLESv2/renderer/Blit11.h"
 
 #include "libGLESv2/renderer/shaders/compiled/clear11vs.h"
 #include "libGLESv2/renderer/shaders/compiled/clearsingle11ps.h"
@@ -82,22 +70,7 @@
     mLineLoopIB = NULL;
     mTriangleFanIB = NULL;
 
-    mCopyResourcesInitialized = false;
-    mCopyVB = NULL;
-    mCopySampler = NULL;
-    mCopy2DIL = NULL;
-    mCopy2DVS = NULL;
-    mCopyRGBA2DPS = NULL;
-    mCopyRGB2DPS = NULL;
-    mCopyLum2DPS = NULL;
-    mCopyLumAlpha2DPS = NULL;
-    mCopy3DIL = NULL;
-    mCopy3DVS = NULL;
-    mCopy3DGS = NULL;
-    mCopyRGBA3DPS = NULL;
-    mCopyRGB3DPS = NULL;
-    mCopyLum3DPS = NULL;
-    mCopyLumAlpha3DPS = NULL;
+    mBlit = NULL;
 
     mClearResourcesInitialized = false;
     mClearVB = NULL;
@@ -424,6 +397,9 @@
     mVertexDataManager = new VertexDataManager(this);
     mIndexDataManager = new IndexDataManager(this);
 
+    ASSERT(!mBlit);
+    mBlit = new Blit11(this);
+
     markAllStateDirty();
 }
 
@@ -1844,23 +1820,8 @@
     delete mTriangleFanIB;
     mTriangleFanIB = NULL;
 
-    SafeRelease(mCopyVB);
-    SafeRelease(mCopySampler);
-
-    SafeRelease(mCopy2DIL);
-    SafeRelease(mCopy2DVS);
-    SafeRelease(mCopyRGBA2DPS);
-    SafeRelease(mCopyRGB2DPS);
-    SafeRelease(mCopyLum2DPS);
-    SafeRelease(mCopyLumAlpha2DPS);
-    SafeRelease(mCopy3DIL);
-    SafeRelease(mCopy3DVS);
-    SafeRelease(mCopy3DGS);
-    SafeRelease(mCopyRGBA3DPS);
-    SafeRelease(mCopyRGB3DPS);
-    SafeRelease(mCopyLum3DPS);
-    SafeRelease(mCopyLumAlpha3DPS);
-    mCopyResourcesInitialized = false;
+    delete mBlit;
+    mBlit = NULL;
 
     SafeRelease(mClearVB);
     SafeRelease(mClearIL);
@@ -2639,24 +2600,16 @@
         return gl::error(GL_OUT_OF_MEMORY, false);
     }
 
-    gl::Box sourceArea;
-    sourceArea.x = sourceRect.x;
-    sourceArea.y = sourceRect.y;
-    sourceArea.z = 0;
-    sourceArea.width = sourceRect.width;
-    sourceArea.height = sourceRect.height;
-    sourceArea.depth = 1;
+    gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1);
+    gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1);
 
-    gl::Box destArea;
-    destArea.x = xoffset;
-    destArea.y = yoffset;
-    destArea.z = 0;
-    destArea.width = sourceRect.width;
-    destArea.height = sourceRect.height;
-    destArea.depth = 1;
+    gl::Box destArea(xoffset, yoffset, 0, sourceRect.width, sourceRect.height, 1);
+    gl::Extents destSize(destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1);
 
-    bool ret = copyTexture(source, sourceArea, sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1,
-                           dest, destArea, destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1, destFormat);
+    // 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,
+                                  destFormat, GL_NEAREST);
 
     source->Release();
     dest->Release();
@@ -2712,24 +2665,16 @@
         return gl::error(GL_OUT_OF_MEMORY, false);
     }
 
-    gl::Box sourceArea;
-    sourceArea.x = sourceRect.x;
-    sourceArea.y = sourceRect.y;
-    sourceArea.z = 0;
-    sourceArea.width = sourceRect.width;
-    sourceArea.height = sourceRect.height;
-    sourceArea.depth = 1;
+    gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1);
+    gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1);
 
-    gl::Box destArea;
-    destArea.x = xoffset;
-    destArea.y = yoffset;
-    destArea.z = 0;
-    destArea.width = sourceRect.width;
-    destArea.height = sourceRect.height;
-    destArea.depth = 1;
+    gl::Box destArea(xoffset, yoffset, 0, sourceRect.width, sourceRect.height, 1);
+    gl::Extents destSize(destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1);
 
-    bool ret = copyTexture(source, sourceArea, sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1,
-                           dest, destArea, destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1, destFormat);
+    // 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,
+                                  destFormat, GL_NEAREST);
 
     source->Release();
     dest->Release();
@@ -2785,24 +2730,16 @@
         return gl::error(GL_OUT_OF_MEMORY, false);
     }
 
-    gl::Box sourceArea;
-    sourceArea.x = sourceRect.x;
-    sourceArea.y = sourceRect.y;
-    sourceArea.z = 0;
-    sourceArea.width = sourceRect.width;
-    sourceArea.height = sourceRect.height;
-    sourceArea.depth = 1;
+    gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1);
+    gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1);
 
-    gl::Box destArea;
-    destArea.x = xoffset;
-    destArea.y = yoffset;
-    destArea.z = 0;
-    destArea.width = sourceRect.width;
-    destArea.height = sourceRect.height;
-    destArea.depth = 1;
+    gl::Box destArea(xoffset, yoffset, 0, sourceRect.width, sourceRect.height, 1);
+    gl::Extents destSize(destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1);
 
-    bool ret = copyTexture(source, sourceArea, sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1,
-                           dest, destArea, destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1, destFormat);
+    // 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,
+                                  destFormat, GL_NEAREST);
 
     source->Release();
     dest->Release();
@@ -2858,24 +2795,16 @@
         return gl::error(GL_OUT_OF_MEMORY, false);
     }
 
-    gl::Box sourceArea;
-    sourceArea.x = sourceRect.x;
-    sourceArea.y = sourceRect.y;
-    sourceArea.z = 0;
-    sourceArea.width = sourceRect.width;
-    sourceArea.height = sourceRect.height;
-    sourceArea.depth = 1;
+    gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1);
+    gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1);
 
-    gl::Box destArea;
-    destArea.x = xoffset;
-    destArea.y = yoffset;
-    destArea.z = 0;
-    destArea.width = sourceRect.width;
-    destArea.height = sourceRect.height;
-    destArea.depth = 1;
+    gl::Box destArea(xoffset, yoffset, 0, sourceRect.width, sourceRect.height, 1);
+    gl::Extents destSize(destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1);
 
-    bool ret = copyTexture(source, sourceArea, sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1,
-                           dest, destArea, destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1, destFormat);
+    // 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,
+                                  destFormat, GL_NEAREST);
 
     source->Release();
     dest->Release();
@@ -2883,287 +2812,6 @@
     return ret;
 }
 
-bool Renderer11::copyTexture(ID3D11ShaderResourceView *source, const gl::Box &sourceArea, unsigned int sourceWidth, unsigned int sourceHeight, unsigned int sourceDepth,
-                             ID3D11RenderTargetView *dest, const gl::Box &destArea, unsigned int destWidth, unsigned int destHeight, unsigned int destDepth,
-                             GLenum destFormat)
-{
-    HRESULT result;
-
-    if (!mCopyResourcesInitialized)
-    {
-        ASSERT(!mCopyVB && !mCopySampler && !mCopy2DIL && !mCopy2DVS && !mCopyRGBA2DPS && !mCopyRGB2DPS && !mCopyLum2DPS && !mCopyLumAlpha2DPS &&
-               !mCopy3DIL && !mCopy3DVS && !mCopy3DGS && !mCopyRGBA3DPS && !mCopyRGB3DPS && !mCopyLum3DPS && !mCopyLumAlpha3DPS);
-
-        D3D11_BUFFER_DESC vbDesc;
-        vbDesc.ByteWidth = std::max(sizeof(d3d11::PositionLayerTexCoord3DVertex) * 6 * getMaxTextureDepth(),
-                                    sizeof(d3d11::PositionTexCoordVertex) * 4);
-        vbDesc.Usage = D3D11_USAGE_DYNAMIC;
-        vbDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
-        vbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
-        vbDesc.MiscFlags = 0;
-        vbDesc.StructureByteStride = 0;
-
-        result = mDevice->CreateBuffer(&vbDesc, NULL, &mCopyVB);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopyVB, "Renderer11 copy texture vertex buffer");
-
-        D3D11_SAMPLER_DESC samplerDesc;
-        samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
-        samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
-        samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
-        samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
-        samplerDesc.MipLODBias = 0.0f;
-        samplerDesc.MaxAnisotropy = 0;
-        samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
-        samplerDesc.BorderColor[0] = 0.0f;
-        samplerDesc.BorderColor[1] = 0.0f;
-        samplerDesc.BorderColor[2] = 0.0f;
-        samplerDesc.BorderColor[3] = 0.0f;
-        samplerDesc.MinLOD = 0.0f;
-        samplerDesc.MaxLOD = 0.0f;
-
-        result = mDevice->CreateSamplerState(&samplerDesc, &mCopySampler);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopySampler, "Renderer11 copy sampler");
-
-        // Create 2D copy resources
-        D3D11_INPUT_ELEMENT_DESC quad2DLayout[] =
-        {
-            { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
-            { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0 },
-        };
-
-        result = mDevice->CreateInputLayout(quad2DLayout, ArraySize(quad2DLayout), g_VS_Passthrough2D, sizeof(g_VS_Passthrough2D), &mCopy2DIL);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopy2DIL, "Renderer11 copy 2D texture input layout");
-
-        result = mDevice->CreateVertexShader(g_VS_Passthrough2D, sizeof(g_VS_Passthrough2D), NULL, &mCopy2DVS);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopy2DVS, "Renderer11 copy 2D texture vertex shader");
-
-        result = mDevice->CreatePixelShader(g_PS_PassthroughRGBA2D, sizeof(g_PS_PassthroughRGBA2D), NULL, &mCopyRGBA2DPS);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopyRGBA2DPS, "Renderer11 copy 2D texture RGBA pixel shader");
-
-        result = mDevice->CreatePixelShader(g_PS_PassthroughRGB2D, sizeof(g_PS_PassthroughRGB2D), NULL, &mCopyRGB2DPS);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopyRGB2DPS, "Renderer11 copy 2D texture RGB pixel shader");
-
-        result = mDevice->CreatePixelShader(g_PS_PassthroughLum2D, sizeof(g_PS_PassthroughLum2D), NULL, &mCopyLum2DPS);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopyLum2DPS, "Renderer11 copy 2D texture luminance pixel shader");
-
-        result = mDevice->CreatePixelShader(g_PS_PassthroughLumAlpha2D, sizeof(g_PS_PassthroughLumAlpha2D), NULL, &mCopyLumAlpha2DPS);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopyLumAlpha2DPS, "Renderer11 2D copy texture luminance alpha pixel shader");
-
-        // Create 3D copy resources
-        D3D11_INPUT_ELEMENT_DESC quad3DLayout[] =
-        {
-            { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT,    0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
-            { "LAYER",    0, DXGI_FORMAT_R32_UINT,        0,  8, D3D11_INPUT_PER_VERTEX_DATA, 0 },
-            { "TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
-        };
-
-        result = mDevice->CreateInputLayout(quad3DLayout, ArraySize(quad3DLayout), g_VS_Passthrough3D, sizeof(g_VS_Passthrough3D), &mCopy3DIL);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopy3DIL, "Renderer11 copy 3D texture input layout");
-
-        result = mDevice->CreateVertexShader(g_VS_Passthrough3D, sizeof(g_VS_Passthrough3D), NULL, &mCopy3DVS);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopy3DVS, "Renderer11 copy 3D texture vertex shader");
-
-        result = mDevice->CreateGeometryShader(g_GS_Passthrough3D, sizeof(g_GS_Passthrough3D), NULL, &mCopy3DGS);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopy3DGS, "Renderer11 copy 3D texture geometry shader");
-
-        result = mDevice->CreatePixelShader(g_PS_PassthroughRGBA3D, sizeof(g_PS_PassthroughRGBA3D), NULL, &mCopyRGBA3DPS);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopyRGBA3DPS, "Renderer11 copy 3D texture RGBA pixel shader");
-
-        result = mDevice->CreatePixelShader(g_PS_PassthroughRGB3D, sizeof(g_PS_PassthroughRGB3D), NULL, &mCopyRGB3DPS);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopyRGB3DPS, "Renderer11 copy 3D texture RGB pixel shader");
-
-        result = mDevice->CreatePixelShader(g_PS_PassthroughLum3D, sizeof(g_PS_PassthroughLum3D), NULL, &mCopyLum3DPS);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopyLum3DPS, "Renderer11 copy 3D texture luminance pixel shader");
-
-        result = mDevice->CreatePixelShader(g_PS_PassthroughLumAlpha3D, sizeof(g_PS_PassthroughLumAlpha3D), NULL, &mCopyLumAlpha3DPS);
-        ASSERT(SUCCEEDED(result));
-        d3d11::SetDebugName(mCopyLumAlpha3DPS, "Renderer11 3D copy texture luminance alpha pixel shader");
-
-        mCopyResourcesInitialized = true;
-    }
-
-    // Verify the source and destination area sizes
-    if (sourceArea.x < 0 || sourceArea.x + sourceArea.width > static_cast<int>(sourceWidth) ||
-        sourceArea.y < 0 || sourceArea.y + sourceArea.height > static_cast<int>(sourceHeight) ||
-        sourceArea.z < 0 || sourceArea.z + sourceArea.depth > static_cast<int>(sourceDepth) ||
-        destArea.x < 0 || destArea.x + destArea.width > static_cast<int>(destWidth) ||
-        destArea.y < 0 || destArea.y + destArea.height > static_cast<int>(destHeight) ||
-        destArea.z < 0 || destArea.z + destArea.depth > static_cast<int>(destDepth))
-    {
-        return gl::error(GL_INVALID_VALUE, false);
-    }
-
-    bool use3DCopy = sourceArea.depth > 1;
-
-    // Set vertices
-    D3D11_MAPPED_SUBRESOURCE mappedResource;
-    result = mDeviceContext->Map(mCopyVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
-    if (FAILED(result))
-    {
-        ERR("Failed to map vertex buffer for texture copy, HRESULT: 0x%X.", result);
-        return gl::error(GL_OUT_OF_MEMORY, false);
-    }
-
-    // Create a quad in homogeneous coordinates
-    float x1 = (destArea.x / float(destWidth)) * 2.0f - 1.0f;
-    float y1 = ((destHeight - destArea.y - destArea.height) / float(destHeight)) * 2.0f - 1.0f;
-    float x2 = ((destArea.x + destArea.width) / float(destWidth)) * 2.0f - 1.0f;
-    float y2 = ((destHeight - destArea.y) / float(destHeight)) * 2.0f - 1.0f;
-
-    float u1 = sourceArea.x / float(sourceWidth);
-    float v1 = sourceArea.y / float(sourceHeight);
-    float u2 = (sourceArea.x + sourceArea.width) / float(sourceWidth);
-    float v2 = (sourceArea.y + sourceArea.height) / float(sourceHeight);
-
-    UINT stride = 0;
-    UINT startIdx = 0;
-    UINT drawCount = 0;
-    D3D11_PRIMITIVE_TOPOLOGY topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
-
-    if (use3DCopy)
-    {
-        d3d11::PositionLayerTexCoord3DVertex *vertices = static_cast<d3d11::PositionLayerTexCoord3DVertex*>(mappedResource.pData);
-
-        for (unsigned int i = 0; i < destDepth; i++)
-        {
-            float readDepth = ((i * 2) + 0.5f) / (sourceDepth - 1);
-
-            d3d11::SetPositionLayerTexCoord3DVertex(&vertices[i * 6 + 0], x1, y1, i, u1, v2, readDepth);
-            d3d11::SetPositionLayerTexCoord3DVertex(&vertices[i * 6 + 1], x1, y2, i, u1, v1, readDepth);
-            d3d11::SetPositionLayerTexCoord3DVertex(&vertices[i * 6 + 2], x2, y1, i, u2, v2, readDepth);
-
-            d3d11::SetPositionLayerTexCoord3DVertex(&vertices[i * 6 + 3], x1, y2, i, u1, v1, readDepth);
-            d3d11::SetPositionLayerTexCoord3DVertex(&vertices[i * 6 + 4], x2, y2, i, u2, v1, readDepth);
-            d3d11::SetPositionLayerTexCoord3DVertex(&vertices[i * 6 + 5], x2, y1, i, u2, v2, readDepth);
-        }
-
-        stride = sizeof(d3d11::PositionLayerTexCoord3DVertex);
-        drawCount = destDepth * 6;
-        topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
-    }
-    else
-    {
-        d3d11::PositionTexCoordVertex *vertices = static_cast<d3d11::PositionTexCoordVertex*>(mappedResource.pData);
-
-        d3d11::SetPositionTexCoordVertex(&vertices[0], x1, y1, u1, v2);
-        d3d11::SetPositionTexCoordVertex(&vertices[1], x1, y2, u1, v1);
-        d3d11::SetPositionTexCoordVertex(&vertices[2], x2, y1, u2, v2);
-        d3d11::SetPositionTexCoordVertex(&vertices[3], x2, y2, u2, v1);
-
-        stride = sizeof(d3d11::PositionTexCoordVertex);
-        drawCount = 4;
-        topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
-    }
-
-    mDeviceContext->Unmap(mCopyVB, 0);
-
-    mDeviceContext->IASetVertexBuffers(0, 1, &mCopyVB, &stride, &startIdx);
-
-    // Apply state
-    mDeviceContext->OMSetBlendState(NULL, NULL, 0xFFFFFFF);
-    mDeviceContext->OMSetDepthStencilState(NULL, 0xFFFFFFFF);
-    mDeviceContext->RSSetState(NULL);
-
-    // Apply shaders
-    ID3D11InputLayout *il = NULL;
-    ID3D11VertexShader *vs = NULL;
-    ID3D11GeometryShader *gs = NULL;
-    ID3D11PixelShader *ps = NULL;
-
-    if (use3DCopy)
-    {
-        il = mCopy3DIL;
-        vs = mCopy3DVS;
-        gs = mCopy3DGS;
-
-        switch(destFormat)
-        {
-          case GL_RGBA:            ps = mCopyRGBA3DPS;     break;
-          case GL_RGB:             ps = mCopyRGB3DPS;      break;
-          case GL_ALPHA:           ps = mCopyRGBA3DPS;     break;
-          case GL_BGRA_EXT:        ps = mCopyRGBA3DPS;     break;
-          case GL_LUMINANCE:       ps = mCopyLum3DPS;      break;
-          case GL_LUMINANCE_ALPHA: ps = mCopyLumAlpha3DPS; break;
-          default: UNREACHABLE();  ps = NULL;              break;
-        }
-    }
-    else
-    {
-        il = mCopy2DIL;
-        vs = mCopy2DVS;
-        gs = NULL;
-
-        switch(destFormat)
-        {
-          case GL_RGBA:            ps = mCopyRGBA2DPS;     break;
-          case GL_RGB:             ps = mCopyRGB2DPS;      break;
-          case GL_ALPHA:           ps = mCopyRGBA2DPS;     break;
-          case GL_BGRA_EXT:        ps = mCopyRGBA2DPS;     break;
-          case GL_LUMINANCE:       ps = mCopyLum2DPS;      break;
-          case GL_LUMINANCE_ALPHA: ps = mCopyLumAlpha2DPS; break;
-          default: UNREACHABLE();  ps = NULL;              break;
-        }
-    }
-
-    mDeviceContext->IASetInputLayout(il);
-    mDeviceContext->IASetPrimitiveTopology(topology);
-    mDeviceContext->VSSetShader(vs, NULL, 0);
-
-    mDeviceContext->PSSetShader(ps, NULL, 0);
-    mDeviceContext->GSSetShader(gs, NULL, 0);
-
-    // Unset the currently bound shader resource to avoid conflicts
-    static ID3D11ShaderResourceView *const nullSRV = NULL;
-    mDeviceContext->PSSetShaderResources(0, 1, &nullSRV);
-
-    // Apply render target
-    setOneTimeRenderTarget(dest);
-
-    // Set the viewport
-    D3D11_VIEWPORT viewport;
-    viewport.TopLeftX = 0;
-    viewport.TopLeftY = 0;
-    viewport.Width = destWidth;
-    viewport.Height = destHeight;
-    viewport.MinDepth = 0.0f;
-    viewport.MaxDepth = 1.0f;
-    mDeviceContext->RSSetViewports(1, &viewport);
-
-    // Apply textures
-    mDeviceContext->PSSetShaderResources(0, 1, &source);
-    mDeviceContext->PSSetSamplers(0, 1, &mCopySampler);
-
-    // Draw the quad
-    mDeviceContext->Draw(drawCount, 0);
-
-    // Unbind textures and render targets and vertex buffer
-    mDeviceContext->PSSetShaderResources(0, 1, &nullSRV);
-
-    unapplyRenderTargets();
-
-    UINT zero = 0;
-    ID3D11Buffer *const nullBuffer = NULL;
-    mDeviceContext->IASetVertexBuffers(0, 1, &nullBuffer, &zero, &zero);
-
-    markAllStateDirty();
-
-    return true;
-}
-
 void Renderer11::unapplyRenderTargets()
 {
     setOneTimeRenderTarget(NULL);
diff --git a/src/libGLESv2/renderer/Renderer11.h b/src/libGLESv2/renderer/Renderer11.h
index e83258b..8bc5ad2 100644
--- a/src/libGLESv2/renderer/Renderer11.h
+++ b/src/libGLESv2/renderer/Renderer11.h
@@ -29,6 +29,7 @@
 class VertexDataManager;
 class IndexDataManager;
 class StreamingIndexBufferInterface;
+class Blit11;
 
 enum
 {
@@ -163,10 +164,6 @@
     virtual bool copyImage(gl::Framebuffer *framebuffer, const gl::Rectangle &sourceRect, GLenum destFormat,
                            GLint xoffset, GLint yoffset, GLint zOffset, TextureStorageInterface2DArray *storage, GLint level);
 
-    bool copyTexture(ID3D11ShaderResourceView *source, const gl::Box &sourceArea, unsigned int sourceWidth, unsigned int sourceHeight, unsigned int sourceDepth,
-                     ID3D11RenderTargetView *dest, const gl::Box &destArea, unsigned int destWidth, unsigned int destHeight, unsigned int destDepth,
-                     GLenum destFormat);
-
     virtual bool blitRect(gl::Framebuffer *readTarget, const gl::Rectangle &readRect, gl::Framebuffer *drawTarget, const gl::Rectangle &drawRect,
                           bool blitRenderTarget, bool blitDepthStencil);
     virtual void readPixels(gl::Framebuffer *framebuffer, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
@@ -203,6 +200,8 @@
     ID3D11DeviceContext *getDeviceContext() { return mDeviceContext; };
     IDXGIFactory *getDxgiFactory() { return mDxgiFactory; };
 
+    Blit11 *getBlitter() { return mBlit; }
+
     bool getRenderTargetResource(gl::Renderbuffer *colorbuffer, unsigned int *subresourceIndex, ID3D11Texture2D **resource);
     void unapplyRenderTargets();
     void setOneTimeRenderTarget(ID3D11RenderTargetView *renderTargetView);
@@ -339,24 +338,7 @@
     StreamingIndexBufferInterface *mTriangleFanIB;
 
     // Texture copy resources
-    bool mCopyResourcesInitialized;
-    ID3D11Buffer *mCopyVB;
-    ID3D11SamplerState *mCopySampler;
-
-    ID3D11InputLayout *mCopy2DIL;
-    ID3D11VertexShader *mCopy2DVS;
-    ID3D11PixelShader *mCopyRGBA2DPS;
-    ID3D11PixelShader *mCopyRGB2DPS;
-    ID3D11PixelShader *mCopyLum2DPS;
-    ID3D11PixelShader *mCopyLumAlpha2DPS;
-
-    ID3D11InputLayout *mCopy3DIL;
-    ID3D11VertexShader *mCopy3DVS;
-    ID3D11GeometryShader *mCopy3DGS;
-    ID3D11PixelShader *mCopyRGBA3DPS;
-    ID3D11PixelShader *mCopyRGB3DPS;
-    ID3D11PixelShader *mCopyLum3DPS;
-    ID3D11PixelShader *mCopyLumAlpha3DPS;
+    Blit11 *mBlit;
 
     // Masked clear resources
     bool mClearResourcesInitialized;
diff --git a/src/libGLESv2/renderer/TextureStorage11.cpp b/src/libGLESv2/renderer/TextureStorage11.cpp
index cbb69fe..45e980d 100644
--- a/src/libGLESv2/renderer/TextureStorage11.cpp
+++ b/src/libGLESv2/renderer/TextureStorage11.cpp
@@ -14,6 +14,7 @@
 #include "libGLESv2/renderer/RenderTarget11.h"
 #include "libGLESv2/renderer/SwapChain11.h"
 #include "libGLESv2/renderer/renderer11_utils.h"
+#include "libGLESv2/renderer/Blit11.h"
 #include "libGLESv2/renderer/formatutils11.h"
 
 #include "common/utilities.h"
@@ -142,25 +143,16 @@
 
         if (sourceSRV && destRTV)
         {
-            gl::Box sourceArea;
-            sourceArea.x = 0;
-            sourceArea.y = 0;
-            sourceArea.z = 0;
-            sourceArea.width = source->getWidth();
-            sourceArea.height = source->getHeight();
-            sourceArea.depth = source->getDepth();
+            gl::Box sourceArea(0, 0, 0, source->getWidth(), source->getHeight(), source->getDepth());
+            gl::Extents sourceSize(source->getWidth(), source->getHeight(), source->getDepth());
 
-            gl::Box destArea;
-            destArea.x = 0;
-            destArea.y = 0;
-            destArea.z = 0;
-            destArea.width = dest->getWidth();
-            destArea.height = dest->getHeight();
-            destArea.depth = dest->getDepth();
+            gl::Box destArea(0, 0, 0, dest->getWidth(), dest->getHeight(), dest->getDepth());
+            gl::Extents destSize(dest->getWidth(), dest->getHeight(), dest->getDepth());
 
-            mRenderer->copyTexture(sourceSRV, sourceArea, source->getWidth(), source->getHeight(), source->getDepth(),
-                                   destRTV, destArea, dest->getWidth(), dest->getHeight(), dest->getDepth(),
-                                   GL_RGBA);
+            Blit11 *blitter = mRenderer->getBlitter();
+
+            blitter->copyTexture(sourceSRV, sourceArea, sourceSize, destRTV, destArea, destSize,
+                                 GL_RGBA, GL_LINEAR);
         }
 
         if (sourceSRV)