Esempio n. 1
0
void GrVkProgram::setData(const GrVkGpu* gpu,
                          const GrPrimitiveProcessor& primProc,
                          const GrPipeline& pipeline) {
    // This is here to protect against someone calling setData multiple times in a row without
    // freeing the tempData between calls.
    this->freeTempResources(gpu);

    this->setRenderTargetState(pipeline);

    SkSTArray<8, const GrTextureAccess*> textureBindings;

    fGeometryProcessor->setData(fProgramDataManager, primProc);
    append_texture_bindings(primProc, &textureBindings);

    for (int i = 0; i < fFragmentProcessors.count(); ++i) {
        const GrFragmentProcessor& processor = pipeline.getFragmentProcessor(i);
        fFragmentProcessors[i]->setData(fProgramDataManager, processor);
        fGeometryProcessor->setTransformData(primProc, fProgramDataManager, i,
                                             processor.coordTransforms());
        append_texture_bindings(processor, &textureBindings);
    }

    fXferProcessor->setData(fProgramDataManager, pipeline.getXferProcessor());
    append_texture_bindings(pipeline.getXferProcessor(), &textureBindings);

    this->writeUniformBuffers(gpu);

    this->writeSamplers(gpu, textureBindings);
}
Esempio n. 2
0
bool GrPipeline::AreEqual(const GrPipeline& a, const GrPipeline& b,
                          bool ignoreCoordTransforms) {
    SkASSERT(&a != &b);

    if (a.getRenderTarget() != b.getRenderTarget() ||
        a.fFragmentProcessors.count() != b.fFragmentProcessors.count() ||
        a.fNumColorProcessors != b.fNumColorProcessors ||
        a.fScissorState != b.fScissorState ||
        a.fFlags != b.fFlags ||
        a.fStencilSettings != b.fStencilSettings ||
        a.fDrawFace != b.fDrawFace) {
        return false;
    }

    // Most of the time both are nullptr
    if (a.fXferProcessor.get() || b.fXferProcessor.get()) {
        if (!a.getXferProcessor().isEqual(b.getXferProcessor())) {
            return false;
        }
    }

    for (int i = 0; i < a.numFragmentProcessors(); i++) {
        if (!a.getFragmentProcessor(i).isEqual(b.getFragmentProcessor(i), ignoreCoordTransforms)) {
            return false;
        }
    }
    return true;
}
Esempio n. 3
0
void GrVkPipelineState::setData(GrVkGpu* gpu,
                                const GrPrimitiveProcessor& primProc,
                                const GrPipeline& pipeline) {
    // This is here to protect against someone calling setData multiple times in a row without
    // freeing the tempData between calls.
    this->freeTempResources(gpu);

    this->setRenderTargetState(pipeline);

    SkSTArray<8, const GrTextureAccess*> textureBindings;

    fGeometryProcessor->setData(fDataManager, primProc);
    append_texture_bindings(primProc, &textureBindings);

    for (int i = 0; i < fFragmentProcessors.count(); ++i) {
        const GrFragmentProcessor& processor = pipeline.getFragmentProcessor(i);
        fFragmentProcessors[i]->setData(fDataManager, processor);
        fGeometryProcessor->setTransformData(primProc, fDataManager, i,
                                             processor.coordTransforms());
        append_texture_bindings(processor, &textureBindings);
    }

    fXferProcessor->setData(fDataManager, pipeline.getXferProcessor());
    append_texture_bindings(pipeline.getXferProcessor(), &textureBindings);

    // Get new descriptor sets
    if (fNumSamplers) {
        fSamplerPoolManager.getNewDescriptorSet(gpu,
                                             &fDescriptorSets[GrVkUniformHandler::kSamplerDescSet]);
        this->writeSamplers(gpu, textureBindings, pipeline.getAllowSRGBInputs());
    }

    if (fVertexUniformBuffer.get() || fFragmentUniformBuffer.get()) {
        if (fDataManager.uploadUniformBuffers(gpu, fVertexUniformBuffer, fFragmentUniformBuffer) ||
            VK_NULL_HANDLE == fDescriptorSets[GrVkUniformHandler::kUniformBufferDescSet]) {
            const GrVkDescriptorPool* pool;
            int uniformDSIdx = GrVkUniformHandler::kUniformBufferDescSet;
            gpu->resourceProvider().getUniformDescriptorSet(&fDescriptorSets[uniformDSIdx],
                                                            &pool);
            if (pool != fCurrentUniformDescPool) {
                if (fCurrentUniformDescPool) {
                    fCurrentUniformDescPool->unref(gpu);
                }
                fCurrentUniformDescPool = pool;
                fCurrentUniformDescPool->ref();
            }
            this->writeUniformBuffers(gpu);
        }
    }
}
void GrGLProgram::setData(const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline) {
    this->setRenderTargetState(primProc, pipeline.proxy());

    // we set the textures, and uniforms for installed processors in a generic way, but subclasses
    // of GLProgram determine how to set coord transforms

    // We must bind to texture units in the same order in which we set the uniforms in
    // GrGLProgramDataManager. That is first all texture samplers and then texel buffers.
    // Within each group we will bind them in primProc, fragProcs, XP order.
    int nextTexSamplerIdx = 0;
    int nextTexelBufferIdx = fNumTextureSamplers;
    fGeometryProcessor->setData(fProgramDataManager, primProc,
                                GrFragmentProcessor::CoordTransformIter(pipeline));
    this->bindTextures(primProc, pipeline.getAllowSRGBInputs(), &nextTexSamplerIdx,
                       &nextTexelBufferIdx);

    this->setFragmentData(primProc, pipeline, &nextTexSamplerIdx, &nextTexelBufferIdx);

    const GrXferProcessor& xp = pipeline.getXferProcessor();
    SkIPoint offset;
    GrTexture* dstTexture = pipeline.peekDstTexture(&offset);

    fXferProcessor->setData(fProgramDataManager, xp, dstTexture, offset);
    if (dstTexture) {
        fGpu->bindTexture(nextTexSamplerIdx++, GrSamplerState::ClampNearest(), true,
                          static_cast<GrGLTexture*>(dstTexture),
                          pipeline.dstTextureProxy()->origin());
    }
    SkASSERT(nextTexSamplerIdx == fNumTextureSamplers);
    SkASSERT(nextTexelBufferIdx == fNumTextureSamplers + fNumTexelBuffers);
}
Esempio n. 5
0
bool GrPipeline::isEqual(const GrPipeline& that) const {
    if (this->getRenderTarget() != that.getRenderTarget() ||
        this->fFragmentStages.count() != that.fFragmentStages.count() ||
        this->fNumColorStages != that.fNumColorStages ||
        this->fScissorState != that.fScissorState ||
        this->fFlags != that.fFlags ||
        this->fStencilSettings != that.fStencilSettings ||
        this->fDrawFace != that.fDrawFace) {
        return false;
    }

    if (!this->getXferProcessor()->isEqual(*that.getXferProcessor())) {
        return false;
    }

    // The program desc comparison should have already assured that the stage counts match.
    SkASSERT(this->numFragmentStages() == that.numFragmentStages());
    for (int i = 0; i < this->numFragmentStages(); i++) {

        if (this->getFragmentStage(i) != that.getFragmentStage(i)) {
            return false;
        }
    }
    return true;
}
Esempio n. 6
0
void GrVkPipelineState::setAndBindUniforms(GrVkGpu* gpu,
                                           const GrRenderTarget* renderTarget,
                                           GrSurfaceOrigin origin,
                                           const GrPrimitiveProcessor& primProc,
                                           const GrPipeline& pipeline,
                                           GrVkCommandBuffer* commandBuffer) {
    this->setRenderTargetState(renderTarget, origin);

    fGeometryProcessor->setData(fDataManager, primProc,
                                GrFragmentProcessor::CoordTransformIter(pipeline));
    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);
        fp = iter.next();
        glslFP = glslIter.next();
    }
    SkASSERT(!fp && !glslFP);

    {
        SkIPoint offset;
        GrTexture* dstTexture = pipeline.peekDstTexture(&offset);

        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());
        }
    }
}
Esempio n. 7
0
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);
}
Esempio n. 8
0
void GrGLProgram::generateMipmaps(const GrPrimitiveProcessor& primProc,
                                  const GrPipeline& pipeline) {
    this->generateMipmaps(primProc, pipeline.getAllowSRGBInputs());

    GrFragmentProcessor::Iter iter(pipeline);
    while (const GrFragmentProcessor* fp  = iter.next()) {
        this->generateMipmaps(*fp, pipeline.getAllowSRGBInputs());
    }

    if (primProc.getPixelLocalStorageState() !=
        GrPixelLocalStorageState::kDraw_GrPixelLocalStorageState) {
        const GrXferProcessor& xp = pipeline.getXferProcessor();
        this->generateMipmaps(xp, pipeline.getAllowSRGBInputs());
    }
}
Esempio n. 9
0
uint32_t get_blend_info_key(const GrPipeline& pipeline) {
    GrXferProcessor::BlendInfo blendInfo;
    pipeline.getXferProcessor().getBlendInfo(&blendInfo);

    static const uint32_t kBlendWriteShift = 1;
    static const uint32_t kBlendCoeffShift = 5;
    GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << kBlendCoeffShift));
    GR_STATIC_ASSERT(kFirstAdvancedGrBlendEquation - 1 < 4);

    uint32_t key = blendInfo.fWriteColor;
    key |= (blendInfo.fSrcBlend << kBlendWriteShift);
    key |= (blendInfo.fDstBlend << (kBlendWriteShift + kBlendCoeffShift));
    key |= (blendInfo.fEquation << (kBlendWriteShift + 2 * kBlendCoeffShift));

    return key;
}
Esempio n. 10
0
void GrGLProgram::setData(const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline,
                          const GrBatchTracker& batchTracker) {
    this->setRenderTargetState(primProc, pipeline);

    // we set the textures, and uniforms for installed processors in a generic way, but subclasses
    // of GLProgram determine how to set coord transforms
    fGeometryProcessor->fGLProc->setData(fProgramDataManager, primProc, batchTracker);
    this->bindTextures(fGeometryProcessor.get(), primProc);

    const GrXferProcessor& xp = *pipeline.getXferProcessor();
    fXferProcessor->fGLProc->setData(fProgramDataManager, xp);
    this->bindTextures(fXferProcessor.get(), xp);

    this->setFragmentData(primProc, pipeline);

    // Some of GrGLProgram subclasses need to update state here
    this->didSetData();
}
Esempio n. 11
0
void GrGLProgram::setData(const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline) {
    this->setRenderTargetState(primProc, pipeline);

    // we set the textures, and uniforms for installed processors in a generic way, but subclasses
    // of GLProgram determine how to set coord transforms
    int nextSamplerIdx = 0;
    fGeometryProcessor->setData(fProgramDataManager, primProc);
    this->bindTextures(primProc, pipeline.getAllowSRGBInputs(), &nextSamplerIdx);

    this->setFragmentData(primProc, pipeline, &nextSamplerIdx);

    if (primProc.getPixelLocalStorageState() !=
        GrPixelLocalStorageState::kDraw_GrPixelLocalStorageState) {
        const GrXferProcessor& xp = pipeline.getXferProcessor();
        fXferProcessor->setData(fProgramDataManager, xp);
        this->bindTextures(xp, pipeline.getAllowSRGBInputs(), &nextSamplerIdx);
    }
}
Esempio n. 12
0
void GrGLProgram::setData(const GrPrimitiveProcessor& primProc,
                          const GrPipeline& pipeline,
                          SkTArray<const GrTextureAccess*>* textureBindings) {
    this->setRenderTargetState(primProc, pipeline);

    // we set the textures, and uniforms for installed processors in a generic way, but subclasses
    // of GLProgram determine how to set coord transforms
    fGeometryProcessor->setData(fProgramDataManager, primProc);
    append_texture_bindings(primProc, textureBindings);

    this->setFragmentData(primProc, pipeline, textureBindings);

    if (primProc.getPixelLocalStorageState() !=
        GrPixelLocalStorageState::kDraw_GrPixelLocalStorageState) {
        const GrXferProcessor& xp = pipeline.getXferProcessor();
        fXferProcessor->setData(fProgramDataManager, xp);
        append_texture_bindings(xp, textureBindings);
    }
}
Esempio n. 13
0
void GrGLProgram::setData(const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline) {
    this->setRenderTargetState(primProc, pipeline.getRenderTarget());

    // we set the textures, and uniforms for installed processors in a generic way, but subclasses
    // of GLProgram determine how to set coord transforms
    int nextSamplerIdx = 0;
    fGeometryProcessor->setData(fProgramDataManager, primProc,
                                GrFragmentProcessor::CoordTransformIter(pipeline));
    this->bindTextures(primProc, pipeline.getAllowSRGBInputs(), &nextSamplerIdx);

    this->setFragmentData(primProc, pipeline, &nextSamplerIdx);

    const GrXferProcessor& xp = pipeline.getXferProcessor();
    SkIPoint offset;
    GrTexture* dstTexture = pipeline.dstTexture(&offset);
    fXferProcessor->setData(fProgramDataManager, xp, dstTexture, offset);
    if (dstTexture) {
        fGpu->bindTexture(nextSamplerIdx++, GrSamplerParams::ClampNoFilter(), true,
                          static_cast<GrGLTexture*>(dstTexture));
    }
}
Esempio n. 14
0
void setup_color_blend_state(const GrVkGpu* gpu,
                             const GrPipeline& pipeline,
                             VkPipelineColorBlendStateCreateInfo* colorBlendInfo,
                             VkPipelineColorBlendAttachmentState* attachmentState) {
    GrXferProcessor::BlendInfo blendInfo;
    pipeline.getXferProcessor().getBlendInfo(&blendInfo);

    GrBlendEquation equation = blendInfo.fEquation;
    GrBlendCoeff srcCoeff = blendInfo.fSrcBlend;
    GrBlendCoeff dstCoeff = blendInfo.fDstBlend;
    bool blendOff = (kAdd_GrBlendEquation == equation || kSubtract_GrBlendEquation == equation) &&
                    kOne_GrBlendCoeff == srcCoeff && kZero_GrBlendCoeff == dstCoeff;

    memset(attachmentState, 0, sizeof(VkPipelineColorBlendAttachmentState));
    attachmentState->blendEnable = !blendOff;
    if (!blendOff) {
        attachmentState->srcColorBlendFactor = blend_coeff_to_vk_blend(srcCoeff);
        attachmentState->dstColorBlendFactor = blend_coeff_to_vk_blend(dstCoeff);
        attachmentState->colorBlendOp = blend_equation_to_vk_blend_op(equation);
        attachmentState->srcAlphaBlendFactor = blend_coeff_to_vk_blend(srcCoeff);
        attachmentState->dstAlphaBlendFactor = blend_coeff_to_vk_blend(dstCoeff);
        attachmentState->alphaBlendOp = blend_equation_to_vk_blend_op(equation);
    }

    if (!blendInfo.fWriteColor) {
        attachmentState->colorWriteMask = 0;
    } else {
        attachmentState->colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
                                          VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
    }

    memset(colorBlendInfo, 0, sizeof(VkPipelineColorBlendStateCreateInfo));
    colorBlendInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
    colorBlendInfo->pNext = nullptr;
    colorBlendInfo->flags = 0;
    colorBlendInfo->logicOpEnable = VK_FALSE;
    colorBlendInfo->attachmentCount = 1;
    colorBlendInfo->pAttachments = attachmentState;
    // colorBlendInfo->blendConstants is set dynamically
}
Esempio n. 15
0
bool GrGLProgramDescBuilder::Build(GrProgramDesc* desc,
                                   const GrPrimitiveProcessor& primProc,
                                   const GrPipeline& pipeline,
                                   const GrGLGpu* gpu,
                                   const GrBatchTracker& batchTracker) {
    // The descriptor is used as a cache key. Thus when a field of the
    // descriptor will not affect program generation (because of the attribute
    // bindings in use or other descriptor field settings) it should be set
    // to a canonical value to avoid duplicate programs with different keys.

    GrGLProgramDesc* glDesc = (GrGLProgramDesc*) desc;

    GR_STATIC_ASSERT(0 == kProcessorKeysOffset % sizeof(uint32_t));
    // Make room for everything up to the effect keys.
    glDesc->key().reset();
    glDesc->key().push_back_n(kProcessorKeysOffset);

    GrProcessorKeyBuilder b(&glDesc->key());

    primProc.getGLProcessorKey(batchTracker, gpu->glCaps(), &b);
    if (!get_meta_key(primProc, gpu->glCaps(), 0, &b)) {
        glDesc->key().reset();
        return false;
    }

    for (int s = 0; s < pipeline.numFragmentStages(); ++s) {
        const GrPendingFragmentStage& fps = pipeline.getFragmentStage(s);
        const GrFragmentProcessor& fp = *fps.processor();
        fp.getGLProcessorKey(gpu->glCaps(), &b);
        if (!get_meta_key(fp, gpu->glCaps(), primProc.getTransformKey(fp.coordTransforms()), &b)) {
            glDesc->key().reset();
            return false;
        }
    }

    const GrXferProcessor& xp = *pipeline.getXferProcessor();
    xp.getGLProcessorKey(gpu->glCaps(), &b);
    if (!get_meta_key(xp, gpu->glCaps(), 0, &b)) {
        glDesc->key().reset();
        return false;
    }

    // --------DO NOT MOVE HEADER ABOVE THIS LINE--------------------------------------------------
    // Because header is a pointer into the dynamic array, we can't push any new data into the key
    // below here.
    KeyHeader* header = glDesc->atOffset<KeyHeader, kHeaderOffset>();

    // make sure any padding in the header is zeroed.
    memset(header, 0, kHeaderSize);

    if (pipeline.readsFragPosition()) {
        header->fFragPosKey =
                GrGLFragmentShaderBuilder::KeyForFragmentPosition(pipeline.getRenderTarget(),
                                                                  gpu->glCaps());
    } else {
        header->fFragPosKey = 0;
    }

    header->fColorEffectCnt = pipeline.numColorFragmentStages();
    header->fCoverageEffectCnt = pipeline.numCoverageFragmentStages();
    glDesc->finalize();
    return true;
}
Esempio n. 16
0
bool GrVkProgramDescBuilder::Build(GrProgramDesc* desc,
                                   const GrPrimitiveProcessor& primProc,
                                   const GrPipeline& pipeline,
                                   const GrGLSLCaps& glslCaps) {
    // The descriptor is used as a cache key. Thus when a field of the
    // descriptor will not affect program generation (because of the attribute
    // bindings in use or other descriptor field settings) it should be set
    // to a canonical value to avoid duplicate programs with different keys.

    GrVkProgramDesc* vkDesc = (GrVkProgramDesc*)desc;

    GR_STATIC_ASSERT(0 == kProcessorKeysOffset % sizeof(uint32_t));
    // Make room for everything up to the effect keys.
    vkDesc->key().reset();
    vkDesc->key().push_back_n(kProcessorKeysOffset);

    GrProcessorKeyBuilder b(&vkDesc->key());

    primProc.getGLSLProcessorKey(glslCaps, &b);
    if (!gen_meta_key(primProc, glslCaps, 0, &b)) {
        vkDesc->key().reset();
        return false;
    }
    GrProcessor::RequiredFeatures requiredFeatures = primProc.requiredFeatures();

    for (int i = 0; i < pipeline.numFragmentProcessors(); ++i) {
        const GrFragmentProcessor& fp = pipeline.getFragmentProcessor(i);
        if (!gen_frag_proc_and_meta_keys(primProc, fp, glslCaps, &b)) {
            vkDesc->key().reset();
            return false;
        }
        requiredFeatures |= fp.requiredFeatures();
    }

    const GrXferProcessor& xp = pipeline.getXferProcessor();
    xp.getGLSLProcessorKey(glslCaps, &b);
    if (!gen_meta_key(xp, glslCaps, 0, &b)) {
        vkDesc->key().reset();
        return false;
    }
    requiredFeatures |= xp.requiredFeatures();

    // --------DO NOT MOVE HEADER ABOVE THIS LINE--------------------------------------------------
    // Because header is a pointer into the dynamic array, we can't push any new data into the key
    // below here.
    KeyHeader* header = vkDesc->atOffset<KeyHeader, kHeaderOffset>();

    // make sure any padding in the header is zeroed.
    memset(header, 0, kHeaderSize);

    GrRenderTarget* rt = pipeline.getRenderTarget();

    if (requiredFeatures & (GrProcessor::kFragmentPosition_RequiredFeature |
                            GrProcessor::kSampleLocations_RequiredFeature)) {
        header->fSurfaceOriginKey = GrGLSLFragmentShaderBuilder::KeyForSurfaceOrigin(rt->origin());
    } else {
        header->fSurfaceOriginKey = 0;
    }

    if (requiredFeatures & GrProcessor::kSampleLocations_RequiredFeature) {
        SkASSERT(pipeline.isHWAntialiasState());
        header->fSamplePatternKey =
            rt->renderTargetPriv().getMultisampleSpecs(pipeline.getStencil()).fUniqueID;
    } else {
        header->fSamplePatternKey = 0;
    }

    header->fOutputSwizzle = glslCaps.configOutputSwizzle(rt->config()).asKey();

    if (pipeline.ignoresCoverage()) {
        header->fIgnoresCoverage = 1;
    } else {
        header->fIgnoresCoverage = 0;
    }

    header->fSnapVerticesToPixelCenters = pipeline.snapVerticesToPixelCenters();
    header->fColorEffectCnt = pipeline.numColorFragmentProcessors();
    header->fCoverageEffectCnt = pipeline.numCoverageFragmentProcessors();
    vkDesc->finalize();
    return true;
}
Esempio n. 17
0
void GrVkGpuCommandBuffer::onDraw(const GrPipeline& pipeline,
                                  const GrPrimitiveProcessor& primProc,
                                  const GrMesh* meshes,
                                  int meshCount) {
    if (!meshCount) {
        return;
    }
    GrRenderTarget* rt = pipeline.getRenderTarget();
    GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(rt);
    const GrVkRenderPass* renderPass = vkRT->simpleRenderPass();
    SkASSERT(renderPass);

    prepare_sampled_images(primProc, fGpu);
    GrFragmentProcessor::Iter iter(pipeline);
    while (const GrFragmentProcessor* fp = iter.next()) {
        prepare_sampled_images(*fp, fGpu);
    }
    prepare_sampled_images(pipeline.getXferProcessor(), fGpu);

    GrPrimitiveType primitiveType = meshes[0].primitiveType();
    sk_sp<GrVkPipelineState> pipelineState = this->prepareDrawState(pipeline,
                                                                    primProc,
                                                                    primitiveType,
                                                                    *renderPass);
    if (!pipelineState) {
        return;
    }

    for (int i = 0; i < meshCount; ++i) {
        const GrMesh& mesh = meshes[i];
        GrMesh::Iterator iter;
        const GrNonInstancedMesh* nonIdxMesh = iter.init(mesh);
        do {
            if (nonIdxMesh->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 = nonIdxMesh->primitiveType();
                pipelineState = this->prepareDrawState(pipeline,
                                                       primProc,
                                                       primitiveType,
                                                       *renderPass);
                if (!pipelineState) {
                    return;
                }
            }
            SkASSERT(pipelineState);
            this->bindGeometry(primProc, *nonIdxMesh);

            if (nonIdxMesh->isIndexed()) {
                fCommandBuffer->drawIndexed(fGpu,
                                            nonIdxMesh->indexCount(),
                                            1,
                                            nonIdxMesh->startIndex(),
                                            nonIdxMesh->startVertex(),
                                            0);
            } else {
                fCommandBuffer->draw(fGpu,
                                     nonIdxMesh->vertexCount(),
                                     1,
                                     nonIdxMesh->startVertex(),
                                     0);
            }
            fIsEmpty = false;

            fGpu->stats()->incNumDraws();
        } while ((nonIdxMesh = iter.next()));
    }

    // 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);
}