/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrVkResourceProvider.h" #include "GrVkGpu.h" #include "GrProcessor.h" #include "GrRenderTargetPriv.h" // TODO: remove once refPipelineState gets passed stencil settings. #include "GrVkPipelineState.h" #include "GrVkPipelineStateBuilder.h" #include "SkOpts.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLProgramDataManager.h" #ifdef GR_PIPELINE_STATE_CACHE_STATS // Display pipeline state cache usage static const bool c_DisplayVkPipelineCache{false}; #endif struct GrVkResourceProvider::PipelineStateCache::Entry { Entry() : fPipelineState(nullptr) {} static const GrVkPipelineState::Desc& GetKey(const Entry* entry) { return entry->fPipelineState->getDesc(); } static uint32_t Hash(const GrVkPipelineState::Desc& key) { return key.getChecksum(); } sk_sp fPipelineState; private: SK_DECLARE_INTERNAL_LLIST_INTERFACE(Entry); }; GrVkResourceProvider::PipelineStateCache::PipelineStateCache(GrVkGpu* gpu) : fCount(0) , fGpu(gpu) #ifdef GR_PIPELINE_STATE_CACHE_STATS , fTotalRequests(0) , fCacheMisses(0) #endif {} GrVkResourceProvider::PipelineStateCache::~PipelineStateCache() { SkASSERT(0 == fCount); // dump stats #ifdef GR_PIPELINE_STATE_CACHE_STATS if (c_DisplayVkPipelineCache) { SkDebugf("--- Pipeline State Cache ---\n"); SkDebugf("Total requests: %d\n", fTotalRequests); SkDebugf("Cache misses: %d\n", fCacheMisses); SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ? 100.f * fCacheMisses / fTotalRequests : 0.f); SkDebugf("---------------------\n"); } #endif } void GrVkResourceProvider::PipelineStateCache::reset() { fHashTable.foreach([](Entry** entry) { delete *entry; }); fHashTable.reset(); fCount = 0; } void GrVkResourceProvider::PipelineStateCache::abandon() { fHashTable.foreach([](Entry** entry) { SkASSERT((*entry)->fPipelineState.get()); (*entry)->fPipelineState->abandonGPUResources(); }); this->reset(); } void GrVkResourceProvider::PipelineStateCache::release() { fHashTable.foreach([this](Entry** entry) { SkASSERT((*entry)->fPipelineState.get()); (*entry)->fPipelineState->freeGPUResources(fGpu); }); this->reset(); } sk_sp GrVkResourceProvider::PipelineStateCache::refPipelineState( const GrPipeline& pipeline, const GrPrimitiveProcessor& primProc, GrPrimitiveType primitiveType, const GrVkRenderPass& renderPass) { #ifdef GR_PIPELINE_STATE_CACHE_STATS ++fTotalRequests; #endif GrStencilSettings stencil; if (pipeline.isStencilEnabled()) { GrRenderTarget* rt = pipeline.getRenderTarget(); // TODO: attach stencil and create settings during render target flush. SkASSERT(rt->renderTargetPriv().getStencilAttachment()); stencil.reset(*pipeline.getUserStencil(), pipeline.hasStencilClip(), rt->renderTargetPriv().numStencilBits()); } // Get GrVkProgramDesc GrVkPipelineState::Desc desc; if (!GrVkPipelineState::Desc::Build(&desc, primProc, pipeline, stencil, primitiveType, *fGpu->vkCaps().glslCaps())) { GrCapsDebugf(fGpu->caps(), "Failed to build vk program descriptor!\n"); return nullptr; } desc.finalize(); 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 pipelineState( GrVkPipelineStateBuilder::CreatePipelineState(fGpu, pipeline, stencil, 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; }