gl::Error PixelTransfer11::copyBufferToTexture(const gl::PixelUnpackState &unpack, unsigned int offset, RenderTargetD3D *destRenderTarget,
                                               GLenum destinationFormat, GLenum sourcePixelsType, const gl::Box &destArea)
{
    ANGLE_TRY(loadResources());

    gl::Extents destSize = destRenderTarget->getExtents();

    ASSERT(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  );

    const gl::Buffer &sourceBuffer = *unpack.pixelBuffer.get();

    ASSERT(mRenderer->supportsFastCopyBufferToTexture(destinationFormat));

    ID3D11PixelShader *pixelShader = findBufferToTexturePS(destinationFormat);
    ASSERT(pixelShader);

    // The SRV must be in the proper read format, which may be different from the destination format
    // EG: for half float data, we can load full precision floats with implicit conversion
    GLenum unsizedFormat = gl::GetInternalFormatInfo(destinationFormat).format;
    GLenum sourceFormat = gl::GetSizedInternalFormat(unsizedFormat, sourcePixelsType);

    const d3d11::Format &sourceFormatInfo =
        d3d11::Format::Get(sourceFormat, mRenderer->getRenderer11DeviceCaps());
    DXGI_FORMAT srvFormat = sourceFormatInfo.srvFormat;
    ASSERT(srvFormat != DXGI_FORMAT_UNKNOWN);
    Buffer11 *bufferStorage11 = GetAs<Buffer11>(sourceBuffer.getImplementation());
    ID3D11ShaderResourceView *bufferSRV = nullptr;
    ANGLE_TRY_RESULT(bufferStorage11->getSRV(srvFormat), bufferSRV);
    ASSERT(bufferSRV != nullptr);

    ID3D11RenderTargetView *textureRTV = GetAs<RenderTarget11>(destRenderTarget)->getRenderTargetView();
    ASSERT(textureRTV != nullptr);

    CopyShaderParams shaderParams;
    setBufferToTextureCopyParams(destArea, destSize, sourceFormat, unpack, offset, &shaderParams);

    ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();

    ID3D11Buffer *nullBuffer = nullptr;
    UINT zero = 0;

    // Are we doing a 2D or 3D copy?
    ID3D11GeometryShader *geometryShader = ((destSize.depth > 1) ? mBufferToTextureGS : NULL);
    auto stateManager                    = mRenderer->getStateManager();

    deviceContext->VSSetShader(mBufferToTextureVS, NULL, 0);
    deviceContext->GSSetShader(geometryShader, NULL, 0);
    deviceContext->PSSetShader(pixelShader, NULL, 0);
    stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, bufferSRV);
    deviceContext->IASetInputLayout(NULL);
    deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);

    deviceContext->IASetVertexBuffers(0, 1, &nullBuffer, &zero, &zero);
    deviceContext->OMSetBlendState(NULL, NULL, 0xFFFFFFF);
    deviceContext->OMSetDepthStencilState(mCopyDepthStencilState, 0xFFFFFFFF);
    deviceContext->RSSetState(mCopyRasterizerState);

    stateManager->setOneTimeRenderTarget(textureRTV, nullptr);

    if (!StructEquals(mParamsData, shaderParams))
    {
        d3d11::SetBufferData(deviceContext, mParamsConstantBuffer, shaderParams);
        mParamsData = shaderParams;
    }

    deviceContext->VSSetConstantBuffers(0, 1, &mParamsConstantBuffer);

    // Set the viewport
    D3D11_VIEWPORT viewport;
    viewport.TopLeftX = 0;
    viewport.TopLeftY = 0;
    viewport.Width = static_cast<FLOAT>(destSize.width);
    viewport.Height = static_cast<FLOAT>(destSize.height);
    viewport.MinDepth = 0.0f;
    viewport.MaxDepth = 1.0f;
    deviceContext->RSSetViewports(1, &viewport);

    UINT numPixels = (destArea.width * destArea.height * destArea.depth);
    deviceContext->Draw(numPixels, 0);

    // Unbind textures and render targets and vertex buffer
    stateManager->setShaderResource(gl::SAMPLER_PIXEL, 0, NULL);
    deviceContext->VSSetConstantBuffers(0, 1, &nullBuffer);

    mRenderer->markAllStateDirty();

    return gl::NoError();
}
angle::Result PixelTransfer11::copyBufferToTexture(const gl::Context *context,
                                                   const gl::PixelUnpackState &unpack,
                                                   unsigned int offset,
                                                   RenderTargetD3D *destRenderTarget,
                                                   GLenum destinationFormat,
                                                   GLenum sourcePixelsType,
                                                   const gl::Box &destArea)
{
    ANGLE_TRY(loadResources(context));

    gl::Extents destSize = destRenderTarget->getExtents();

    ASSERT(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  );

    const gl::Buffer &sourceBuffer =
        *context->getGLState().getTargetBuffer(gl::BufferBinding::PixelUnpack);

    ASSERT(mRenderer->supportsFastCopyBufferToTexture(destinationFormat));

    const d3d11::PixelShader *pixelShader = findBufferToTexturePS(destinationFormat);
    ASSERT(pixelShader);

    // The SRV must be in the proper read format, which may be different from the destination format
    // EG: for half float data, we can load full precision floats with implicit conversion
    GLenum unsizedFormat = gl::GetUnsizedFormat(destinationFormat);
    const gl::InternalFormat &sourceglFormatInfo =
        gl::GetInternalFormatInfo(unsizedFormat, sourcePixelsType);

    const d3d11::Format &sourceFormatInfo = d3d11::Format::Get(
        sourceglFormatInfo.sizedInternalFormat, mRenderer->getRenderer11DeviceCaps());
    DXGI_FORMAT srvFormat = sourceFormatInfo.srvFormat;
    ASSERT(srvFormat != DXGI_FORMAT_UNKNOWN);
    Buffer11 *bufferStorage11 = GetAs<Buffer11>(sourceBuffer.getImplementation());
    const d3d11::ShaderResourceView *bufferSRV = nullptr;
    ANGLE_TRY(bufferStorage11->getSRV(context, srvFormat, &bufferSRV));
    ASSERT(bufferSRV != nullptr);

    const d3d11::RenderTargetView &textureRTV =
        GetAs<RenderTarget11>(destRenderTarget)->getRenderTargetView();
    ASSERT(textureRTV.valid());

    CopyShaderParams shaderParams;
    setBufferToTextureCopyParams(destArea, destSize, sourceglFormatInfo.sizedInternalFormat, unpack,
                                 offset, &shaderParams);

    ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();

    // Are we doing a 2D or 3D copy?
    const auto *geometryShader = ((destSize.depth > 1) ? &mBufferToTextureGS : nullptr);
    StateManager11 *stateManager = mRenderer->getStateManager();

    stateManager->setDrawShaders(&mBufferToTextureVS, geometryShader, pixelShader);
    stateManager->setShaderResource(gl::ShaderType::Fragment, 0, bufferSRV);
    stateManager->setInputLayout(nullptr);
    stateManager->setPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);

    stateManager->setSingleVertexBuffer(nullptr, 0, 0);
    stateManager->setSimpleBlendState(nullptr);
    stateManager->setDepthStencilState(&mCopyDepthStencilState, 0xFFFFFFFF);
    stateManager->setRasterizerState(&mCopyRasterizerState);

    stateManager->setRenderTarget(textureRTV.get(), nullptr);

    if (!StructEquals(mParamsData, shaderParams))
    {
        d3d11::SetBufferData(deviceContext, mParamsConstantBuffer.get(), shaderParams);
        mParamsData = shaderParams;
    }

    stateManager->setVertexConstantBuffer(0, &mParamsConstantBuffer);

    // Set the viewport
    stateManager->setSimpleViewport(destSize);

    UINT numPixels = (destArea.width * destArea.height * destArea.depth);
    deviceContext->Draw(numPixels, 0);

    return angle::Result::Continue();
}