Support internal pipelines for Dawn

Like copyTextureCHROMIUM in Chromium, CopyImageBitmapToTexture can use
internal pipeline to do the GPU uploading. But Dawn doesn't support
internal pipeline now.

This patch adds the first internal pipeline for Dawn and add the API
CopyTextureForBrowser to use internal pipeline to do gpu uploading.

The patch integrates very simple wgsl vertex/fragment shaders to do
simple direct blit to verify the whole system works.

BUG=dawn:465

Change-Id: I8b566af38a10eea00f7426c39e752958ef057abd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/30960
Commit-Queue: Shaobo Yan <shaobo.yan@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/BUILD.gn b/src/dawn_native/BUILD.gn
index a20ac2e..6c6bec3 100644
--- a/src/dawn_native/BUILD.gn
+++ b/src/dawn_native/BUILD.gn
@@ -183,6 +183,8 @@
     "ComputePassEncoder.h",
     "ComputePipeline.cpp",
     "ComputePipeline.h",
+    "CopyTextureForBrowserHelper.cpp",
+    "CopyTextureForBrowserHelper.h",
     "CreateReadyPipelineTracker.cpp",
     "CreateReadyPipelineTracker.h",
     "Device.cpp",
@@ -213,6 +215,7 @@
     "Instance.cpp",
     "Instance.h",
     "IntegerTypes.h",
+    "InternalPipelineStore.h",
     "ObjectBase.cpp",
     "ObjectBase.h",
     "PassResourceUsage.h",
diff --git a/src/dawn_native/CMakeLists.txt b/src/dawn_native/CMakeLists.txt
index 9f01dc1..e290aaa0 100644
--- a/src/dawn_native/CMakeLists.txt
+++ b/src/dawn_native/CMakeLists.txt
@@ -70,6 +70,8 @@
     "ComputePassEncoder.h"
     "ComputePipeline.cpp"
     "ComputePipeline.h"
+    "CopyTextureForBrowserHelper.cpp"
+    "CopyTextureForBrowserHelper.h"
     "CreateReadyPipelineTracker.cpp"
     "CreateReadyPipelineTracker.h"
     "Device.cpp"
@@ -99,6 +101,7 @@
     "Forward.h"
     "Instance.cpp"
     "Instance.h"
+    "InternalPipelineStore.h"
     "IntegerTypes.h"
     "ObjectBase.cpp"
     "ObjectBase.h"
diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp
index 5468387..7d4cb18 100644
--- a/src/dawn_native/CommandEncoder.cpp
+++ b/src/dawn_native/CommandEncoder.cpp
@@ -66,41 +66,6 @@
             return {};
         }
 
-        MaybeError ValidateTextureToTextureCopyRestrictions(const TextureCopyView& src,
-                                                            const TextureCopyView& dst,
-                                                            const Extent3D& copySize) {
-            const uint32_t srcSamples = src.texture->GetSampleCount();
-            const uint32_t dstSamples = dst.texture->GetSampleCount();
-
-            if (srcSamples != dstSamples) {
-                return DAWN_VALIDATION_ERROR(
-                    "Source and destination textures must have matching sample counts.");
-            }
-
-            if (src.texture->GetFormat().format != dst.texture->GetFormat().format) {
-                // Metal requires texture-to-texture copies be the same format
-                return DAWN_VALIDATION_ERROR("Source and destination texture formats must match.");
-            }
-
-            if (src.aspect != wgpu::TextureAspect::All || dst.aspect != wgpu::TextureAspect::All) {
-                // Metal cannot select a single aspect for texture-to-texture copies
-                return DAWN_VALIDATION_ERROR(
-                    "Texture aspect must be \"all\" for texture to texture copies");
-            }
-
-            if (src.texture == dst.texture && src.mipLevel == dst.mipLevel) {
-                ASSERT(src.texture->GetDimension() == wgpu::TextureDimension::e2D &&
-                       dst.texture->GetDimension() == wgpu::TextureDimension::e2D);
-                if (IsRangeOverlapped(src.origin.z, dst.origin.z, copySize.depth)) {
-                    return DAWN_VALIDATION_ERROR(
-                        "Copy subresources cannot be overlapped when copying within the same "
-                        "texture.");
-                }
-            }
-
-            return {};
-        }
-
         MaybeError ValidateLinearTextureCopyOffset(const TextureDataLayout& layout,
                                                    const TexelBlockInfo& blockInfo) {
             if (layout.offset % blockInfo.byteSize != 0) {
@@ -132,24 +97,6 @@
             return {};
         }
 
-        MaybeError ValidateCanUseAs(const BufferBase* buffer, wgpu::BufferUsage usage) {
-            ASSERT(wgpu::HasZeroOrOneBits(usage));
-            if (!(buffer->GetUsage() & usage)) {
-                return DAWN_VALIDATION_ERROR("buffer doesn't have the required usage.");
-            }
-
-            return {};
-        }
-
-        MaybeError ValidateCanUseAs(const TextureBase* texture, wgpu::TextureUsage usage) {
-            ASSERT(wgpu::HasZeroOrOneBits(usage));
-            if (!(texture->GetUsage() & usage)) {
-                return DAWN_VALIDATION_ERROR("texture doesn't have the required usage.");
-            }
-
-            return {};
-        }
-
         MaybeError ValidateAttachmentArrayLayersAndLevelCount(const TextureViewBase* attachment) {
             // Currently we do not support layered rendering.
             if (attachment->GetLayerCount() > 1) {
diff --git a/src/dawn_native/CommandEncoder.h b/src/dawn_native/CommandEncoder.h
index 8b5ae1f..50291a1 100644
--- a/src/dawn_native/CommandEncoder.h
+++ b/src/dawn_native/CommandEncoder.h
@@ -72,7 +72,7 @@
                              uint64_t destinationOffset);
         void WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex);
 
-        CommandBufferBase* Finish(const CommandBufferDescriptor* descriptor);
+        CommandBufferBase* Finish(const CommandBufferDescriptor* descriptor = nullptr);
 
       private:
         MaybeError ValidateFinish(CommandIterator* commands,
diff --git a/src/dawn_native/CommandValidation.cpp b/src/dawn_native/CommandValidation.cpp
index 4a19243..8ca86f8 100644
--- a/src/dawn_native/CommandValidation.cpp
+++ b/src/dawn_native/CommandValidation.cpp
@@ -616,4 +616,57 @@
         return {};
     }
 
+    MaybeError ValidateTextureToTextureCopyRestrictions(const TextureCopyView& src,
+                                                        const TextureCopyView& dst,
+                                                        const Extent3D& copySize) {
+        const uint32_t srcSamples = src.texture->GetSampleCount();
+        const uint32_t dstSamples = dst.texture->GetSampleCount();
+
+        if (srcSamples != dstSamples) {
+            return DAWN_VALIDATION_ERROR(
+                "Source and destination textures must have matching sample counts.");
+        }
+
+        if (src.texture->GetFormat().format != dst.texture->GetFormat().format) {
+            // Metal requires texture-to-texture copies be the same format
+            return DAWN_VALIDATION_ERROR("Source and destination texture formats must match.");
+        }
+
+        if (src.aspect != wgpu::TextureAspect::All || dst.aspect != wgpu::TextureAspect::All) {
+            // Metal cannot select a single aspect for texture-to-texture copies
+            return DAWN_VALIDATION_ERROR(
+                "Texture aspect must be \"all\" for texture to texture copies");
+        }
+
+        if (src.texture == dst.texture && src.mipLevel == dst.mipLevel) {
+            ASSERT(src.texture->GetDimension() == wgpu::TextureDimension::e2D &&
+                   dst.texture->GetDimension() == wgpu::TextureDimension::e2D);
+            if (IsRangeOverlapped(src.origin.z, dst.origin.z, copySize.depth)) {
+                return DAWN_VALIDATION_ERROR(
+                    "Copy subresources cannot be overlapped when copying within the same "
+                    "texture.");
+            }
+        }
+
+        return {};
+    }
+
+    MaybeError ValidateCanUseAs(const TextureBase* texture, wgpu::TextureUsage usage) {
+        ASSERT(wgpu::HasZeroOrOneBits(usage));
+        if (!(texture->GetUsage() & usage)) {
+            return DAWN_VALIDATION_ERROR("texture doesn't have the required usage.");
+        }
+
+        return {};
+    }
+
+    MaybeError ValidateCanUseAs(const BufferBase* buffer, wgpu::BufferUsage usage) {
+        ASSERT(wgpu::HasZeroOrOneBits(usage));
+        if (!(buffer->GetUsage() & usage)) {
+            return DAWN_VALIDATION_ERROR("buffer doesn't have the required usage.");
+        }
+
+        return {};
+    }
+
 }  // namespace dawn_native
diff --git a/src/dawn_native/CommandValidation.h b/src/dawn_native/CommandValidation.h
index faf6f12..0eb04f5 100644
--- a/src/dawn_native/CommandValidation.h
+++ b/src/dawn_native/CommandValidation.h
@@ -78,6 +78,14 @@
 
     bool IsRangeOverlapped(uint32_t startA, uint32_t startB, uint32_t length);
 
+    MaybeError ValidateTextureToTextureCopyRestrictions(const TextureCopyView& src,
+                                                        const TextureCopyView& dst,
+                                                        const Extent3D& copySize);
+
+    MaybeError ValidateCanUseAs(const TextureBase* texture, wgpu::TextureUsage usage);
+
+    MaybeError ValidateCanUseAs(const BufferBase* buffer, wgpu::BufferUsage usage);
+
 }  // namespace dawn_native
 
 #endif  // DAWNNATIVE_COMMANDVALIDATION_H_
diff --git a/src/dawn_native/CopyTextureForBrowserHelper.cpp b/src/dawn_native/CopyTextureForBrowserHelper.cpp
new file mode 100644
index 0000000..facac3a
--- /dev/null
+++ b/src/dawn_native/CopyTextureForBrowserHelper.cpp
@@ -0,0 +1,307 @@
+// Copyright 2020 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn_native/CopyTextureForBrowserHelper.h"
+
+#include "dawn_native/BindGroup.h"
+#include "dawn_native/BindGroupLayout.h"
+#include "dawn_native/Buffer.h"
+#include "dawn_native/CommandBuffer.h"
+#include "dawn_native/CommandEncoder.h"
+#include "dawn_native/CommandValidation.h"
+#include "dawn_native/Device.h"
+#include "dawn_native/InternalPipelineStore.h"
+#include "dawn_native/Queue.h"
+#include "dawn_native/RenderPassEncoder.h"
+#include "dawn_native/RenderPipeline.h"
+#include "dawn_native/Sampler.h"
+#include "dawn_native/Texture.h"
+
+#include <unordered_set>
+
+namespace dawn_native {
+    namespace {
+
+        // TODO(shaobo.yan@intel.com): Use ANGLE's strategy(render a large triangle) to handle
+        // transform and border interop issue.
+        static const char sCopyTextureForBrowserVertex[] = R"(
+            [[block]] struct Uniforms {
+                [[offset(0)]] rotation : mat4x4<f32>;
+            };
+            const pos : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+                vec2<f32>(1.0, 1.0),
+                vec2<f32>(1.0, -1.0),
+                vec2<f32>(-1.0, -1.0),
+                vec2<f32>(1.0, 1.0),
+                vec2<f32>(-1.0, -1.0),
+                vec2<f32>(-1.0, 1.0));
+
+            const texUV : array<vec2<f32>, 6> = array<vec2<f32>, 6>(
+                vec2<f32>(1.0, 0.0),
+                vec2<f32>(1.0, 1.0),
+                vec2<f32>(0.0, 1.0),
+                vec2<f32>(1.0, 0.0),
+                vec2<f32>(0.0, 1.0),
+                vec2<f32>(0.0, 0.0));
+
+            [[location(0)]] var<out> texCoord: vec2<f32>;
+            [[builtin(position)]] var<out> Position : vec4<f32>;
+            [[builtin(vertex_idx)]] var<in> VertexIndex : i32;
+            [[binding(0), set(0)]] var<uniform> uniforms : Uniforms;
+            [[stage(vertex)]]
+            fn main() -> void {
+                Position = uniforms.rotation * vec4<f32>(pos[VertexIndex], 0.0, 1.0);
+                texCoord = texUV[VertexIndex];
+                return;
+            }
+        )";
+
+        static const char sPassthrough2D4ChannelFrag[] = R"(
+            [[binding(1), set(0)]] var<uniform_constant> mySampler: sampler;
+            [[binding(2), set(0)]] var<uniform_constant> myTexture: texture_sampled_2d<f32>;
+            [[location(0)]] var<in> texCoord : vec2<f32>;
+            [[location(0)]] var<out> rgbaColor : vec4<f32>;
+            [[stage(fragment)]]
+            fn main() -> void {
+                rgbaColor = textureSample(myTexture, mySampler, texCoord);
+                return;
+            }
+        )";
+
+        // TODO(shaobo.yan@intel.com): Expand supported texture formats
+        MaybeError ValidateCopyTextureFormatConversion(const wgpu::TextureFormat srcFormat,
+                                                       const wgpu::TextureFormat dstFormat) {
+            switch (srcFormat) {
+                case wgpu::TextureFormat::RGBA8Unorm:
+                    break;
+                default:
+                    return DAWN_VALIDATION_ERROR(
+                        "Unsupported src texture format for CopyTextureForBrowser.");
+            }
+
+            switch (dstFormat) {
+                case wgpu::TextureFormat::RGBA8Unorm:
+                    break;
+                default:
+                    return DAWN_VALIDATION_ERROR(
+                        "Unsupported dst texture format for CopyTextureForBrowser.");
+            }
+
+            return {};
+        }
+
+        RenderPipelineBase* GetOrCreateCopyTextureForBrowserPipeline(DeviceBase* device) {
+            InternalPipelineStore* store = device->GetInternalPipelineStore();
+
+            if (store->copyTextureForBrowserPipeline == nullptr) {
+                // Create vertex shader module if not cached before.
+                if (store->copyTextureForBrowserVS == nullptr) {
+                    ShaderModuleDescriptor descriptor;
+                    ShaderModuleWGSLDescriptor wgslDesc;
+                    wgslDesc.source = sCopyTextureForBrowserVertex;
+                    descriptor.nextInChain = reinterpret_cast<ChainedStruct*>(&wgslDesc);
+
+                    store->copyTextureForBrowserVS =
+                        AcquireRef(device->CreateShaderModule(&descriptor));
+                }
+
+                ShaderModuleBase* vertexModule = store->copyTextureForBrowserVS.Get();
+
+                // Create fragment shader module if not cached before.
+                if (store->copyTextureForBrowserFS == nullptr) {
+                    ShaderModuleDescriptor descriptor;
+                    ShaderModuleWGSLDescriptor wgslDesc;
+                    wgslDesc.source = sPassthrough2D4ChannelFrag;
+                    descriptor.nextInChain = reinterpret_cast<ChainedStruct*>(&wgslDesc);
+                    store->copyTextureForBrowserFS =
+                        AcquireRef(device->CreateShaderModule(&descriptor));
+                }
+
+                ShaderModuleBase* fragmentModule = store->copyTextureForBrowserFS.Get();
+
+                // Prepare vertex stage.
+                ProgrammableStageDescriptor vertexStage = {};
+                vertexStage.module = vertexModule;
+                vertexStage.entryPoint = "main";
+
+                // Prepare frgament stage.
+                ProgrammableStageDescriptor fragmentStage = {};
+                fragmentStage.module = fragmentModule;
+                fragmentStage.entryPoint = "main";
+
+                // Prepare color state.
+                ColorStateDescriptor colorState = {};
+                colorState.format = wgpu::TextureFormat::RGBA8Unorm;
+
+                // Create RenderPipeline.
+                RenderPipelineDescriptor renderPipelineDesc = {};
+
+                // Generate the layout based on shader modules.
+                renderPipelineDesc.layout = nullptr;
+
+                renderPipelineDesc.vertexStage = vertexStage;
+                renderPipelineDesc.fragmentStage = &fragmentStage;
+
+                renderPipelineDesc.primitiveTopology = wgpu::PrimitiveTopology::TriangleList;
+
+                renderPipelineDesc.colorStateCount = 1;
+                renderPipelineDesc.colorStates = &colorState;
+
+                store->copyTextureForBrowserPipeline =
+                    AcquireRef(device->CreateRenderPipeline(&renderPipelineDesc));
+            }
+
+            return store->copyTextureForBrowserPipeline.Get();
+        }
+
+    }  // anonymous namespace
+
+    MaybeError ValidateCopyTextureForBrowser(DeviceBase* device,
+                                             const TextureCopyView* source,
+                                             const TextureCopyView* destination,
+                                             const Extent3D* copySize) {
+        DAWN_TRY(device->ValidateObject(source->texture));
+        DAWN_TRY(device->ValidateObject(destination->texture));
+
+        DAWN_TRY(ValidateTextureCopyView(device, *source, *copySize));
+        DAWN_TRY(ValidateTextureCopyView(device, *destination, *copySize));
+
+        DAWN_TRY(ValidateTextureToTextureCopyRestrictions(*source, *destination, *copySize));
+
+        DAWN_TRY(ValidateTextureCopyRange(*source, *copySize));
+        DAWN_TRY(ValidateTextureCopyRange(*destination, *copySize));
+
+        DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc));
+        DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst));
+
+        DAWN_TRY(ValidateCopyTextureFormatConversion(source->texture->GetFormat().format,
+                                                     destination->texture->GetFormat().format));
+
+        // TODO(shaobo.yan@intel.com): Support the simplest case for now that source and destination
+        // texture has the same size and do full texture blit. Will address sub texture blit in
+        // future and remove these validations.
+        if (source->origin.x != 0 || source->origin.y != 0 || source->origin.z != 0 ||
+            destination->origin.x != 0 || destination->origin.y != 0 ||
+            destination->origin.z != 0 || source->mipLevel != 0 || destination->mipLevel != 0 ||
+            source->texture->GetWidth() != destination->texture->GetWidth() ||
+            source->texture->GetHeight() != destination->texture->GetHeight()) {
+            return DAWN_VALIDATION_ERROR("Cannot support sub blit now.");
+        }
+
+        return {};
+    }
+
+    MaybeError DoCopyTextureForBrowser(DeviceBase* device,
+                                       const TextureCopyView* source,
+                                       const TextureCopyView* destination,
+                                       const Extent3D* copySize) {
+        // TODO(shaobo.yan@intel.com): In D3D12 and Vulkan, compatible texture format can directly
+        // copy to each other. This can be a potential fast path.
+        RenderPipelineBase* pipeline = GetOrCreateCopyTextureForBrowserPipeline(device);
+
+        // Prepare bind group layout.
+        Ref<BindGroupLayoutBase> layout = AcquireRef(pipeline->GetBindGroupLayout(0));
+
+        // Prepare bind group descriptor
+        BindGroupEntry bindGroupEntries[3] = {};
+        BindGroupDescriptor bgDesc = {};
+        bgDesc.layout = layout.Get();
+        bgDesc.entryCount = 3;
+        bgDesc.entries = bindGroupEntries;
+
+        // Prepare binding 0 resource: uniform buffer.
+        // TODO(shaobo.yan@intel.com): Will use scale vector and offset vector to replace the
+        // 4x4 rotation matrix here.
+        const float rotationMatrix[] = {
+            1.0, 0.0, 0.0, 0.0,  //
+            0.0, 1.0, 0.0, 0.0,  //
+            0.0, 0.0, 1.0, 0.0,  //
+            0.0, 0.0, 0.0, 1.0,  //
+        };
+
+        BufferDescriptor rotationUniformDesc = {};
+        rotationUniformDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform;
+        rotationUniformDesc.size = sizeof(rotationMatrix);
+        Ref<BufferBase> rotationUniform = AcquireRef(device->CreateBuffer(&rotationUniformDesc));
+
+        device->GetDefaultQueue()->WriteBuffer(rotationUniform.Get(), 0, rotationMatrix,
+                                               sizeof(rotationMatrix));
+
+        // Prepare binding 1 resource: sampler
+        // Use default configuration, filterMode set to Nearest for min and mag.
+        SamplerDescriptor samplerDesc = {};
+        Ref<SamplerBase> sampler = AcquireRef(device->CreateSampler(&samplerDesc));
+
+        // Prepare binding 2 resource: sampled texture
+        TextureViewDescriptor srcTextureViewDesc = {};
+        srcTextureViewDesc.baseMipLevel = source->mipLevel;
+        srcTextureViewDesc.mipLevelCount = 1;
+        Ref<TextureViewBase> srcTextureView =
+            AcquireRef(source->texture->CreateView(&srcTextureViewDesc));
+
+        // Set bind group entries.
+        bindGroupEntries[0].binding = 0;
+        bindGroupEntries[0].buffer = rotationUniform.Get();
+        bindGroupEntries[0].size = sizeof(rotationMatrix);
+        bindGroupEntries[1].binding = 1;
+        bindGroupEntries[1].sampler = sampler.Get();
+        bindGroupEntries[2].binding = 2;
+        bindGroupEntries[2].textureView = srcTextureView.Get();
+
+        // Create bind group after all binding entries are set.
+        Ref<BindGroupBase> bindGroup = AcquireRef(device->CreateBindGroup(&bgDesc));
+
+        // Create command encoder.
+        CommandEncoderDescriptor encoderDesc = {};
+        Ref<CommandEncoder> encoder = AcquireRef(device->CreateCommandEncoder(&encoderDesc));
+
+        // Prepare dst texture view as color Attachment.
+        TextureViewDescriptor dstTextureViewDesc;
+        dstTextureViewDesc.baseMipLevel = destination->mipLevel;
+        dstTextureViewDesc.mipLevelCount = 1;
+        Ref<TextureViewBase> dstView =
+            AcquireRef(destination->texture->CreateView(&dstTextureViewDesc));
+
+        // Prepare render pass color attachment descriptor.
+        RenderPassColorAttachmentDescriptor colorAttachmentDesc;
+        colorAttachmentDesc.attachment = dstView.Get();
+        colorAttachmentDesc.loadOp = wgpu::LoadOp::Load;
+        colorAttachmentDesc.storeOp = wgpu::StoreOp::Store;
+        colorAttachmentDesc.clearColor = {0.0, 0.0, 0.0, 1.0};
+
+        // Create render pass.
+        RenderPassDescriptor renderPassDesc;
+        renderPassDesc.colorAttachmentCount = 1;
+        renderPassDesc.colorAttachments = &colorAttachmentDesc;
+        Ref<RenderPassEncoder> passEncoder = AcquireRef(encoder->BeginRenderPass(&renderPassDesc));
+
+        // Start pipeline  and encode commands to complete
+        // the copy from src texture to dst texture with transformation.
+        passEncoder->SetPipeline(pipeline);
+        passEncoder->SetBindGroup(0, bindGroup.Get());
+        passEncoder->Draw(6);
+        passEncoder->EndPass();
+
+        // Finsh encoding.
+        Ref<CommandBufferBase> commandBuffer = AcquireRef(encoder->Finish());
+        CommandBufferBase* submitCommandBuffer = commandBuffer.Get();
+
+        // Submit command buffer.
+        Ref<QueueBase> queue = AcquireRef(device->GetDefaultQueue());
+        queue->Submit(1, &submitCommandBuffer);
+
+        return {};
+    }
+
+}  // namespace dawn_native
diff --git a/src/dawn_native/CopyTextureForBrowserHelper.h b/src/dawn_native/CopyTextureForBrowserHelper.h
new file mode 100644
index 0000000..1894e8b
--- /dev/null
+++ b/src/dawn_native/CopyTextureForBrowserHelper.h
@@ -0,0 +1,38 @@
+// Copyright 2020 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DAWNNATIVE_COPYTEXTUREFORBROWSERHELPER_H_
+#define DAWNNATIVE_COPYTEXTUREFORBROWSERHELPER_H_
+
+#include "dawn_native/Error.h"
+#include "dawn_native/ObjectBase.h"
+
+namespace dawn_native {
+    class DeviceBase;
+    struct Extent3D;
+    struct TextureCopyView;
+
+    MaybeError ValidateCopyTextureForBrowser(DeviceBase* device,
+                                             const TextureCopyView* source,
+                                             const TextureCopyView* destination,
+                                             const Extent3D* copySize);
+
+    MaybeError DoCopyTextureForBrowser(DeviceBase* device,
+                                       const TextureCopyView* source,
+                                       const TextureCopyView* destination,
+                                       const Extent3D* copySize);
+
+}  // namespace dawn_native
+
+#endif  // DAWNNATIVE_COPYTEXTUREFORBROWSERHELPER_H_
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index e5454f8..c3421c3 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -30,6 +30,7 @@
 #include "dawn_native/ErrorScopeTracker.h"
 #include "dawn_native/Fence.h"
 #include "dawn_native/Instance.h"
+#include "dawn_native/InternalPipelineStore.h"
 #include "dawn_native/PipelineLayout.h"
 #include "dawn_native/QuerySet.h"
 #include "dawn_native/Queue.h"
@@ -105,6 +106,7 @@
         mDynamicUploader = std::make_unique<DynamicUploader>(this);
         mCreateReadyPipelineTracker = std::make_unique<CreateReadyPipelineTracker>(this);
         mDeprecationWarnings = std::make_unique<DeprecationWarnings>();
+        mInternalPipelineStore = std::make_unique<InternalPipelineStore>();
 
         // Starting from now the backend can start doing reentrant calls so the device is marked as
         // alive.
@@ -172,6 +174,8 @@
 
         mEmptyBindGroupLayout = nullptr;
 
+        mInternalPipelineStore = nullptr;
+
         AssumeCommandsComplete();
         // Tell the backend that it can free all the objects now that the GPU timeline is empty.
         ShutDownImpl();
@@ -329,6 +333,10 @@
         return mFutureSerial;
     }
 
+    InternalPipelineStore* DeviceBase::GetInternalPipelineStore() {
+        return mInternalPipelineStore.get();
+    }
+
     void DeviceBase::IncrementLastSubmittedCommandSerial() {
         mLastSubmittedSerial++;
     }
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index 494c431..ce7ef26 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -38,6 +38,7 @@
     class ErrorScope;
     class ErrorScopeTracker;
     class StagingBufferBase;
+    struct InternalPipelineStore;
 
     class DeviceBase {
       public:
@@ -161,6 +162,7 @@
         TextureBase* CreateTexture(const TextureDescriptor* descriptor);
         TextureViewBase* CreateTextureView(TextureBase* texture,
                                            const TextureViewDescriptor* descriptor);
+        InternalPipelineStore* GetInternalPipelineStore();
 
         // For Dawn Wire
         BufferBase* CreateErrorBuffer();
@@ -385,6 +387,8 @@
         size_t mLazyClearCountForTesting = 0;
 
         ExtensionsSet mEnabledExtensions;
+
+        std::unique_ptr<InternalPipelineStore> mInternalPipelineStore;
     };
 
 }  // namespace dawn_native
diff --git a/src/dawn_native/InternalPipelineStore.h b/src/dawn_native/InternalPipelineStore.h
new file mode 100644
index 0000000..83277c1
--- /dev/null
+++ b/src/dawn_native/InternalPipelineStore.h
@@ -0,0 +1,34 @@
+// Copyright 2020 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DAWNNATIVE_INTERNALPIPELINESTORE_H_
+#define DAWNNATIVE_INTERNALPIPELINESTORE_H_
+
+#include "dawn_native/ObjectBase.h"
+#include "dawn_native/dawn_platform.h"
+
+#include <unordered_map>
+
+namespace dawn_native {
+    class RenderPipelineBase;
+    class ShaderModuleBase;
+
+    struct InternalPipelineStore {
+        Ref<RenderPipelineBase> copyTextureForBrowserPipeline;
+        Ref<ShaderModuleBase> copyTextureForBrowserVS;
+        Ref<ShaderModuleBase> copyTextureForBrowserFS;
+    };
+}  // namespace dawn_native
+
+#endif  // DAWNNATIVE_INTERNALPIPELINESTORE_H_
\ No newline at end of file
diff --git a/src/dawn_native/ProgrammablePassEncoder.h b/src/dawn_native/ProgrammablePassEncoder.h
index b7bd452..f2a2918 100644
--- a/src/dawn_native/ProgrammablePassEncoder.h
+++ b/src/dawn_native/ProgrammablePassEncoder.h
@@ -40,8 +40,8 @@
 
         void SetBindGroup(uint32_t groupIndex,
                           BindGroupBase* group,
-                          uint32_t dynamicOffsetCount,
-                          const uint32_t* dynamicOffsets);
+                          uint32_t dynamicOffsetCount = 0,
+                          const uint32_t* dynamicOffsets = nullptr);
 
       protected:
         // Construct an "error" programmable pass encoder.
diff --git a/src/dawn_native/Queue.cpp b/src/dawn_native/Queue.cpp
index cd3a135..11b7f5e 100644
--- a/src/dawn_native/Queue.cpp
+++ b/src/dawn_native/Queue.cpp
@@ -17,14 +17,18 @@
 #include "common/Constants.h"
 #include "dawn_native/Buffer.h"
 #include "dawn_native/CommandBuffer.h"
+#include "dawn_native/CommandEncoder.h"
 #include "dawn_native/CommandValidation.h"
 #include "dawn_native/Commands.h"
+#include "dawn_native/CopyTextureForBrowserHelper.h"
 #include "dawn_native/Device.h"
 #include "dawn_native/DynamicUploader.h"
 #include "dawn_native/ErrorScope.h"
 #include "dawn_native/ErrorScopeTracker.h"
 #include "dawn_native/Fence.h"
 #include "dawn_native/QuerySet.h"
+#include "dawn_native/RenderPassEncoder.h"
+#include "dawn_native/RenderPipeline.h"
 #include "dawn_native/Texture.h"
 #include "dawn_platform/DawnPlatform.h"
 #include "dawn_platform/tracing/TraceEvent.h"
@@ -131,7 +135,6 @@
                 UNREACHABLE();
             }
         };
-
     }  // namespace
 
     // QueueBase
@@ -306,6 +309,23 @@
         return device->CopyFromStagingToTexture(uploadHandle.stagingBuffer, passDataLayout,
                                                 &textureCopy, writeSizePixel);
     }
+
+    void QueueBase::CopyTextureForBrowser(const TextureCopyView* source,
+                                          const TextureCopyView* destination,
+                                          const Extent3D* copySize) {
+        GetDevice()->ConsumedError(CopyTextureForBrowserInternal(source, destination, copySize));
+    }
+
+    MaybeError QueueBase::CopyTextureForBrowserInternal(const TextureCopyView* source,
+                                                        const TextureCopyView* destination,
+                                                        const Extent3D* copySize) {
+        if (GetDevice()->IsValidationEnabled()) {
+            DAWN_TRY(ValidateCopyTextureForBrowser(GetDevice(), source, destination, copySize));
+        }
+
+        return DoCopyTextureForBrowser(GetDevice(), source, destination, copySize);
+    }
+
     MaybeError QueueBase::ValidateSubmit(uint32_t commandCount,
                                          CommandBufferBase* const* commands) const {
         TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "Queue::ValidateSubmit");
diff --git a/src/dawn_native/Queue.h b/src/dawn_native/Queue.h
index 9e0d6c2..00a132d 100644
--- a/src/dawn_native/Queue.h
+++ b/src/dawn_native/Queue.h
@@ -45,6 +45,9 @@
                           size_t dataSize,
                           const TextureDataLayout* dataLayout,
                           const Extent3D* writeSize);
+        void CopyTextureForBrowser(const TextureCopyView* source,
+                                   const TextureCopyView* destination,
+                                   const Extent3D* copySize);
 
         void TrackTask(std::unique_ptr<TaskInFlight> task, ExecutionSerial serial);
         void Tick(ExecutionSerial finishedSerial);
@@ -63,6 +66,9 @@
                                         size_t dataSize,
                                         const TextureDataLayout* dataLayout,
                                         const Extent3D* writeSize);
+        MaybeError CopyTextureForBrowserInternal(const TextureCopyView* source,
+                                                 const TextureCopyView* destination,
+                                                 const Extent3D* copySize);
 
         virtual MaybeError SubmitImpl(uint32_t commandCount,
                                       CommandBufferBase* const* commands) = 0;
diff --git a/src/dawn_native/RenderEncoderBase.h b/src/dawn_native/RenderEncoderBase.h
index b3f8543..670c961 100644
--- a/src/dawn_native/RenderEncoderBase.h
+++ b/src/dawn_native/RenderEncoderBase.h
@@ -25,9 +25,9 @@
         RenderEncoderBase(DeviceBase* device, EncodingContext* encodingContext);
 
         void Draw(uint32_t vertexCount,
-                  uint32_t instanceCount,
-                  uint32_t firstVertex,
-                  uint32_t firstInstance);
+                  uint32_t instanceCount = 1,
+                  uint32_t firstVertex = 0,
+                  uint32_t firstInstance = 0);
         void DrawIndexed(uint32_t vertexCount,
                          uint32_t instanceCount,
                          uint32_t firstIndex,
diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn
index ad45f05..5a9477c 100644
--- a/src/tests/BUILD.gn
+++ b/src/tests/BUILD.gn
@@ -275,6 +275,7 @@
     "end2end/ComputeSharedMemoryTests.cpp",
     "end2end/ComputeStorageBufferBarrierTests.cpp",
     "end2end/CopyTests.cpp",
+    "end2end/CopyTextureForBrowserTests.cpp",
     "end2end/CreateReadyPipelineTests.cpp",
     "end2end/CullingTests.cpp",
     "end2end/DebugMarkerTests.cpp",
diff --git a/src/tests/end2end/CopyTextureForBrowserTests.cpp b/src/tests/end2end/CopyTextureForBrowserTests.cpp
new file mode 100644
index 0000000..1ab8a44
--- /dev/null
+++ b/src/tests/end2end/CopyTextureForBrowserTests.cpp
@@ -0,0 +1,183 @@
+// Copyright 2020 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "tests/DawnTest.h"
+
+#include "common/Constants.h"
+#include "common/Math.h"
+#include "utils/TestUtils.h"
+#include "utils/TextureFormatUtils.h"
+#include "utils/WGPUHelpers.h"
+
+class CopyTextureForBrowserTests : public DawnTest {
+  protected:
+    static constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::RGBA8Unorm;
+
+    struct TextureSpec {
+        wgpu::Origin3D copyOrigin;
+        wgpu::Extent3D textureSize;
+        uint32_t level;
+    };
+
+    static std::vector<RGBA8> GetExpectedTextureData(const utils::TextureDataCopyLayout& layout) {
+        std::vector<RGBA8> textureData(layout.texelBlockCount);
+        for (uint32_t layer = 0; layer < layout.mipSize.depth; ++layer) {
+            const uint32_t sliceOffset = layout.texelBlocksPerImage * layer;
+            for (uint32_t y = 0; y < layout.mipSize.height; ++y) {
+                const uint32_t rowOffset = layout.texelBlocksPerRow * y;
+                for (uint32_t x = 0; x < layout.mipSize.width; ++x) {
+                    textureData[sliceOffset + rowOffset + x] =
+                        RGBA8(static_cast<uint8_t>((x + layer * x) % 256),
+                              static_cast<uint8_t>((y + layer * y) % 256),
+                              static_cast<uint8_t>(x / 256), static_cast<uint8_t>(y / 256));
+                }
+            }
+        }
+
+        return textureData;
+    }
+
+    static void PackTextureData(const RGBA8* srcData,
+                                uint32_t width,
+                                uint32_t height,
+                                uint32_t srcTexelsPerRow,
+                                RGBA8* dstData,
+                                uint32_t dstTexelsPerRow) {
+        for (unsigned int y = 0; y < height; ++y) {
+            for (unsigned int x = 0; x < width; ++x) {
+                unsigned int src = x + y * srcTexelsPerRow;
+                unsigned int dst = x + y * dstTexelsPerRow;
+                dstData[dst] = srcData[src];
+            }
+        }
+    }
+
+    void DoTest(const TextureSpec& srcSpec,
+                const TextureSpec& dstSpec,
+                const wgpu::Extent3D& copySize) {
+        wgpu::TextureDescriptor srcDescriptor;
+        srcDescriptor.size = srcSpec.textureSize;
+        srcDescriptor.format = kTextureFormat;
+        srcDescriptor.mipLevelCount = srcSpec.level + 1;
+        srcDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
+                              wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment;
+        wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor);
+
+        wgpu::Texture dstTexture;
+        wgpu::TextureDescriptor dstDescriptor;
+        dstDescriptor.size = dstSpec.textureSize;
+        dstDescriptor.format = kTextureFormat;
+        dstDescriptor.mipLevelCount = dstSpec.level + 1;
+        dstDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
+                              wgpu::TextureUsage::OutputAttachment;
+        dstTexture = device.CreateTexture(&dstDescriptor);
+
+        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+
+        // Use writeTexture to populate the current slice of the texture in
+        // `level` mip level for all platforms except OpenGL.
+        // TODO(shaobo.yan@intel.com): OpenGL doesn't have 'WriteTexture' implementation.
+        const utils::TextureDataCopyLayout copyLayout =
+            utils::GetTextureDataCopyLayoutForTexture2DAtLevel(
+                kTextureFormat,
+                {srcSpec.textureSize.width, srcSpec.textureSize.height, copySize.depth},
+                srcSpec.level, 0);
+
+        const std::vector<RGBA8> textureArrayCopyData = GetExpectedTextureData(copyLayout);
+        wgpu::TextureCopyView textureCopyView =
+            utils::CreateTextureCopyView(srcTexture, srcSpec.level, {0, 0, srcSpec.copyOrigin.z});
+
+        wgpu::TextureDataLayout textureDataLayout;
+        textureDataLayout.offset = 0;
+        textureDataLayout.bytesPerRow = copyLayout.bytesPerRow;
+        textureDataLayout.rowsPerImage = copyLayout.bytesPerImage / copyLayout.bytesPerRow;
+
+        device.GetDefaultQueue().WriteTexture(&textureCopyView, textureArrayCopyData.data(),
+                                              textureArrayCopyData.size() * sizeof(RGBA8),
+                                              &textureDataLayout, &copyLayout.mipSize);
+
+        const wgpu::Extent3D copySizePerSlice = {copySize.width, copySize.height, 1};
+        // Perform the texture to texture copy
+        wgpu::TextureCopyView srcTextureCopyView =
+            utils::CreateTextureCopyView(srcTexture, srcSpec.level, srcSpec.copyOrigin);
+        wgpu::TextureCopyView dstTextureCopyView =
+            utils::CreateTextureCopyView(dstTexture, dstSpec.level, dstSpec.copyOrigin);
+
+        wgpu::CommandBuffer commands = encoder.Finish();
+        queue.Submit(1, &commands);
+
+        // Perform a copy here for testing.
+        device.GetDefaultQueue().CopyTextureForBrowser(&srcTextureCopyView, &dstTextureCopyView,
+                                                       &copySize);
+
+        // Texels in single slice.
+        const uint32_t texelCountInCopyRegion = utils::GetTexelCountInCopyRegion(
+            copyLayout.bytesPerRow, copyLayout.bytesPerImage / copyLayout.bytesPerRow,
+            copySizePerSlice, kTextureFormat);
+        std::vector<RGBA8> expected(texelCountInCopyRegion);
+        for (uint32_t slice = 0; slice < copySize.depth; ++slice) {
+            std::fill(expected.begin(), expected.end(), RGBA8());
+            const uint32_t texelIndexOffset = copyLayout.texelBlocksPerImage * slice;
+            const uint32_t expectedTexelArrayDataStartIndex =
+                texelIndexOffset +
+                (srcSpec.copyOrigin.x + srcSpec.copyOrigin.y * copyLayout.texelBlocksPerRow);
+            PackTextureData(&textureArrayCopyData[expectedTexelArrayDataStartIndex], copySize.width,
+                            copySize.height, copyLayout.texelBlocksPerRow, expected.data(),
+                            copySize.width);
+
+            EXPECT_TEXTURE_RGBA8_EQ(expected.data(), dstTexture, dstSpec.copyOrigin.x,
+                                    dstSpec.copyOrigin.y, copySize.width, copySize.height,
+                                    dstSpec.level, dstSpec.copyOrigin.z + slice)
+                << "Texture to Texture copy failed copying region [(" << srcSpec.copyOrigin.x
+                << ", " << srcSpec.copyOrigin.y << "), (" << srcSpec.copyOrigin.x + copySize.width
+                << ", " << srcSpec.copyOrigin.y + copySize.height << ")) from "
+                << srcSpec.textureSize.width << " x " << srcSpec.textureSize.height
+                << " texture at mip level " << srcSpec.level << " layer "
+                << srcSpec.copyOrigin.z + slice << " to [(" << dstSpec.copyOrigin.x << ", "
+                << dstSpec.copyOrigin.y << "), (" << dstSpec.copyOrigin.x + copySize.width << ", "
+                << dstSpec.copyOrigin.y + copySize.height << ")) region of "
+                << dstSpec.textureSize.width << " x " << dstSpec.textureSize.height
+                << " texture at mip level " << dstSpec.level << " layer "
+                << dstSpec.copyOrigin.z + slice << std::endl;
+        }
+    }
+};
+
+// Verify CopyTextureForBrowserTests works with internal pipeline.
+// The case do copy without any transform.
+TEST_P(CopyTextureForBrowserTests, PassthroughCopy) {
+    // These tests fails due to crbug.com/tint/63.
+    DAWN_SKIP_TEST_IF(IsSwiftshader());
+    DAWN_SKIP_TEST_IF(IsVulkan());
+    DAWN_SKIP_TEST_IF(IsD3D12() && IsBackendValidationEnabled());
+
+    // OpenGL tests fails due to 'WriteTexture' unimplemented.
+    // Related bug : crbug.com/dawn/483
+    DAWN_SKIP_TEST_IF(IsOpenGL());
+
+    constexpr uint32_t kWidth = 10;
+    constexpr uint32_t kHeight = 1;
+
+    TextureSpec textureSpec;
+    textureSpec.copyOrigin = {0, 0, 0};
+    textureSpec.level = 0;
+    textureSpec.textureSize = {kWidth, kHeight, 1};
+    DoTest(textureSpec, textureSpec, {kWidth, kHeight, 1});
+}
+
+DAWN_INSTANTIATE_TEST(CopyTextureForBrowserTests,
+                      D3D12Backend(),
+                      MetalBackend(),
+                      OpenGLBackend(),
+                      VulkanBackend());