Dawn: implement a ring buffer for uniform uploads.

GrDawnGpu vends ring buffer slices out of a persistent (larger) buffer.

GrDawnProgramDataManager::setData() now returns a BindGroup containing
the (possibly new) UBO bindings, as well as the texture and sampler
bindings.
Change-Id: Id6694d6f44a815cfbffe4293779bf9bf558a2365
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/235866
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Stephen White <senorblanco@chromium.org>
diff --git a/src/gpu/dawn/GrDawnGpu.cpp b/src/gpu/dawn/GrDawnGpu.cpp
index 1fd124f..f21cbb9 100644
--- a/src/gpu/dawn/GrDawnGpu.cpp
+++ b/src/gpu/dawn/GrDawnGpu.cpp
@@ -49,7 +49,8 @@
         : INHERITED(context)
         , fDevice(device)
         , fQueue(device.CreateQueue())
-        , fCompiler(new SkSL::Compiler()) {
+        , fCompiler(new SkSL::Compiler())
+        , fUniformRingBuffer(this, dawn::BufferUsageBit::Uniform) {
     fCaps.reset(new GrDawnCaps(options));
 }
 
@@ -543,3 +544,7 @@
     SkASSERT(!"unimplemented");
     return nullptr;
 }
+
+GrDawnRingBuffer::Slice GrDawnGpu::allocateUniformRingBufferSlice(int size) {
+    return fUniformRingBuffer.allocate(size);
+}
diff --git a/src/gpu/dawn/GrDawnGpu.h b/src/gpu/dawn/GrDawnGpu.h
index 3973055..736e6cc 100644
--- a/src/gpu/dawn/GrDawnGpu.h
+++ b/src/gpu/dawn/GrDawnGpu.h
@@ -10,6 +10,7 @@
 
 #include "src/gpu/GrGpu.h"
 #include "dawn/dawncpp.h"
+#include "src/gpu/dawn/GrDawnRingBuffer.h"
 
 class GrPipeline;
 class GrDawnGpuRTCommandBuffer;
@@ -82,6 +83,8 @@
 
     sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
 
+    GrDawnRingBuffer::Slice allocateUniformRingBufferSlice(int size);
+
 private:
     void onResetContext(uint32_t resetBits) override {}
 
@@ -147,6 +150,7 @@
     std::unique_ptr<SkSL::Compiler>                 fCompiler;
     std::unique_ptr<GrDawnGpuRTCommandBuffer>       fRTCommandBuffer;
     std::unique_ptr<GrDawnGpuTextureCommandBuffer>  fTextureCommandBuffer;
+    GrDawnRingBuffer                                fUniformRingBuffer;
 
     typedef GrGpu INHERITED;
 };
diff --git a/src/gpu/dawn/GrDawnGpuCommandBuffer.cpp b/src/gpu/dawn/GrDawnGpuCommandBuffer.cpp
index 020207e..312663d 100644
--- a/src/gpu/dawn/GrDawnGpuCommandBuffer.cpp
+++ b/src/gpu/dawn/GrDawnGpuCommandBuffer.cpp
@@ -326,7 +326,8 @@
                                                                colorFormat, hasDepthStencil,
                                                                stencilFormat, &desc);
     SkASSERT(program);
-    program->setData(primProc, fRenderTarget, fOrigin, pipeline);
+    auto bindGroup = program->setData(fGpu, fRenderTarget, fOrigin, primProc, pipeline,
+                                      primProcProxies);
 
     std::vector<dawn::VertexBufferDescriptor> inputs;
     std::vector<dawn::VertexAttributeDescriptor> vertexAttributes;
@@ -404,7 +405,7 @@
     rpDesc.colorStates = colorStates;
     dawn::RenderPipeline renderPipeline = fGpu->device().CreateRenderPipeline(&rpDesc);
     fPassEncoder.SetPipeline(renderPipeline);
-    fPassEncoder.SetBindGroup(0, program->fBindGroup, 0, nullptr);
+    fPassEncoder.SetBindGroup(0, bindGroup, 0, nullptr);
     if (pipeline.isStencilEnabled()) {
         fPassEncoder.SetStencilReference(pipeline.getUserStencil()->fFront.fRef);
     }
diff --git a/src/gpu/dawn/GrDawnProgramBuilder.cpp b/src/gpu/dawn/GrDawnProgramBuilder.cpp
index f0fe759..db51fce 100644
--- a/src/gpu/dawn/GrDawnProgramBuilder.cpp
+++ b/src/gpu/dawn/GrDawnProgramBuilder.cpp
@@ -347,54 +347,11 @@
     dawn::BindGroupLayoutDescriptor bindGroupLayoutDesc;
     bindGroupLayoutDesc.bindingCount = layoutBindings.size();
     bindGroupLayoutDesc.bindings = layoutBindings.data();
-    auto bindGroupLayout = gpu->device().CreateBindGroupLayout(&bindGroupLayoutDesc);
+    result->fBindGroupLayout = gpu->device().CreateBindGroupLayout(&bindGroupLayoutDesc);
     dawn::PipelineLayoutDescriptor pipelineLayoutDesc;
     pipelineLayoutDesc.bindGroupLayoutCount = 1;
-    pipelineLayoutDesc.bindGroupLayouts = &bindGroupLayout;
+    pipelineLayoutDesc.bindGroupLayouts = &result->fBindGroupLayout;
     result->fPipelineLayout = gpu->device().CreatePipelineLayout(&pipelineLayoutDesc);
-    if (0 != geometryUniformSize) {
-        dawn::BufferDescriptor desc;
-        desc.usage = dawn::BufferUsageBit::Uniform | dawn::BufferUsageBit::CopyDst;
-        desc.size = geometryUniformSize;
-        result->fGeometryUniformBuffer = gpu->device().CreateBuffer(&desc);
-        bindings.push_back(make_bind_group_binding(GrDawnUniformHandler::kGeometryBinding,
-                                                   result->fGeometryUniformBuffer,
-                                                   0, geometryUniformSize));
-    }
-    if (0 != fragmentUniformSize) {
-        dawn::BufferDescriptor desc;
-        desc.usage = dawn::BufferUsageBit::Uniform | dawn::BufferUsageBit::CopyDst;
-        desc.size = fragmentUniformSize;
-        result->fFragmentUniformBuffer = gpu->device().CreateBuffer(&desc);
-        bindings.push_back(make_bind_group_binding(GrDawnUniformHandler::kFragBinding,
-                                                   result->fFragmentUniformBuffer,
-                                                   0, fragmentUniformSize));
-    }
-    binding = GrDawnUniformHandler::kSamplerBindingBase;
-    for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
-        dawn::Sampler sampler = create_sampler(gpu, primProc.textureSampler(i).samplerState());
-        bindings.push_back(make_bind_group_binding(binding++, sampler));
-        GrDawnTexture* tex = static_cast<GrDawnTexture*>(primProcProxies[i]->peekTexture());
-        dawn::TextureView textureView = tex->textureView();
-        bindings.push_back(make_bind_group_binding(binding++, textureView));
-    }
-    GrFragmentProcessor::Iter iter(pipeline);
-    const GrFragmentProcessor* fp = iter.next();
-    while (fp) {
-        for (int i = 0; i < fp->numTextureSamplers(); ++i) {
-            dawn::Sampler sampler = create_sampler(gpu, fp->textureSampler(i).samplerState());
-            bindings.push_back(make_bind_group_binding(binding++, sampler));
-            GrDawnTexture* tex = static_cast<GrDawnTexture*>(fp->textureSampler(i).peekTexture());
-            dawn::TextureView textureView = tex->textureView();
-            bindings.push_back(make_bind_group_binding(binding++, textureView));
-        }
-        fp = iter.next();
-    }
-    dawn::BindGroupDescriptor bindGroupDescriptor;
-    bindGroupDescriptor.layout = bindGroupLayout;
-    bindGroupDescriptor.bindingCount = bindings.size();
-    bindGroupDescriptor.bindings = bindings.data();
-    result->fBindGroup = gpu->device().CreateBindGroup(&bindGroupDescriptor);
     result->fBuiltinUniformHandles = builder.fUniformHandles;
     result->fColorState = create_color_state(gpu, pipeline, colorFormat);
     GrStencilSettings stencil;
@@ -465,22 +422,70 @@
     }
 }
 
-void GrDawnProgram::setData(const GrPrimitiveProcessor& primProc,
-                            const GrRenderTarget* renderTarget,
-                            GrSurfaceOrigin origin,
-                            const GrPipeline& pipeline) {
+static void setTexture(GrDawnGpu* gpu, const GrSamplerState& state, GrTexture* texture,
+                       std::vector<dawn::BindGroupBinding> *bindings, int* binding) {
+    // FIXME: could probably cache samplers in GrDawnProgram
+    dawn::Sampler sampler = create_sampler(gpu, state);
+    bindings->push_back(make_bind_group_binding((*binding)++, sampler));
+    GrDawnTexture* tex = static_cast<GrDawnTexture*>(texture);
+    dawn::TextureView textureView = tex->textureView();
+    bindings->push_back(make_bind_group_binding((*binding)++, textureView));
+}
+
+dawn::BindGroup GrDawnProgram::setData(GrDawnGpu* gpu, const GrRenderTarget* renderTarget,
+                                       GrSurfaceOrigin origin,
+                                       const GrPrimitiveProcessor& primProc,
+                                       const GrPipeline& pipeline,
+                                       const GrTextureProxy* const primProcTextures[]) {
+    std::vector<dawn::BindGroupBinding> bindings;
+    GrDawnRingBuffer::Slice geom, frag;
+    uint32_t geometryUniformSize = fDataManager.geometryUniformSize();
+    uint32_t fragmentUniformSize = fDataManager.fragmentUniformSize();
+    if (0 != geometryUniformSize) {
+        geom = gpu->allocateUniformRingBufferSlice(geometryUniformSize);
+        bindings.push_back(make_bind_group_binding(GrDawnUniformHandler::kGeometryBinding,
+                                                   geom.fBuffer, geom.fOffset,
+                                                   geometryUniformSize));
+    }
+    if (0 != fragmentUniformSize) {
+        frag = gpu->allocateUniformRingBufferSlice(fragmentUniformSize);
+        bindings.push_back(make_bind_group_binding(GrDawnUniformHandler::kFragBinding,
+                                                   frag.fBuffer, frag.fOffset,
+                                                   fragmentUniformSize));
+    }
     this->setRenderTargetState(renderTarget, origin);
     fGeometryProcessor->setData(fDataManager, primProc,
                                 GrFragmentProcessor::CoordTransformIter(pipeline));
+    int binding = GrDawnUniformHandler::kSamplerBindingBase;
+    for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
+        auto& sampler = primProc.textureSampler(i);
+        setTexture(gpu, sampler.samplerState(), primProcTextures[i]->peekTexture(), &bindings,
+                   &binding);
+    }
     GrFragmentProcessor::Iter iter(pipeline);
     GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.get(), fFragmentProcessorCnt);
     const GrFragmentProcessor* fp = iter.next();
     GrGLSLFragmentProcessor* glslFP = glslIter.next();
     while (fp && glslFP) {
         glslFP->setData(fDataManager, *fp);
+        for (int i = 0; i < fp->numTextureSamplers(); ++i) {
+            auto& s = fp->textureSampler(i);
+            setTexture(gpu, s.samplerState(), s.peekTexture(), &bindings, &binding);
+        }
         fp = iter.next();
         glslFP = glslIter.next();
     }
-    fDataManager.uploadUniformBuffers(fGeometryUniformBuffer,
-                                      fFragmentUniformBuffer);
+    SkIPoint offset;
+    GrTexture* dstTexture = pipeline.peekDstTexture(&offset);
+    fXferProcessor->setData(fDataManager, pipeline.getXferProcessor(), dstTexture, offset);
+    if (GrTextureProxy* proxy = pipeline.dstTextureProxy()) {
+        GrFragmentProcessor::TextureSampler sampler(sk_ref_sp(proxy));
+        setTexture(gpu, sampler.samplerState(), sampler.peekTexture(), &bindings, &binding);
+    }
+    fDataManager.uploadUniformBuffers(geom, frag);
+    dawn::BindGroupDescriptor descriptor;
+    descriptor.layout = fBindGroupLayout;
+    descriptor.bindingCount = bindings.size();
+    descriptor.bindings = bindings.data();
+    return gpu->device().CreateBindGroup(&descriptor);
 }
diff --git a/src/gpu/dawn/GrDawnProgramBuilder.h b/src/gpu/dawn/GrDawnProgramBuilder.h
index 319dff9..a98f270 100644
--- a/src/gpu/dawn/GrDawnProgramBuilder.h
+++ b/src/gpu/dawn/GrDawnProgramBuilder.h
@@ -60,8 +60,7 @@
     std::unique_ptr<GrGLSLXferProcessor> fXferProcessor;
     std::unique_ptr<std::unique_ptr<GrGLSLFragmentProcessor>[]> fFragmentProcessors;
     int fFragmentProcessorCnt;
-    dawn::Buffer fGeometryUniformBuffer;
-    dawn::Buffer fFragmentUniformBuffer;
+    dawn::BindGroupLayout fBindGroupLayout;
     dawn::PipelineLayout fPipelineLayout;
     dawn::BindGroup fBindGroup;
     dawn::ColorStateDescriptor fColorState;
@@ -71,8 +70,9 @@
     BuiltinUniformHandles fBuiltinUniformHandles;
 
     void setRenderTargetState(const GrRenderTarget*, GrSurfaceOrigin);
-    void setData(const GrPrimitiveProcessor&, const GrRenderTarget*, GrSurfaceOrigin,
-                 const GrPipeline&);
+    dawn::BindGroup setData(GrDawnGpu* gpu, const GrRenderTarget*, GrSurfaceOrigin origin,
+                            const GrPrimitiveProcessor&, const GrPipeline&,
+                            const GrTextureProxy* const primProcTextures[]);
 };
 
 class GrDawnProgramBuilder : public GrGLSLProgramBuilder {
diff --git a/src/gpu/dawn/GrDawnProgramDataManager.cpp b/src/gpu/dawn/GrDawnProgramDataManager.cpp
index 45371f4..b2bb013 100644
--- a/src/gpu/dawn/GrDawnProgramDataManager.cpp
+++ b/src/gpu/dawn/GrDawnProgramDataManager.cpp
@@ -263,16 +263,17 @@
     }
 };
 
-void GrDawnProgramDataManager::uploadUniformBuffers(dawn::Buffer geometryBuffer,
-                                                    dawn::Buffer fragmentBuffer) const {
-    if (geometryBuffer && fGeometryUniformsDirty) {
-        geometryBuffer.SetSubData(0, fGeometryUniformSize,
-                                  static_cast<const uint8_t*>(fGeometryUniformData.get()));
-        fGeometryUniformsDirty = false;
+void GrDawnProgramDataManager::uploadUniformBuffers(GrDawnRingBuffer::Slice geometryBuffer,
+                                                    GrDawnRingBuffer::Slice fragmentBuffer) const {
+
+    dawn::Buffer geom = geometryBuffer.fBuffer;
+    dawn::Buffer frag = fragmentBuffer.fBuffer;
+    if (geom && fGeometryUniformsDirty) {
+        geom.SetSubData(geometryBuffer.fOffset, fGeometryUniformSize,
+                        static_cast<const uint8_t*>(fGeometryUniformData.get()));
     }
-    if (fragmentBuffer && fFragmentUniformsDirty) {
-        fragmentBuffer.SetSubData(0, fFragmentUniformSize,
-                                  static_cast<const uint8_t*>(fFragmentUniformData.get()));
-        fFragmentUniformsDirty = false;
+    if (frag && fFragmentUniformsDirty) {
+        frag.SetSubData(fragmentBuffer.fOffset, fFragmentUniformSize,
+                        static_cast<const uint8_t*>(fFragmentUniformData.get()));
     }
 }
diff --git a/src/gpu/dawn/GrDawnProgramDataManager.h b/src/gpu/dawn/GrDawnProgramDataManager.h
index 0b2f35e..d74f1ee 100644
--- a/src/gpu/dawn/GrDawnProgramDataManager.h
+++ b/src/gpu/dawn/GrDawnProgramDataManager.h
@@ -8,6 +8,7 @@
 #ifndef GrDawnProgramDataManager_DEFINED
 #define GrDawnProgramDataManager_DEFINED
 
+#include "src/gpu/dawn/GrDawnRingBuffer.h"
 #include "src/gpu/dawn/GrDawnUniformHandler.h"
 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
 #include "dawn/dawncpp.h"
@@ -57,8 +58,11 @@
         SK_ABORT("Only supported in NVPR, which is not in Dawn");
     }
 
-    void uploadUniformBuffers(dawn::Buffer geometryBuffer, dawn::Buffer fragmentBuffer) const;
+    void uploadUniformBuffers(GrDawnRingBuffer::Slice geometryBuffer,
+                              GrDawnRingBuffer::Slice fragmentBuffer) const;
 
+    uint32_t geometryUniformSize() const { return fGeometryUniformSize; }
+    uint32_t fragmentUniformSize() const { return fFragmentUniformSize; }
 private:
     struct Uniform {
         uint32_t fBinding;
diff --git a/src/gpu/dawn/GrDawnRingBuffer.cpp b/src/gpu/dawn/GrDawnRingBuffer.cpp
new file mode 100644
index 0000000..0e26369
--- /dev/null
+++ b/src/gpu/dawn/GrDawnRingBuffer.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/dawn/GrDawnRingBuffer.h"
+
+#include "src/gpu/dawn/GrDawnGpu.h"
+#include "src/gpu/dawn/GrDawnUtil.h"
+
+namespace {
+    const int kDefaultSize = 512 * 1024;
+}
+
+GrDawnRingBuffer::GrDawnRingBuffer(GrDawnGpu* gpu, dawn::BufferUsageBit usage)
+    : fGpu(gpu) , fUsage(usage) {
+}
+
+GrDawnRingBuffer::~GrDawnRingBuffer() {
+}
+
+GrDawnRingBuffer::Slice GrDawnRingBuffer::allocate(int size) {
+    if (!fBuffer || fOffset + size > kDefaultSize) {
+        dawn::BufferDescriptor desc;
+        desc.usage = fUsage | dawn::BufferUsageBit::CopyDst;
+        desc.size = kDefaultSize;
+        fBuffer = fGpu->device().CreateBuffer(&desc);
+        fOffset = 0;
+    }
+    int offset = fOffset;
+    fOffset += size;
+    fOffset = GrDawnRoundRowBytes(fOffset);
+    return Slice(fBuffer, offset);
+}
diff --git a/src/gpu/dawn/GrDawnRingBuffer.h b/src/gpu/dawn/GrDawnRingBuffer.h
new file mode 100644
index 0000000..48e2576
--- /dev/null
+++ b/src/gpu/dawn/GrDawnRingBuffer.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrDawnRingBuffer_DEFINED
+#define GrDawnRingBuffer_DEFINED
+
+#include "src/gpu/GrBuffer.h"
+#include "src/gpu/dawn/GrDawnBuffer.h"
+#include "dawn/dawncpp.h"
+
+class GrDawnGpu;
+
+class GrDawnRingBuffer : public SkRefCnt {
+public:
+    GrDawnRingBuffer(GrDawnGpu* gpu, dawn::BufferUsageBit usage);
+    ~GrDawnRingBuffer() override;
+
+    struct Slice {
+        Slice(dawn::Buffer buffer, int offset) : fBuffer(buffer), fOffset(offset) {}
+        Slice() : fBuffer(nullptr), fOffset(0) {}
+        Slice(const Slice& other) : fBuffer(other.fBuffer), fOffset(other.fOffset) {}
+        Slice& operator=(const Slice& other) {
+            fBuffer = other.fBuffer;
+            fOffset = other.fOffset;
+            return *this;
+        }
+        dawn::Buffer fBuffer;
+        int fOffset;
+    };
+    Slice allocate(int size);
+
+private:
+    GrDawnGpu*            fGpu;
+    dawn::BufferUsageBit  fUsage;
+    dawn::Buffer          fBuffer;
+    int                   fOffset = 0;
+};
+
+#endif