Introduce dynamic pipeline state

Adds a DynamicState struct to GrPipeline that has a field for the
scissor rect. Eventually this should become the only way to specify a
scissor rectangle and may grow to contain more fields. Adds an array of
DynamicStates to GrGpuCommandBuffer::draw and implements support in GL
and Vulkan.

Bug: skia:
Change-Id: If5aebbf9da5d192acf7e68e7def4674ffc7ec310
Reviewed-on: https://skia-review.googlesource.com/18510
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index 6f39cfa..6e9c292 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -468,7 +468,8 @@
 sk_sp<GrVkPipelineState> GrVkGpuCommandBuffer::prepareDrawState(
                                                                const GrPipeline& pipeline,
                                                                const GrPrimitiveProcessor& primProc,
-                                                               GrPrimitiveType primitiveType) {
+                                                               GrPrimitiveType primitiveType,
+                                                               bool hasDynamicState) {
     CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
     SkASSERT(cbInfo.fRenderPass);
 
@@ -492,7 +493,18 @@
 
     pipelineState->bind(fGpu, cbInfo.currentCmdBuf());
 
-    GrVkPipeline::SetDynamicState(fGpu, cbInfo.currentCmdBuf(), pipeline);
+    GrRenderTarget* rt = pipeline.getRenderTarget();
+
+    if (!pipeline.getScissorState().enabled()) {
+        GrVkPipeline::SetDynamicScissorRectState(fGpu, cbInfo.currentCmdBuf(), rt,
+                                                 SkIRect::MakeWH(rt->width(), rt->height()));
+    } else if (!hasDynamicState) {
+        GrVkPipeline::SetDynamicScissorRectState(fGpu, cbInfo.currentCmdBuf(), rt,
+                                                 pipeline.getScissorState().rect());
+    }
+    GrVkPipeline::SetDynamicViewportState(fGpu, cbInfo.currentCmdBuf(), rt);
+    GrVkPipeline::SetDynamicBlendConstantState(fGpu, cbInfo.currentCmdBuf(), rt->config(),
+                                               pipeline.getXferProcessor());
 
     return pipelineState;
 }
@@ -533,7 +545,8 @@
 
 void GrVkGpuCommandBuffer::onDraw(const GrPipeline& pipeline,
                                   const GrPrimitiveProcessor& primProc,
-                                  const GrMesh* meshes,
+                                  const GrMesh meshes[],
+                                  const GrPipeline::DynamicState dynamicStates[],
                                   int meshCount,
                                   const SkRect& bounds) {
     GrVkRenderTarget* target = static_cast<GrVkRenderTarget*>(pipeline.getRenderTarget());
@@ -557,11 +570,14 @@
     GrPrimitiveType primitiveType = meshes[0].primitiveType();
     sk_sp<GrVkPipelineState> pipelineState = this->prepareDrawState(pipeline,
                                                                     primProc,
-                                                                    primitiveType);
+                                                                    primitiveType,
+                                                                    SkToBool(dynamicStates));
     if (!pipelineState) {
         return;
     }
 
+    CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
+
     for (int i = 0; i < meshCount; ++i) {
         const GrMesh& mesh = meshes[i];
         if (mesh.primitiveType() != primitiveType) {
@@ -573,17 +589,24 @@
             primitiveType = mesh.primitiveType();
             pipelineState = this->prepareDrawState(pipeline,
                                                    primProc,
-                                                   primitiveType);
+                                                   primitiveType,
+                                                   SkToBool(dynamicStates));
             if (!pipelineState) {
                 return;
             }
         }
 
+        if (dynamicStates) {
+            if (pipeline.getScissorState().enabled()) {
+                GrVkPipeline::SetDynamicScissorRectState(fGpu, cbInfo.currentCmdBuf(),
+                                                         target, dynamicStates[i].fScissorRect);
+            }
+        }
+
         SkASSERT(pipelineState);
         mesh.sendToGpu(primProc, this);
     }
 
-    CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
     cbInfo.fBounds.join(bounds);
     cbInfo.fIsEmpty = false;
 
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.h b/src/gpu/vk/GrVkGpuCommandBuffer.h
index 6836fac..9ef166e 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.h
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.h
@@ -53,11 +53,13 @@
 
     sk_sp<GrVkPipelineState> prepareDrawState(const GrPipeline&,
                                               const GrPrimitiveProcessor&,
-                                              GrPrimitiveType);
+                                              GrPrimitiveType,
+                                              bool hasDynamicState);
 
     void onDraw(const GrPipeline& pipeline,
                 const GrPrimitiveProcessor& primProc,
-                const GrMesh* mesh,
+                const GrMesh mesh[],
+                const GrPipeline::DynamicState[],
                 int meshCount,
                 const SkRect& bounds) override;
 
diff --git a/src/gpu/vk/GrVkPipeline.cpp b/src/gpu/vk/GrVkPipeline.cpp
index 2732c6f..0b4289e 100644
--- a/src/gpu/vk/GrVkPipeline.cpp
+++ b/src/gpu/vk/GrVkPipeline.cpp
@@ -481,65 +481,56 @@
     GR_VK_CALL(gpu->vkInterface(), DestroyPipeline(gpu->device(), fPipeline, nullptr));
 }
 
-static void set_dynamic_scissor_state(GrVkGpu* gpu,
-                                      GrVkCommandBuffer* cmdBuffer,
-                                      const GrPipeline& pipeline,
-                                      const GrRenderTarget& target) {
-    // We always use one scissor and if it is disabled we just make it the size of the RT
-    const GrScissorState& scissorState = pipeline.getScissorState();
-    VkRect2D scissor;
-    if (scissorState.enabled() &&
-        !scissorState.rect().contains(0, 0, target.width(), target.height())) {
-        // This all assumes the scissorState has previously been clipped to the device space render
-        // target.
-        scissor.offset.x = SkTMax(scissorState.rect().fLeft, 0);
-        scissor.extent.width = scissorState.rect().width();
-        if (kTopLeft_GrSurfaceOrigin == target.origin()) {
-            scissor.offset.y = scissorState.rect().fTop;
-        } else {
-            SkASSERT(kBottomLeft_GrSurfaceOrigin == target.origin());
-            scissor.offset.y = target.height() - scissorState.rect().fBottom;
-        }
-        scissor.offset.y = SkTMax(scissor.offset.y, 0);
-        scissor.extent.height = scissorState.rect().height();
-
-        SkASSERT(scissor.offset.x >= 0);
-        SkASSERT(scissor.offset.y >= 0);
-    } else {
-        scissor.extent.width = target.width();
-        scissor.extent.height = target.height();
-        scissor.offset.x = 0;
-        scissor.offset.y = 0;
+void GrVkPipeline::SetDynamicScissorRectState(GrVkGpu* gpu,
+                                              GrVkCommandBuffer* cmdBuffer,
+                                              const GrRenderTarget* renderTarget,
+                                              SkIRect scissorRect) {
+    if (!scissorRect.intersect(SkIRect::MakeWH(renderTarget->width(), renderTarget->height()))) {
+        scissorRect.setEmpty();
     }
+
+    VkRect2D scissor;
+    scissor.offset.x = scissorRect.fLeft;
+    scissor.extent.width = scissorRect.width();
+    if (kTopLeft_GrSurfaceOrigin == renderTarget->origin()) {
+        scissor.offset.y = scissorRect.fTop;
+    } else {
+        SkASSERT(kBottomLeft_GrSurfaceOrigin == renderTarget->origin());
+        scissor.offset.y = renderTarget->height() - scissorRect.fBottom;
+    }
+    scissor.extent.height = scissorRect.height();
+
+    SkASSERT(scissor.offset.x >= 0);
+    SkASSERT(scissor.offset.y >= 0);
     cmdBuffer->setScissor(gpu, 0, 1, &scissor);
 }
 
-static void set_dynamic_viewport_state(GrVkGpu* gpu,
-                                       GrVkCommandBuffer* cmdBuffer,
-                                       const GrRenderTarget& target) {
+void GrVkPipeline::SetDynamicViewportState(GrVkGpu* gpu,
+                                           GrVkCommandBuffer* cmdBuffer,
+                                           const GrRenderTarget* renderTarget) {
     // We always use one viewport the size of the RT
     VkViewport viewport;
     viewport.x = 0.0f;
     viewport.y = 0.0f;
-    viewport.width = SkIntToScalar(target.width());
-    viewport.height = SkIntToScalar(target.height());
+    viewport.width = SkIntToScalar(renderTarget->width());
+    viewport.height = SkIntToScalar(renderTarget->height());
     viewport.minDepth = 0.0f;
     viewport.maxDepth = 1.0f;
     cmdBuffer->setViewport(gpu, 0, 1, &viewport);
 }
 
-static void set_dynamic_blend_constant_state(GrVkGpu* gpu,
-                                             GrVkCommandBuffer* cmdBuffer,
-                                             const GrPipeline& pipeline) {
+void GrVkPipeline::SetDynamicBlendConstantState(GrVkGpu* gpu,
+                                                GrVkCommandBuffer* cmdBuffer,
+                                                GrPixelConfig pixelConfig,
+                                                const GrXferProcessor& xferProcessor) {
     GrXferProcessor::BlendInfo blendInfo;
-    pipeline.getXferProcessor().getBlendInfo(&blendInfo);
+    xferProcessor.getBlendInfo(&blendInfo);
     GrBlendCoeff srcCoeff = blendInfo.fSrcBlend;
     GrBlendCoeff dstCoeff = blendInfo.fDstBlend;
     float floatColors[4];
     if (blend_coeff_refs_constant(srcCoeff) || blend_coeff_refs_constant(dstCoeff)) {
         // Swizzle the blend to match what the shader will output.
-        const GrSwizzle& swizzle = gpu->caps()->shaderCaps()->configOutputSwizzle(
-                pipeline.getRenderTarget()->config());
+        const GrSwizzle& swizzle = gpu->caps()->shaderCaps()->configOutputSwizzle(pixelConfig);
         GrColor blendConst = swizzle.applyTo(blendInfo.fBlendConstant);
         GrColorToRGBAFloat(blendConst, floatColors);
     } else {
@@ -547,12 +538,3 @@
     }
     cmdBuffer->setBlendConstants(gpu, floatColors);
 }
-
-void GrVkPipeline::SetDynamicState(GrVkGpu* gpu,
-                                   GrVkCommandBuffer* cmdBuffer,
-                                   const GrPipeline& pipeline) {
-    const GrRenderTarget& target = *pipeline.getRenderTarget();
-    set_dynamic_scissor_state(gpu, cmdBuffer, pipeline, target);
-    set_dynamic_viewport_state(gpu, cmdBuffer, target);
-    set_dynamic_blend_constant_state(gpu, cmdBuffer, pipeline);
-}
diff --git a/src/gpu/vk/GrVkPipeline.h b/src/gpu/vk/GrVkPipeline.h
index 585014e..d05974b 100644
--- a/src/gpu/vk/GrVkPipeline.h
+++ b/src/gpu/vk/GrVkPipeline.h
@@ -16,10 +16,13 @@
 
 class GrPipeline;
 class GrPrimitiveProcessor;
+class GrRenderTarget;
+class GrXferProcessor;
 class GrStencilSettings;
 class GrVkCommandBuffer;
 class GrVkGpu;
 class GrVkRenderPass;
+struct SkIRect;
 
 class GrVkPipeline : public GrVkResource {
 public:
@@ -36,7 +39,11 @@
 
     VkPipeline pipeline() const { return fPipeline; }
 
-    static void SetDynamicState(GrVkGpu*, GrVkCommandBuffer*, const GrPipeline&);
+    static void SetDynamicScissorRectState(GrVkGpu*, GrVkCommandBuffer*, const GrRenderTarget*,
+                                           SkIRect);
+    static void SetDynamicViewportState(GrVkGpu*, GrVkCommandBuffer*, const GrRenderTarget*);
+    static void SetDynamicBlendConstantState(GrVkGpu*, GrVkCommandBuffer*, GrPixelConfig,
+                                             const GrXferProcessor&);
 
 #ifdef SK_TRACE_VK_RESOURCES
     void dumpInfo() const override {