Use dynamic state from vulkan pipelines

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1813913002

Review URL: https://codereview.chromium.org/1813913002
diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp
index d26d06d..c05e955 100644
--- a/src/gpu/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkCommandBuffer.cpp
@@ -9,6 +9,7 @@
 
 #include "GrVkFramebuffer.h"
 #include "GrVkImageView.h"
+#include "GrVkPipeline.h"
 #include "GrVkRenderPass.h"
 #include "GrVkRenderTarget.h"
 #include "GrVkProgram.h"
@@ -40,10 +41,20 @@
 }
 
 void GrVkCommandBuffer::invalidateState() {
-    fBoundVertexBuffer = 0;
+    fBoundVertexBuffer = VK_NULL_HANDLE;
     fBoundVertexBufferIsValid = false;
-    fBoundIndexBuffer = 0;
+    fBoundIndexBuffer = VK_NULL_HANDLE;
     fBoundIndexBufferIsValid = false;
+
+    memset(&fCachedViewport, 0, sizeof(VkViewport));
+    fCachedViewport.width = - 1.0f; // Viewport must have a width greater than 0
+
+    memset(&fCachedScissor, 0, sizeof(VkRect2D));
+    fCachedScissor.offset.x = -1; // Scissor offset must be greater that 0 to be valid
+
+    for (int i = 0; i < 4; ++i) {
+        fCachedBlendConstant[i] = -1.0;
+    }
 }
 
 void GrVkCommandBuffer::freeGPUData(const GrVkGpu* gpu) const {
@@ -355,6 +366,15 @@
     program->addUniformResources(*this);
 }
 
+void GrVkCommandBuffer::bindPipeline(const GrVkGpu* gpu, const GrVkPipeline* pipeline) {
+    SkASSERT(fIsActive);
+    SkASSERT(fActiveRenderPass);
+    GR_VK_CALL(gpu->vkInterface(), CmdBindPipeline(fCmdBuffer,
+                                                   VK_PIPELINE_BIND_POINT_GRAPHICS,
+                                                   pipeline->pipeline()));
+    addResource(pipeline);
+}
+
 void GrVkCommandBuffer::drawIndexed(const GrVkGpu* gpu,
                                     uint32_t indexCount,
                                     uint32_t instanceCount,
@@ -384,3 +404,42 @@
                                            firstVertex,
                                            firstInstance));
 }
+
+void GrVkCommandBuffer::setViewport(const GrVkGpu* gpu,
+                                    uint32_t firstViewport,
+                                    uint32_t viewportCount,
+                                    const VkViewport* viewports) {
+    SkASSERT(fIsActive);
+    SkASSERT(1 == viewportCount);
+    if (memcmp(viewports, &fCachedViewport, sizeof(VkViewport))) {
+        GR_VK_CALL(gpu->vkInterface(), CmdSetViewport(fCmdBuffer,
+                                                      firstViewport,
+                                                      viewportCount,
+                                                      viewports));
+        fCachedViewport = viewports[0];
+    }
+}
+
+void GrVkCommandBuffer::setScissor(const GrVkGpu* gpu,
+                                   uint32_t firstScissor,
+                                   uint32_t scissorCount,
+                                   const VkRect2D* scissors) {
+    SkASSERT(fIsActive);
+    SkASSERT(1 == scissorCount);
+    if (memcmp(scissors, &fCachedScissor, sizeof(VkRect2D))) {
+        GR_VK_CALL(gpu->vkInterface(), CmdSetScissor(fCmdBuffer,
+                                                     firstScissor,
+                                                     scissorCount,
+                                                     scissors));
+        fCachedScissor = scissors[0];
+    }
+}
+
+void GrVkCommandBuffer::setBlendConstants(const GrVkGpu* gpu,
+                                          const float blendConstants[4]) {
+    SkASSERT(fIsActive);
+    if (memcmp(blendConstants, fCachedBlendConstant, 4 * sizeof(float))) {
+        GR_VK_CALL(gpu->vkInterface(), CmdSetBlendConstants(fCmdBuffer, blendConstants));
+        memcpy(fCachedBlendConstant, blendConstants, 4 * sizeof(float));
+    }
+}
diff --git a/src/gpu/vk/GrVkCommandBuffer.h b/src/gpu/vk/GrVkCommandBuffer.h
index 5cdb2c0..f63a191 100644
--- a/src/gpu/vk/GrVkCommandBuffer.h
+++ b/src/gpu/vk/GrVkCommandBuffer.h
@@ -9,11 +9,11 @@
 #define GrVkCommandBuffer_DEFINED
 
 #include "GrVkGpu.h"
-#include "GrVkPipeline.h"
 #include "GrVkResource.h"
 #include "GrVkUtil.h"
 #include "vulkan/vulkan.h"
 
+class GrVkPipeline;
 class GrVkRenderPass;
 class GrVkRenderTarget;
 class GrVkTransferBuffer;
@@ -39,15 +39,6 @@
     bool finished(const GrVkGpu* gpu) const;
 
     ////////////////////////////////////////////////////////////////////////////
-    // CommandBuffer State/Object bindings
-    ////////////////////////////////////////////////////////////////////////////
-#if 0
-    void bindPipeline(const GrVkGpu* gpu) const;
-    void bindDynamicState(const GrVkGpu* gpu) const;
-    void bindDescriptorSet(const GrVkGpu* gpu) const;
-#endif
-
-    ////////////////////////////////////////////////////////////////////////////
     // CommandBuffer commands
     ////////////////////////////////////////////////////////////////////////////
     enum BarrierType {
@@ -91,13 +82,6 @@
         }
     }
 
-    void bindPipeline(const GrVkGpu* gpu, const GrVkPipeline* pipeline) {
-        GR_VK_CALL(gpu->vkInterface(), CmdBindPipeline(fCmdBuffer,
-                                                       VK_PIPELINE_BIND_POINT_GRAPHICS,
-                                                       pipeline->pipeline()));
-        addResource(pipeline);
-    }
-
     void bindDescriptorSets(const GrVkGpu* gpu,
                             GrVkProgram*,
                             VkPipelineLayout layout,
@@ -107,6 +91,18 @@
                             uint32_t dynamicOffsetCount,
                             const uint32_t* dynamicOffsets);
 
+    void setViewport(const GrVkGpu* gpu,
+                     uint32_t firstViewport,
+                     uint32_t viewportCount,
+                     const VkViewport* viewports);
+
+    void setScissor(const GrVkGpu* gpu,
+                    uint32_t firstScissor,
+                    uint32_t scissorCount,
+                    const VkRect2D* scissors);
+
+    void setBlendConstants(const GrVkGpu* gpu, const float blendConstants[4]);
+
     // Commands that only work outside of a render pass
     void clearColorImage(const GrVkGpu* gpu,
                          GrVkImage* image,
@@ -149,6 +145,9 @@
                           int numRects,
                           const VkClearRect* clearRects) const;
 
+    void bindPipeline(const GrVkGpu* gpu, const GrVkPipeline* pipeline);
+
+
     void drawIndexed(const GrVkGpu* gpu,
                      uint32_t indexCount,
                      uint32_t instanceCount,
@@ -205,6 +204,11 @@
     // A nullptr means there is no active render pass. The GrVKCommandBuffer does not own the render
     // pass.
     const GrVkRenderPass*     fActiveRenderPass;
+
+    // Cached values used for dynamic state updates
+    VkViewport fCachedViewport;
+    VkRect2D   fCachedScissor;
+    float      fCachedBlendConstant[4];
 };
 
 
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index ee87f49..96c74dd 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1533,6 +1533,9 @@
     (*program)->setData(this, primProc, pipeline);
 
     (*program)->bind(this, fCurrentCmdBuffer);
+
+    GrVkPipeline::SetDynamicState(this, fCurrentCmdBuffer, pipeline);
+
     return true;
 }
 
@@ -1548,6 +1551,8 @@
     const GrVkRenderPass* renderPass = vkRT->simpleRenderPass();
     SkASSERT(renderPass);
 
+    fCurrentCmdBuffer->beginRenderPass(this, renderPass, *vkRT);
+
     GrVkProgram* program = nullptr;
     GrPrimitiveType primitiveType = meshes[0].primitiveType();
     if (!this->prepareDrawState(pipeline, primProc, primitiveType, *renderPass, &program)) {
@@ -1590,7 +1595,6 @@
                                   false);
     }
 
-    fCurrentCmdBuffer->beginRenderPass(this, renderPass, *vkRT);
 
     for (int i = 0; i < meshCount; ++i) {
         if (GrXferBarrierType barrierType = pipeline.xferBarrierType(*this->caps())) {
diff --git a/src/gpu/vk/GrVkPipeline.cpp b/src/gpu/vk/GrVkPipeline.cpp
index c0197cd..7571a40 100644
--- a/src/gpu/vk/GrVkPipeline.cpp
+++ b/src/gpu/vk/GrVkPipeline.cpp
@@ -9,7 +9,7 @@
 
 #include "GrGeometryProcessor.h"
 #include "GrPipeline.h"
-
+#include "GrVkCommandBuffer.h"
 #include "GrVkGpu.h"
 #include "GrVkProgramDesc.h"
 #include "GrVkRenderTarget.h"
@@ -188,52 +188,18 @@
 void setup_viewport_scissor_state(const GrVkGpu* gpu,
                                   const GrPipeline& pipeline,
                                   const GrVkRenderTarget* vkRT,
-                                  VkPipelineViewportStateCreateInfo* viewportInfo,
-                                  VkViewport* viewport,
-                                  VkRect2D* scissor) {
+                                  VkPipelineViewportStateCreateInfo* viewportInfo) {
     memset(viewportInfo, 0, sizeof(VkPipelineViewportStateCreateInfo));
     viewportInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
     viewportInfo->pNext = nullptr;
     viewportInfo->flags = 0;
 
-    viewport->x = 0.0f;
-    viewport->y = 0.0f;
-    viewport->width = SkIntToScalar(vkRT->width());
-    viewport->height = SkIntToScalar(vkRT->height());
-    viewport->minDepth = 0.0f;
-    viewport->maxDepth = 1.0f;
     viewportInfo->viewportCount = 1;
-    viewportInfo->pViewports = viewport;
+    viewportInfo->pViewports = nullptr; // This is set dynamically
 
-    const GrScissorState& scissorState = pipeline.getScissorState();
-    if (scissorState.enabled() && 
-        !scissorState.rect().contains(0, 0, vkRT->width(), vkRT->height())) {
-        // This all assumes the scissorState has previously been clipped to the device space render
-        // target. 
-        scissor->offset.x = scissorState.rect().fLeft;
-        scissor->extent.width = scissorState.rect().width();
-        if (kTopLeft_GrSurfaceOrigin == vkRT->origin()) {
-            scissor->offset.y = scissorState.rect().fTop;
-        } else {
-            SkASSERT(kBottomLeft_GrSurfaceOrigin == vkRT->origin());
-            scissor->offset.y = vkRT->height() - scissorState.rect().fBottom;
-        }
-        scissor->extent.height = scissorState.rect().height();
+    viewportInfo->scissorCount = 1;
+    viewportInfo->pScissors = nullptr; // This is set dynamically
 
-        viewportInfo->scissorCount = 1;
-        viewportInfo->pScissors = scissor;
-        SkASSERT(scissor->offset.x >= 0);
-        SkASSERT(scissor->offset.x + scissor->extent.width <= (uint32_t)vkRT->width());
-        SkASSERT(scissor->offset.y >= 0);
-        SkASSERT(scissor->offset.y + scissor->extent.height <= (uint32_t)vkRT->height());
-    } else {
-        scissor->extent.width = vkRT->width();
-        scissor->extent.height = vkRT->height();
-        scissor->offset.x = 0;
-        scissor->offset.y = 0;
-        viewportInfo->scissorCount = 1;
-        viewportInfo->pScissors = scissor;
-    }
     SkASSERT(viewportInfo->viewportCount == viewportInfo->scissorCount);
 }
 
@@ -381,9 +347,7 @@
     colorBlendInfo->logicOpEnable = VK_FALSE;
     colorBlendInfo->attachmentCount = 1;
     colorBlendInfo->pAttachments = attachmentState;
-    if (blend_coeff_refs_constant(srcCoeff) || blend_coeff_refs_constant(dstCoeff)) {
-        GrColorToRGBAFloat(blendInfo.fBlendConstant, colorBlendInfo->blendConstants);
-    }
+    // colorBlendInfo->blendConstants is set dynamically
 }
 
 VkCullModeFlags draw_face_to_vk_cull_mode(GrPipelineBuilder::DrawFace drawFace) {
@@ -422,11 +386,17 @@
 
 void setup_dynamic_state(const GrVkGpu* gpu,
                          const GrPipeline& pipeline,
-                         VkPipelineDynamicStateCreateInfo* dynamicInfo) {
+                         VkPipelineDynamicStateCreateInfo* dynamicInfo,
+                         VkDynamicState* dynamicStates) {
     memset(dynamicInfo, 0, sizeof(VkPipelineDynamicStateCreateInfo));
     dynamicInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
-    // TODO: mask out any state we might want to set dynamically
-    dynamicInfo->dynamicStateCount = 0;
+    dynamicInfo->pNext = VK_NULL_HANDLE;
+    dynamicInfo->flags = 0;
+    dynamicStates[0] = VK_DYNAMIC_STATE_VIEWPORT;
+    dynamicStates[1] = VK_DYNAMIC_STATE_SCISSOR;
+    dynamicStates[2] = VK_DYNAMIC_STATE_BLEND_CONSTANTS;
+    dynamicInfo->dynamicStateCount = 3;
+    dynamicInfo->pDynamicStates = dynamicStates;
 }
 
 GrVkPipeline* GrVkPipeline::Create(GrVkGpu* gpu, const GrPipeline& pipeline,
@@ -454,9 +424,7 @@
     GrRenderTarget* rt = pipeline.getRenderTarget();
     GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(rt);
     VkPipelineViewportStateCreateInfo viewportInfo;
-    VkViewport viewport;
-    VkRect2D scissor;
-    setup_viewport_scissor_state(gpu, pipeline, vkRT, &viewportInfo, &viewport, &scissor);
+    setup_viewport_scissor_state(gpu, pipeline, vkRT, &viewportInfo);
 
     VkPipelineMultisampleStateCreateInfo multisampleInfo;
     setup_multisample_state(pipeline, &multisampleInfo);
@@ -469,8 +437,9 @@
     VkPipelineRasterizationStateCreateInfo rasterInfo;
     setup_raster_state(gpu, pipeline, &rasterInfo);
 
+    VkDynamicState dynamicStates[3];
     VkPipelineDynamicStateCreateInfo dynamicInfo;
-    setup_dynamic_state(gpu, pipeline, &dynamicInfo);
+    setup_dynamic_state(gpu, pipeline, &dynamicInfo, dynamicStates);
 
     VkGraphicsPipelineCreateInfo pipelineCreateInfo;
     memset(&pipelineCreateInfo, 0, sizeof(VkGraphicsPipelineCreateInfo));
@@ -511,3 +480,77 @@
 }
 
 
+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 = scissorState.rect().fLeft;
+        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.extent.height = scissorState.rect().height();
+
+        SkASSERT(scissor.offset.x >= 0);
+        SkASSERT(scissor.offset.x + scissor.extent.width <= (uint32_t)target.width());
+        SkASSERT(scissor.offset.y >= 0);
+        SkASSERT(scissor.offset.y + scissor.extent.height <= (uint32_t)target.height());
+    } else {
+        scissor.extent.width = target.width();
+        scissor.extent.height = target.height();
+        scissor.offset.x = 0;
+        scissor.offset.y = 0;
+    }
+    cmdBuffer->setScissor(gpu, 0, 1, &scissor);
+}
+
+void set_dynamic_viewport_state(GrVkGpu* gpu,
+                                GrVkCommandBuffer* cmdBuffer,
+                                const GrRenderTarget& target) {
+    // 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.minDepth = 0.0f;
+    viewport.maxDepth = 1.0f;
+    cmdBuffer->setViewport(gpu, 0, 1, &viewport);
+}
+
+void set_dynamic_blend_constant_state(GrVkGpu* gpu,
+                                     GrVkCommandBuffer* cmdBuffer,
+                                     const GrPipeline& pipeline) {
+    GrXferProcessor::BlendInfo blendInfo;
+    pipeline.getXferProcessor().getBlendInfo(&blendInfo);
+    GrBlendCoeff srcCoeff = blendInfo.fSrcBlend;
+    GrBlendCoeff dstCoeff = blendInfo.fDstBlend;
+    float floatColors[4];
+    if (blend_coeff_refs_constant(srcCoeff) || blend_coeff_refs_constant(dstCoeff)) {
+        GrColorToRGBAFloat(blendInfo.fBlendConstant, floatColors);
+    } else {
+        memset(floatColors, 0, 4 * sizeof(float));
+    }
+    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 e1c946a..46f53a7 100644
--- a/src/gpu/vk/GrVkPipeline.h
+++ b/src/gpu/vk/GrVkPipeline.h
@@ -17,6 +17,7 @@
 class GrNonInstancedVertices;
 class GrPipeline;
 class GrPrimitiveProcessor;
+class GrVkCommandBuffer;
 class GrVkGpu;
 class GrVkRenderPass;
 
@@ -34,6 +35,9 @@
 
     VkPipeline pipeline() const { return fPipeline; }
 
+    static void SetDynamicState(GrVkGpu*, GrVkCommandBuffer*, const GrPipeline&);
+
+
 private:
     GrVkPipeline(VkPipeline pipeline) : INHERITED(), fPipeline(pipeline) {}