bool GrGLProgramBuilder::emitAndInstallProcs(GrGLSLExpr4* inputColor, GrGLSLExpr4* inputCoverage) {
    // First we loop over all of the installed processors and collect coord transforms.  These will
    // be sent to the GrGLPrimitiveProcessor in its emitCode function
    const GrPrimitiveProcessor& primProc = this->primitiveProcessor();
    int totalTextures = primProc.numTextures();
    const int maxTextureUnits = fGpu->glCaps().maxFragmentTextureUnits();

    for (int i = 0; i < this->pipeline().numFragmentProcessors(); i++) {
        const GrFragmentProcessor& processor = this->pipeline().getFragmentProcessor(i);

        if (!primProc.hasTransformedLocalCoords()) {
            SkTArray<const GrCoordTransform*, true>& procCoords = fCoordTransforms.push_back();
            processor.gatherCoordTransforms(&procCoords);
        }

        totalTextures += processor.numTextures();
        if (totalTextures >= maxTextureUnits) {
            GrCapsDebugf(fGpu->caps(), "Program would use too many texture units\n");
            return false;
        }
    }

    this->emitAndInstallProc(primProc, inputColor, inputCoverage);

    fFragmentProcessors.reset(new GrGLInstalledFragProcs);
    int numProcs = this->pipeline().numFragmentProcessors();
    this->emitAndInstallFragProcs(0, this->pipeline().numColorFragmentProcessors(), inputColor);
    this->emitAndInstallFragProcs(this->pipeline().numColorFragmentProcessors(), numProcs,
                                  inputCoverage);
    this->emitAndInstallXferProc(*this->pipeline().getXferProcessor(), *inputColor, *inputCoverage);
    return true;
}
GrGLProgram* GrGLGpu::ProgramCache::refProgram(const GrGLGpu* gpu,
                                               const GrPipeline& pipeline,
                                               const GrPrimitiveProcessor& primProc) {
#ifdef PROGRAM_CACHE_STATS
    ++fTotalRequests;
#endif

    // Get GrGLProgramDesc
    GrGLProgramDesc desc;
    if (!GrGLProgramDescBuilder::Build(&desc, primProc, pipeline, *gpu->glCaps().glslCaps())) {
        GrCapsDebugf(gpu->caps(), "Failed to gl program descriptor!\n");
        return nullptr;
    }

    Entry* entry = nullptr;

    uint32_t hashIdx = desc.getChecksum();
    hashIdx ^= hashIdx >> 16;
    if (kHashBits <= 8) {
        hashIdx ^= hashIdx >> 8;
    }
bool GrDrawTarget::setupDstReadIfNecessary(const GrPipelineBuilder& pipelineBuilder,
                                           const GrPipelineOptimizations& optimizations,
                                           GrXferProcessor::DstTexture* dstTexture,
                                           const SkRect& batchBounds) {
    SkRect bounds = batchBounds;
    bounds.outset(0.5f, 0.5f);

    if (!pipelineBuilder.willXPNeedDstTexture(*this->caps(), optimizations)) {
        return true;
    }

    GrRenderTarget* rt = pipelineBuilder.getRenderTarget();

    if (this->caps()->textureBarrierSupport()) {
        if (GrTexture* rtTex = rt->asTexture()) {
            // The render target is a texture, so we can read from it directly in the shader. The XP
            // will be responsible to detect this situation and request a texture barrier.
            dstTexture->setTexture(rtTex);
            dstTexture->setOffset(0, 0);
            return true;
        }
    }

    SkIRect copyRect;
    pipelineBuilder.clip().getConservativeBounds(rt->width(), rt->height(), &copyRect);

    SkIRect drawIBounds;
    bounds.roundOut(&drawIBounds);
    if (!copyRect.intersect(drawIBounds)) {
#ifdef SK_DEBUG
        GrCapsDebugf(this->caps(), "Missed an early reject. "
                                   "Bailing on draw from setupDstReadIfNecessary.\n");
#endif
        return false;
    }

    // MSAA consideration: When there is support for reading MSAA samples in the shader we could
    // have per-sample dst values by making the copy multisampled.
    GrSurfaceDesc desc;
    if (!fGpu->initCopySurfaceDstDesc(rt, &desc)) {
        desc.fOrigin = kDefault_GrSurfaceOrigin;
        desc.fFlags = kRenderTarget_GrSurfaceFlag;
        desc.fConfig = rt->config();
    }

    desc.fWidth = copyRect.width();
    desc.fHeight = copyRect.height();

    static const uint32_t kFlags = 0;
    SkAutoTUnref<GrTexture> copy(fResourceProvider->createApproxTexture(desc, kFlags));

    if (!copy) {
        SkDebugf("Failed to create temporary copy of destination texture.\n");
        return false;
    }
    SkIPoint dstPoint = {0, 0};
    this->copySurface(copy, rt, copyRect, dstPoint);
    dstTexture->setTexture(copy);
    dstTexture->setOffset(copyRect.fLeft, copyRect.fTop);
    return true;
}
sk_sp<GrVkPipelineState> GrVkResourceProvider::PipelineStateCache::refPipelineState(
                                                               const GrPipeline& pipeline,
                                                               const GrPrimitiveProcessor& primProc,
                                                               GrPrimitiveType primitiveType,
                                                               const GrVkRenderPass& renderPass) {
#ifdef GR_PIPELINE_STATE_CACHE_STATS
    ++fTotalRequests;
#endif
    // Get GrVkProgramDesc
    GrVkPipelineState::Desc desc;
    if (!GrVkProgramDescBuilder::Build(&desc.fProgramDesc,
                                       primProc,
                                       pipeline,
                                       *fGpu->vkCaps().glslCaps())) {
        GrCapsDebugf(fGpu->caps(), "Failed to build vk program descriptor!\n");
        return false;
    }

    // Get vulkan specific descriptor key
    GrVkPipelineState::BuildStateKey(pipeline, primitiveType, &desc.fStateKey);
    // Get checksum of entire PipelineDesc
    int keyLength = desc.fStateKey.count();
    SkASSERT(0 == (keyLength % 4));
    // Seed the checksum with the checksum of the programDesc then add the vulkan key to it.
    desc.fChecksum = SkChecksum::Murmur3(desc.fStateKey.begin(), keyLength,
                                         desc.fProgramDesc.getChecksum());

    Entry* entry = nullptr;
    if (Entry** entryptr = fHashTable.find(desc)) {
        SkASSERT(*entryptr);
        entry = *entryptr;
    }
    if (!entry) {
#ifdef GR_PIPELINE_STATE_CACHE_STATS
        ++fCacheMisses;
#endif
        sk_sp<GrVkPipelineState> pipelineState(
            GrVkPipelineStateBuilder::CreatePipelineState(fGpu,
                                                          pipeline,
                                                          primProc,
                                                          primitiveType,
                                                          desc,
                                                          renderPass));
        if (nullptr == pipelineState) {
            return nullptr;
        }
        if (fCount < kMaxEntries) {
            entry = new Entry;
            fCount++;
        } else {
            SkASSERT(fCount == kMaxEntries);
            entry = fLRUList.head();
            fLRUList.remove(entry);
            entry->fPipelineState->freeGPUResources(fGpu);
            fHashTable.remove(entry->fPipelineState->getDesc());
        }
        entry->fPipelineState = std::move(pipelineState);
        fHashTable.set(entry);
        fLRUList.addToTail(entry);
        return entry->fPipelineState;
    } else {
        fLRUList.remove(entry);
        fLRUList.addToTail(entry);
    }
    return entry->fPipelineState;
}