Add support for dynamic state GP textures in Vulkan backend.

Separate uniform bindings from texture bindings in VK.

Combine descriptor set generation and pipeline binding for
uniforms, textures, views, and samplers in GrVkPipelineState.

Remove persistent tracking of textures, views, and samplers from
GrVkPipelineState.

Make GrTextureOp always use indexed draws since changing primitive
type in Vk isn't cheap.

Disable dynamic GP textures in VK/NVIDIA because of suspected driver bug.

Change-Id: I1108ce49b2c3a63c127fd4ad1d1c9440a99f4b22
Reviewed-on: https://skia-review.googlesource.com/146448
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index fa86c2f..a8dd784 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -711,19 +711,14 @@
 
             (op.*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, gp.get());
 
-            if (op.fDraws.count() > 1) {
-                meshes[i].setPrimitiveType(GrPrimitiveType::kTriangles);
-                sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
-                if (!ibuffer) {
-                    SkDebugf("Could not allocate quad indices\n");
-                    return;
-                }
-                meshes[i].setIndexedPatterned(ibuffer.get(), 6, 4, op.fDraws.count(),
-                                              GrResourceProvider::QuadCountOfQuadBuffer());
-            } else {
-                meshes[i].setPrimitiveType(GrPrimitiveType::kTriangleStrip);
-                meshes[i].setNonIndexedNonInstanced(4);
+            meshes[i].setPrimitiveType(GrPrimitiveType::kTriangles);
+            sk_sp<const GrBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
+            if (!ibuffer) {
+                SkDebugf("Could not allocate quad indices\n");
+                return;
             }
+            meshes[i].setIndexedPatterned(ibuffer.get(), 6, 4, op.fDraws.count(),
+                                          GrResourceProvider::QuadCountOfQuadBuffer());
             meshes[i].setVertexData(vbuffer, vstart);
             if (dynamicStateArrays) {
                 dynamicStateArrays->fPrimitiveProcessorTextures[i] = op.fProxy;
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 95b5712..9c501d9 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -32,8 +32,8 @@
     fSupportsMaintenance3 = false;
 
     /**************************************************************************
-    * GrDrawTargetCaps fields
-    **************************************************************************/
+     * GrCaps fields
+     **************************************************************************/
     fMipMapSupport = true;   // always available in Vulkan
     fSRGBSupport = true;   // always available in Vulkan
     fNPOTTextureTileSupport = true;  // always available in Vulkan
@@ -53,6 +53,8 @@
     fMaxRenderTargetSize = 4096; // minimum required by spec
     fMaxTextureSize = 4096; // minimum required by spec
 
+    fDynamicStateArrayGeometryProcessorTextureSupport = true;
+
     fShaderCaps.reset(new GrShaderCaps(contextOptions));
 
     this->init(contextOptions, vkInterface, physDev, features, extensions);
@@ -311,6 +313,11 @@
         fMaxVertexAttributes = SkTMin(fMaxVertexAttributes, 32);
     }
 
+    // The image-surface GM does not draw correctly on all tested NVIDIA systems.
+    if (kNvidia_VkVendor == properties.vendorID) {
+        fDynamicStateArrayGeometryProcessorTextureSupport = false;
+    }
+
     ////////////////////////////////////////////////////////////////////////////
     // GrShaderCaps workarounds
     ////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp
index caff3ca..7b4a92d 100644
--- a/src/gpu/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkCommandBuffer.cpp
@@ -243,7 +243,6 @@
                                                          dynamicOffsetCount,
                                                          dynamicOffsets));
     this->addRecordingResource(layout);
-    pipelineState->addUniformResources(*this);
 }
 
 void GrVkCommandBuffer::bindDescriptorSets(const GrVkGpu* gpu,
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index 44cf980..3ecb340 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -605,13 +605,20 @@
     }
     fLastPipelineState = pipelineState;
 
-    const GrTextureProxy* const* primProcProxies = nullptr;
-    if (fixedDynamicState) {
-        primProcProxies = fixedDynamicState->fPrimitiveProcessorTextures;
-    }
-    pipelineState->setData(fGpu, primProc, pipeline, primProcProxies);
+    pipelineState->bindPipeline(fGpu, cbInfo.currentCmdBuf());
 
-    pipelineState->bind(fGpu, cbInfo.currentCmdBuf());
+    pipelineState->setAndBindUniforms(fGpu, primProc, pipeline, cbInfo.currentCmdBuf());
+
+    // Check whether we need to bind textures between each GrMesh. If not we can bind them all now.
+    bool setTextures = !(dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures);
+    if (setTextures) {
+        const GrTextureProxy* const* primProcProxies = nullptr;
+        if (fixedDynamicState) {
+            primProcProxies = fixedDynamicState->fPrimitiveProcessorTextures;
+        }
+        pipelineState->setAndBindTextures(fGpu, primProc, pipeline, primProcProxies,
+                                          cbInfo.currentCmdBuf());
+    }
 
     GrRenderTarget* rt = pipeline.renderTarget();
 
@@ -666,9 +673,18 @@
         cbInfo.fSampledImages.push_back(vkTexture);
     };
 
-    for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
-        auto texture = fixedDynamicState->fPrimitiveProcessorTextures[i]->peekTexture();
-        prepareSampledImage(texture, primProc.textureSampler(i).samplerState().filter());
+    if (dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures) {
+        for (int m = 0, i = 0; m < meshCount; ++m) {
+            for (int s = 0; s < primProc.numTextureSamplers(); ++s, ++i) {
+                auto texture = dynamicStateArrays->fPrimitiveProcessorTextures[i]->peekTexture();
+                prepareSampledImage(texture, primProc.textureSampler(s).samplerState().filter());
+            }
+        }
+    } else {
+        for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
+            auto texture = fixedDynamicState->fPrimitiveProcessorTextures[i]->peekTexture();
+            prepareSampledImage(texture, primProc.textureSampler(i).samplerState().filter());
+        }
     }
     GrFragmentProcessor::Iter iter(pipeline);
     while (const GrFragmentProcessor* fp = iter.next()) {
@@ -690,14 +706,11 @@
 
     bool dynamicScissor =
             pipeline.isScissorEnabled() && dynamicStateArrays && dynamicStateArrays->fScissorRects;
+    bool dynamicTextures = dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures;
 
     for (int i = 0; i < meshCount; ++i) {
         const GrMesh& mesh = meshes[i];
         if (mesh.primitiveType() != primitiveType) {
-            // Technically we don't have to call this here (since there is a safety check in
-            // pipelineState:setData but this will allow for quicker freeing of resources if the
-            // pipelineState sits in a cache for a while.
-            pipelineState->freeTempResources(fGpu);
             SkDEBUGCODE(pipelineState = nullptr);
             primitiveType = mesh.primitiveType();
             pipelineState = this->prepareDrawState(primProc, pipeline, fixedDynamicState,
@@ -712,18 +725,18 @@
                                                      pipeline.proxy()->origin(),
                                                      dynamicStateArrays->fScissorRects[i]);
         }
-
+        if (dynamicTextures) {
+            GrTextureProxy* const* meshProxies = dynamicStateArrays->fPrimitiveProcessorTextures +
+                                                 primProc.numTextureSamplers() * i;
+            pipelineState->setAndBindTextures(fGpu, primProc, pipeline, meshProxies,
+                                              cbInfo.currentCmdBuf());
+        }
         SkASSERT(pipelineState);
         mesh.sendToGpu(this);
     }
 
     cbInfo.fBounds.join(bounds);
     cbInfo.fIsEmpty = false;
-
-    // Technically we don't have to call this here (since there is a safety check in
-    // pipelineState:setData but this will allow for quicker freeing of resources if the
-    // pipelineState sits in a cache for a while.
-    pipelineState->freeTempResources(fGpu);
 }
 
 void GrVkGpuRTCommandBuffer::sendInstancedMeshToGpu(GrPrimitiveType,
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index 6c2b00b..df85c42 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -53,10 +53,6 @@
         , fFragmentProcessors(std::move(fragmentProcessors))
         , fFragmentProcessorCnt(fragmentProcessorCnt)
         , fDataManager(uniforms, geometryUniformSize, fragmentUniformSize) {
-    fSamplers.setReserve(numSamplers);
-    fTextureViews.setReserve(numSamplers);
-    fTextures.setReserve(numSamplers);
-
     fDescriptorSets[0] = VK_NULL_HANDLE;
     fDescriptorSets[1] = VK_NULL_HANDLE;
     fDescriptorSets[2] = VK_NULL_HANDLE;
@@ -71,26 +67,6 @@
     // Must have freed all GPU resources before this is destroyed
     SkASSERT(!fPipeline);
     SkASSERT(!fPipelineLayout);
-    SkASSERT(!fSamplers.count());
-    SkASSERT(!fTextureViews.count());
-    SkASSERT(!fTextures.count());
-}
-
-void GrVkPipelineState::freeTempResources(const GrVkGpu* gpu) {
-    for (int i = 0; i < fSamplers.count(); ++i) {
-        fSamplers[i]->unref(gpu);
-    }
-    fSamplers.rewind();
-
-    for (int i = 0; i < fTextureViews.count(); ++i) {
-        fTextureViews[i]->unref(gpu);
-    }
-    fTextureViews.rewind();
-
-    for (int i = 0; i < fTextures.count(); ++i) {
-        fTextures[i]->unref(gpu);
-    }
-    fTextures.rewind();
 }
 
 void GrVkPipelineState::freeGPUResources(const GrVkGpu* gpu) {
@@ -121,8 +97,6 @@
         fSamplerDescriptorSet->recycle(const_cast<GrVkGpu*>(gpu));
         fSamplerDescriptorSet = nullptr;
     }
-
-    this->freeTempResources(gpu);
 }
 
 void GrVkPipelineState::abandonGPUResources() {
@@ -139,21 +113,6 @@
     fGeometryUniformBuffer->abandon();
     fFragmentUniformBuffer->abandon();
 
-    for (int i = 0; i < fSamplers.count(); ++i) {
-        fSamplers[i]->unrefAndAbandon();
-    }
-    fSamplers.rewind();
-
-    for (int i = 0; i < fTextureViews.count(); ++i) {
-        fTextureViews[i]->unrefAndAbandon();
-    }
-    fTextureViews.rewind();
-
-    for (int i = 0; i < fTextures.count(); ++i) {
-        fTextures[i]->unrefAndAbandon();
-    }
-    fTextures.rewind();
-
     if (fUniformDescriptorSet) {
         fUniformDescriptorSet->unrefAndAbandon();
         fUniformDescriptorSet = nullptr;
@@ -165,41 +124,20 @@
     }
 }
 
-void GrVkPipelineState::setData(GrVkGpu* gpu,
-                                const GrPrimitiveProcessor& primProc,
-                                const GrPipeline& pipeline,
-                                const GrTextureProxy* const primProcTextures[]) {
-    SkASSERT(primProcTextures || !primProc.numTextureSamplers());
-    // This is here to protect against someone calling setData multiple times in a row without
-    // freeing the tempData between calls.
-    this->freeTempResources(gpu);
-
+void GrVkPipelineState::setAndBindUniforms(GrVkGpu* gpu,
+                                           const GrPrimitiveProcessor& primProc,
+                                           const GrPipeline& pipeline,
+                                           GrVkCommandBuffer* commandBuffer) {
     this->setRenderTargetState(pipeline.proxy());
 
-    SkAutoSTMalloc<8, SamplerBindings> samplerBindings(fNumSamplers);
-    int currTextureBinding = 0;
-
     fGeometryProcessor->setData(fDataManager, primProc,
                                 GrFragmentProcessor::CoordTransformIter(pipeline));
-    if (primProcTextures) {
-        for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
-            const auto& sampler = primProc.textureSampler(i);
-            auto texture = static_cast<GrVkTexture*>(primProcTextures[i]->peekTexture());
-            samplerBindings[currTextureBinding++] = {sampler.samplerState(), texture};
-        }
-    }
-
     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) {
-            const auto& sampler = fp->textureSampler(i);
-            samplerBindings[currTextureBinding++] =
-                    {sampler.samplerState(), static_cast<GrVkTexture*>(sampler.peekTexture())};
-        }
         fp = iter.next();
         glslFP = glslIter.next();
     }
@@ -212,13 +150,77 @@
         fXferProcessor->setData(fDataManager, pipeline.getXferProcessor(), dstTexture, offset);
     }
 
+    // Get new descriptor set
+    if (fGeometryUniformBuffer || fFragmentUniformBuffer) {
+        int uniformDSIdx = GrVkUniformHandler::kUniformBufferDescSet;
+        if (fDataManager.uploadUniformBuffers(
+                    gpu, fGeometryUniformBuffer.get(), fFragmentUniformBuffer.get()) ||
+            !fUniformDescriptorSet) {
+            if (fUniformDescriptorSet) {
+                fUniformDescriptorSet->recycle(gpu);
+            }
+            fUniformDescriptorSet = gpu->resourceProvider().getUniformDescriptorSet();
+            fDescriptorSets[uniformDSIdx] = fUniformDescriptorSet->descriptorSet();
+            this->writeUniformBuffers(gpu);
+        }
+        commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout, uniformDSIdx, 1,
+                                          &fDescriptorSets[uniformDSIdx], 0, nullptr);
+        if (fUniformDescriptorSet) {
+            commandBuffer->addRecycledResource(fUniformDescriptorSet);
+        }
+        if (fGeometryUniformBuffer) {
+            commandBuffer->addRecycledResource(fGeometryUniformBuffer->resource());
+        }
+        if (fFragmentUniformBuffer) {
+            commandBuffer->addRecycledResource(fFragmentUniformBuffer->resource());
+        }
+    }
+}
+
+void GrVkPipelineState::setAndBindTextures(GrVkGpu* gpu,
+                                           const GrPrimitiveProcessor& primProc,
+                                           const GrPipeline& pipeline,
+                                           const GrTextureProxy* const primProcTextures[],
+                                           GrVkCommandBuffer* commandBuffer) {
+    SkASSERT(primProcTextures || !primProc.numTextureSamplers());
+
+    struct SamplerBindings {
+        GrSamplerState fState;
+        GrVkTexture* fTexture;
+    };
+    SkAutoSTMalloc<8, SamplerBindings> samplerBindings(fNumSamplers);
+    int currTextureBinding = 0;
+
+    fGeometryProcessor->setData(fDataManager, primProc,
+                                GrFragmentProcessor::CoordTransformIter(pipeline));
+    for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
+        const auto& sampler = primProc.textureSampler(i);
+        auto texture = static_cast<GrVkTexture*>(primProcTextures[i]->peekTexture());
+        samplerBindings[currTextureBinding++] = {sampler.samplerState(), texture};
+    }
+
+    GrFragmentProcessor::Iter iter(pipeline);
+    GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.get(), fFragmentProcessorCnt);
+    const GrFragmentProcessor* fp = iter.next();
+    GrGLSLFragmentProcessor* glslFP = glslIter.next();
+    while (fp && glslFP) {
+        for (int i = 0; i < fp->numTextureSamplers(); ++i) {
+            const auto& sampler = fp->textureSampler(i);
+            samplerBindings[currTextureBinding++] =
+                    {sampler.samplerState(), static_cast<GrVkTexture*>(sampler.peekTexture())};
+        }
+        fp = iter.next();
+        glslFP = glslIter.next();
+    }
+    SkASSERT(!fp && !glslFP);
+
     if (GrTextureProxy* dstTextureProxy = pipeline.dstTextureProxy()) {
         samplerBindings[currTextureBinding++] = {
                 GrSamplerState::ClampNearest(),
                 static_cast<GrVkTexture*>(dstTextureProxy->peekTexture())};
     }
 
-    // Get new descriptor sets
+    // Get new descriptor set
     SkASSERT(fNumSamplers == currTextureBinding);
     if (fNumSamplers) {
         if (fSamplerDescriptorSet) {
@@ -227,23 +229,44 @@
         fSamplerDescriptorSet = gpu->resourceProvider().getSamplerDescriptorSet(fSamplerDSHandle);
         int samplerDSIdx = GrVkUniformHandler::kSamplerDescSet;
         fDescriptorSets[samplerDSIdx] = fSamplerDescriptorSet->descriptorSet();
-        this->writeSamplers(gpu, samplerBindings.get());
-    }
+        for (int i = 0; i < fNumSamplers; ++i) {
+            const GrSamplerState& state = samplerBindings[i].fState;
+            GrVkTexture* texture = samplerBindings[i].fTexture;
 
-    if (fGeometryUniformBuffer || fFragmentUniformBuffer) {
-        if (fDataManager.uploadUniformBuffers(gpu,
-                                              fGeometryUniformBuffer.get(),
-                                              fFragmentUniformBuffer.get())
-            || !fUniformDescriptorSet)
-        {
-            if (fUniformDescriptorSet) {
-                fUniformDescriptorSet->recycle(gpu);
-            }
-            fUniformDescriptorSet = gpu->resourceProvider().getUniformDescriptorSet();
-            int uniformDSIdx = GrVkUniformHandler::kUniformBufferDescSet;
-            fDescriptorSets[uniformDSIdx] = fUniformDescriptorSet->descriptorSet();
-            this->writeUniformBuffers(gpu);
+            const GrVkImageView* textureView = texture->textureView();
+            GrVkSampler* sampler = gpu->resourceProvider().findOrCreateCompatibleSampler(
+                    state, texture->texturePriv().maxMipMapLevel());
+
+            VkDescriptorImageInfo imageInfo;
+            memset(&imageInfo, 0, sizeof(VkDescriptorImageInfo));
+            imageInfo.sampler = sampler->sampler();
+            imageInfo.imageView = textureView->imageView();
+            imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+            VkWriteDescriptorSet writeInfo;
+            memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
+            writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+            writeInfo.pNext = nullptr;
+            writeInfo.dstSet = fDescriptorSets[GrVkUniformHandler::kSamplerDescSet];
+            writeInfo.dstBinding = i;
+            writeInfo.dstArrayElement = 0;
+            writeInfo.descriptorCount = 1;
+            writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+            writeInfo.pImageInfo = &imageInfo;
+            writeInfo.pBufferInfo = nullptr;
+            writeInfo.pTexelBufferView = nullptr;
+
+            GR_VK_CALL(gpu->vkInterface(),
+                       UpdateDescriptorSets(gpu->device(), 1, &writeInfo, 0, nullptr));
+            commandBuffer->addResource(sampler);
+            sampler->unref(gpu);
+            commandBuffer->addResource(samplerBindings[i].fTexture->textureView());
+            commandBuffer->addResource(samplerBindings[i].fTexture->resource());
         }
+
+        commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout, samplerDSIdx, 1,
+                                          &fDescriptorSets[samplerDSIdx], 0, nullptr);
+        commandBuffer->addRecycledResource(fSamplerDescriptorSet);
     }
 }
 
@@ -305,49 +328,6 @@
     }
 }
 
-void GrVkPipelineState::writeSamplers(GrVkGpu* gpu, const SamplerBindings bindings[]) {
-    for (int i = 0; i < fNumSamplers; ++i) {
-        const GrSamplerState& state = bindings[i].fState;
-        GrVkTexture* texture = bindings[i].fTexture;
-
-        fSamplers.push_back(gpu->resourceProvider().findOrCreateCompatibleSampler(
-                state, texture->texturePriv().maxMipMapLevel()));
-
-        const GrVkResource* textureResource = texture->resource();
-        textureResource->ref();
-        fTextures.push_back(textureResource);
-
-        const GrVkImageView* textureView = texture->textureView();
-        textureView->ref();
-        fTextureViews.push_back(textureView);
-
-        VkDescriptorImageInfo imageInfo;
-        memset(&imageInfo, 0, sizeof(VkDescriptorImageInfo));
-        imageInfo.sampler = fSamplers[i]->sampler();
-        imageInfo.imageView = textureView->imageView();
-        imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
-
-        VkWriteDescriptorSet writeInfo;
-        memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
-        writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-        writeInfo.pNext = nullptr;
-        writeInfo.dstSet = fDescriptorSets[GrVkUniformHandler::kSamplerDescSet];
-        writeInfo.dstBinding = i;
-        writeInfo.dstArrayElement = 0;
-        writeInfo.descriptorCount = 1;
-        writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-        writeInfo.pImageInfo = &imageInfo;
-        writeInfo.pBufferInfo = nullptr;
-        writeInfo.pTexelBufferView = nullptr;
-
-        GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(),
-                                                            1,
-                                                            &writeInfo,
-                                                            0,
-                                                            nullptr));
-    }
-}
-
 void GrVkPipelineState::setRenderTargetState(const GrRenderTargetProxy* proxy) {
     GrRenderTarget* rt = proxy->peekRenderTarget();
 
@@ -372,47 +352,6 @@
     }
 }
 
-void GrVkPipelineState::bind(const GrVkGpu* gpu, GrVkCommandBuffer* commandBuffer) {
+void GrVkPipelineState::bindPipeline(const GrVkGpu* gpu, GrVkCommandBuffer* commandBuffer) {
     commandBuffer->bindPipeline(gpu, fPipeline);
-
-    if (fGeometryUniformBuffer || fFragmentUniformBuffer) {
-        int dsIndex = GrVkUniformHandler::kUniformBufferDescSet;
-        commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout,
-                                          dsIndex, 1,
-                                          &fDescriptorSets[dsIndex], 0, nullptr);
-    }
-    if (fNumSamplers) {
-        int dsIndex = GrVkUniformHandler::kSamplerDescSet;
-        commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout,
-                                          dsIndex, 1,
-                                          &fDescriptorSets[dsIndex], 0, nullptr);
-    }
-}
-
-void GrVkPipelineState::addUniformResources(GrVkCommandBuffer& commandBuffer) {
-    if (fUniformDescriptorSet) {
-        commandBuffer.addRecycledResource(fUniformDescriptorSet);
-    }
-    if (fSamplerDescriptorSet) {
-        commandBuffer.addRecycledResource(fSamplerDescriptorSet);
-    }
-
-    if (fGeometryUniformBuffer.get()) {
-        commandBuffer.addRecycledResource(fGeometryUniformBuffer->resource());
-    }
-    if (fFragmentUniformBuffer.get()) {
-        commandBuffer.addRecycledResource(fFragmentUniformBuffer->resource());
-    }
-
-    for (int i = 0; i < fSamplers.count(); ++i) {
-        commandBuffer.addResource(fSamplers[i]);
-    }
-
-    for (int i = 0; i < fTextureViews.count(); ++i) {
-        commandBuffer.addResource(fTextureViews[i]);
-    }
-
-    for (int i = 0; i < fTextures.count(); ++i) {
-        commandBuffer.addResource(fTextures[i]);
-    }
 }
diff --git a/src/gpu/vk/GrVkPipelineState.h b/src/gpu/vk/GrVkPipelineState.h
index d61359c..994d0a9 100644
--- a/src/gpu/vk/GrVkPipelineState.h
+++ b/src/gpu/vk/GrVkPipelineState.h
@@ -56,35 +56,32 @@
 
     ~GrVkPipelineState();
 
-    void setData(GrVkGpu*, const GrPrimitiveProcessor&, const GrPipeline&,
-                 const GrTextureProxy* const primitiveProcessorTextures[]);
+    void setAndBindUniforms(GrVkGpu*, const GrPrimitiveProcessor&, const GrPipeline&,
+                            GrVkCommandBuffer*);
+    /**
+     * This must be called after setAndBindUniforms() since that function invalidates texture
+     * bindings.
+     */
+    void setAndBindTextures(GrVkGpu*, const GrPrimitiveProcessor&, const GrPipeline&,
+                            const GrTextureProxy* const primitiveProcessorTextures[],
+                            GrVkCommandBuffer*);
 
-    void bind(const GrVkGpu* gpu, GrVkCommandBuffer* commandBuffer);
+    void bindPipeline(const GrVkGpu* gpu, GrVkCommandBuffer* commandBuffer);
 
-    void addUniformResources(GrVkCommandBuffer&);
+    void addUniformResources(GrVkCommandBuffer&, GrVkSampler*[], GrVkTexture*[], int numTextures);
 
     void freeGPUResources(const GrVkGpu* gpu);
 
-    // This releases resources that only a given instance of a GrVkPipelineState needs to hold onto
-    // and don't need to survive across new uses of the GrVkPipelineState.
-    void freeTempResources(const GrVkGpu* gpu);
-
     void abandonGPUResources();
 
 private:
     void writeUniformBuffers(const GrVkGpu* gpu);
 
-    struct SamplerBindings {
-        GrSamplerState fState;
-        GrVkTexture* fTexture;
-    };
-    void writeSamplers(GrVkGpu* gpu, const SamplerBindings[]);
-
     /**
-    * We use the RT's size and origin to adjust from Skia device space to vulkan normalized device
-    * space and to make device space positions have the correct origin for processors that require
-    * them.
-    */
+     * We use the RT's size and origin to adjust from Skia device space to vulkan normalized device
+     * space and to make device space positions have the correct origin for processors that require
+     * them.
+     */
     struct RenderTargetState {
         SkISize         fRenderTargetSize;
         GrSurfaceOrigin fRenderTargetOrigin;
@@ -141,11 +138,6 @@
     std::unique_ptr<GrVkUniformBuffer> fGeometryUniformBuffer;
     std::unique_ptr<GrVkUniformBuffer> fFragmentUniformBuffer;
 
-    // GrVkResources used for sampling textures
-    SkTDArray<GrVkSampler*> fSamplers;
-    SkTDArray<const GrVkImageView*> fTextureViews;
-    SkTDArray<const GrVkResource*> fTextures;
-
     // Tracks the current render target uniforms stored in the vertex buffer.
     RenderTargetState fRenderTargetState;
     GrGLSLBuiltinUniformHandles fBuiltinUniformHandles;
diff --git a/src/gpu/vk/GrVkUniformHandler.h b/src/gpu/vk/GrVkUniformHandler.h
index bd8f8f3..d79b1b3 100644
--- a/src/gpu/vk/GrVkUniformHandler.h
+++ b/src/gpu/vk/GrVkUniformHandler.h
@@ -17,6 +17,12 @@
     static const int kUniformsPerBlock = 8;
 
     enum {
+        /**
+         * Binding a descriptor set invalidates all higher index descriptor sets. We must bind
+         * in the order of this enumeration. Samplers are after Uniforms because GrOps can specify
+         * GP textures as dynamic state, meaning they get rebound for each GrMesh in a draw while
+         * uniforms are bound once before all the draws.
+         */
         kUniformBufferDescSet = 0,
         kSamplerDescSet = 1,
     };