Dawn: submit all command buffers in a single call.

All command buffers are now appended to a single vector, and submitted
submitted in onFinishFlush() in a single submit.

In order to do this, implement a centralized command encoder for copies.
This command encoder is used for buffer-to-buffer, buffer-to-texture and
texture-to-buffer copies.

Change-Id: If1092ae4edbe03c995d4cb80e0dc124516d63dad
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/235826
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Stephen White <senorblanco@chromium.org>
diff --git a/src/gpu/dawn/GrDawnCaps.h b/src/gpu/dawn/GrDawnCaps.h
index e96f0cd..41ca2a2 100644
--- a/src/gpu/dawn/GrDawnCaps.h
+++ b/src/gpu/dawn/GrDawnCaps.h
@@ -78,7 +78,7 @@
     SupportedRead onSupportedReadPixelsColorType(GrColorType srcColorType,
                                                  const GrBackendFormat& backendFormat,
                                                  GrColorType dstColorType) const override {
-        return { GrColorType::kUnknown, 0 };
+        return { srcColorType, GrColorTypeBytesPerPixel(srcColorType) };
     }
 
     typedef GrCaps INHERITED;
diff --git a/src/gpu/dawn/GrDawnGpu.cpp b/src/gpu/dawn/GrDawnGpu.cpp
index 0eb7ed8..7799f2a 100644
--- a/src/gpu/dawn/GrDawnGpu.cpp
+++ b/src/gpu/dawn/GrDawnGpu.cpp
@@ -91,7 +91,8 @@
         SkASSERT(!"uploading to non-texture unimplemented");
         return false;
     }
-    texture->upload(texels, mipLevelCount, SkIRect::MakeXYWH(left, top, width, height));
+    texture->upload(texels, mipLevelCount, SkIRect::MakeXYWH(left, top, width, height),
+                    this->getCopyEncoder());
     return true;
 }
 
@@ -142,7 +143,7 @@
     if (!tex) {
         return nullptr;
     }
-    tex->upload(texels, mipLevelCount);
+    tex->upload(texels, mipLevelCount, this->getCopyEncoder());
     return tex;
 }
 
@@ -394,14 +395,20 @@
 }
 
 void GrDawnGpu::testingOnly_flushGpuAndSync() {
-    SkASSERT(!"unimplemented");
+    this->flush();
 }
 
 #endif
 
+void GrDawnGpu::flush() {
+    this->flushCopyEncoder();
+    fQueue.Submit(fCommandBuffers.size(), &fCommandBuffers.front());
+    fCommandBuffers.clear();
+}
+
 void GrDawnGpu::onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
                               const GrFlushInfo& info, const GrPrepareForExternalIORequests&) {
-    SkASSERT(!"unimplemented");
+    this->flush();
 }
 
 static dawn::Texture get_dawn_texture_from_surface(GrSurface* src) {
@@ -433,10 +440,7 @@
     dstTextureView.origin = {(uint32_t) dstPoint.x(), (uint32_t) dstPoint.y(), 0};
 
     dawn::Extent3D copySize = {width, height, 1};
-    auto encoder = device().CreateCommandEncoder();
-    encoder.CopyTextureToTexture(&srcTextureView, &dstTextureView, &copySize);
-    auto commandBuffer = encoder.Finish();
-    this->queue().Submit(1, &commandBuffer);
+    this->getCopyEncoder().CopyTextureToTexture(&srcTextureView, &dstTextureView, &copySize);
     return true;
 }
 
@@ -475,10 +479,8 @@
     dstBuffer.imageHeight = height;
 
     dawn::Extent3D copySize = {(uint32_t) width, (uint32_t) height, 1};
-    auto encoder = device().CreateCommandEncoder();
-    encoder.CopyTextureToBuffer(&srcTexture, &dstBuffer, &copySize);
-    auto commandBuffer = encoder.Finish();
-    queue().Submit(1, &commandBuffer);
+    this->getCopyEncoder().CopyTextureToBuffer(&srcTexture, &dstBuffer, &copySize);
+    flush();
 
     const void *readPixelsPtr = nullptr;
     buf.MapReadAsync(callback, &readPixelsPtr);
@@ -507,6 +509,7 @@
 }
 
 void GrDawnGpu::submit(GrOpsRenderPass* renderPass) {
+    this->flushCopyEncoder();
     static_cast<GrDawnOpsRenderPass*>(renderPass)->submit();
 }
 
@@ -556,3 +559,23 @@
 GrDawnRingBuffer::Slice GrDawnGpu::allocateUniformRingBufferSlice(int size) {
     return fUniformRingBuffer.allocate(size);
 }
+
+void GrDawnGpu::appendCommandBuffer(dawn::CommandBuffer commandBuffer) {
+    if (commandBuffer) {
+        fCommandBuffers.push_back(commandBuffer);
+    }
+}
+
+dawn::CommandEncoder GrDawnGpu::getCopyEncoder() {
+    if (!fCopyEncoder) {
+        fCopyEncoder = fDevice.CreateCommandEncoder();
+    }
+    return fCopyEncoder;
+}
+
+void GrDawnGpu::flushCopyEncoder() {
+    if (fCopyEncoder) {
+        fCommandBuffers.push_back(fCopyEncoder.Finish());
+        fCopyEncoder = nullptr;
+    }
+}
diff --git a/src/gpu/dawn/GrDawnGpu.h b/src/gpu/dawn/GrDawnGpu.h
index e213d18..2098c3c 100644
--- a/src/gpu/dawn/GrDawnGpu.h
+++ b/src/gpu/dawn/GrDawnGpu.h
@@ -50,6 +50,7 @@
 
     void testingOnly_flushGpuAndSync() override;
 #endif
+    void flush();
 
     GrStencilAttachment* createStencilAttachmentForRenderTarget(const GrRenderTarget*,
                                                                 int width,
@@ -82,6 +83,9 @@
     sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
 
     GrDawnRingBuffer::Slice allocateUniformRingBufferSlice(int size);
+    dawn::CommandEncoder getCopyEncoder();
+    void flushCopyEncoder();
+    void appendCommandBuffer(dawn::CommandBuffer commandBuffer);
 
 private:
     void onResetContext(uint32_t resetBits) override {}
@@ -147,6 +151,8 @@
     std::unique_ptr<SkSL::Compiler>                 fCompiler;
     std::unique_ptr<GrDawnOpsRenderPass>            fOpsRenderPass;
     GrDawnRingBuffer                                fUniformRingBuffer;
+    dawn::CommandEncoder                            fCopyEncoder;
+    std::vector<dawn::CommandBuffer>                fCommandBuffers;
 
     typedef GrGpu INHERITED;
 };
diff --git a/src/gpu/dawn/GrDawnOpsRenderPass.cpp b/src/gpu/dawn/GrDawnOpsRenderPass.cpp
index dc17108..d14a88c 100644
--- a/src/gpu/dawn/GrDawnOpsRenderPass.cpp
+++ b/src/gpu/dawn/GrDawnOpsRenderPass.cpp
@@ -98,10 +98,7 @@
 }
 
 void GrDawnOpsRenderPass::submit() {
-    dawn::CommandBuffer commandBuffer = fEncoder.Finish();
-    if (commandBuffer) {
-        fGpu->queue().Submit(1, &commandBuffer);
-    }
+    fGpu->appendCommandBuffer(fEncoder.Finish());
 }
 
 void GrDawnOpsRenderPass::insertEventMarker(const char* msg) {
diff --git a/src/gpu/dawn/GrDawnTexture.cpp b/src/gpu/dawn/GrDawnTexture.cpp
index 1fc6e0a..ee9df7d 100644
--- a/src/gpu/dawn/GrDawnTexture.cpp
+++ b/src/gpu/dawn/GrDawnTexture.cpp
@@ -124,13 +124,14 @@
     return GrBackendTexture(this->width(), this->height(), fInfo);
 }
 
-void GrDawnTexture::upload(const GrMipLevel texels[], int mipLevels) {
-    upload(texels, mipLevels, SkIRect::MakeWH(width(), height()));
+void GrDawnTexture::upload(const GrMipLevel texels[], int mipLevels,
+                           dawn::CommandEncoder copyEncoder) {
+    this->upload(texels, mipLevels, SkIRect::MakeWH(width(), height()), copyEncoder);
 }
 
-void GrDawnTexture::upload(const GrMipLevel texels[], int mipLevels, const SkIRect& rect) {
+void GrDawnTexture::upload(const GrMipLevel texels[], int mipLevels, const SkIRect& rect,
+                           dawn::CommandEncoder copyEncoder) {
     dawn::Device device = this->getDawnGpu()->device();
-    dawn::Queue queue = this->getDawnGpu()->queue();
 
     uint32_t x = rect.x();
     uint32_t y = rect.y();
@@ -200,11 +201,7 @@
         dstTexture.origin = {x, y, 0};
 
         dawn::Extent3D copySize = {width, height, 1};
-        dawn::CommandEncoder encoder = device.CreateCommandEncoder();
-        encoder.CopyBufferToTexture(&srcBuffer, &dstTexture, &copySize);
-        dawn::CommandBuffer copy = encoder.Finish();
-        queue.Submit(1, &copy);
-
+        copyEncoder.CopyBufferToTexture(&srcBuffer, &dstTexture, &copySize);
         x /= 2;
         y /= 2;
         width /= 2;
diff --git a/src/gpu/dawn/GrDawnTexture.h b/src/gpu/dawn/GrDawnTexture.h
index b809124..cc69fd7 100644
--- a/src/gpu/dawn/GrDawnTexture.h
+++ b/src/gpu/dawn/GrDawnTexture.h
@@ -32,8 +32,9 @@
 
     void textureParamsModified() override {}
 
-    void upload(const GrMipLevel texels[], int mipLevels);
-    void upload(const GrMipLevel texels[], int mipLevels, const SkIRect& dstRect);
+    void upload(const GrMipLevel texels[], int mipLevels, dawn::CommandEncoder copyEncoder);
+    void upload(const GrMipLevel texels[], int mipLevels, const SkIRect& dstRect,
+                dawn::CommandEncoder copyEncoder);
 
     dawn::Texture texture() const { return fInfo.fTexture; }
     dawn::TextureView textureView() const { return fTextureView; }