Index: src/gpu/vk/GrVkPipelineStateCache.cpp |
diff --git a/src/gpu/vk/GrVkPipelineStateCache.cpp b/src/gpu/vk/GrVkPipelineStateCache.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f2092af981c91b73ba5f9593ac0535f8b86d37ad |
--- /dev/null |
+++ b/src/gpu/vk/GrVkPipelineStateCache.cpp |
@@ -0,0 +1,249 @@ |
+/* |
+ * 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 "GrVkPipelineState.h" |
+#include "GrVkPipelineStateBuilder.h" |
+#include "SkRTConf.h" |
+#include "SkTSearch.h" |
+#include "glsl/GrGLSLFragmentProcessor.h" |
+#include "glsl/GrGLSLProgramDataManager.h" |
+ |
+#ifdef PIPELINE_STATE_CACHE_STATS |
+SK_CONF_DECLARE(bool, c_DisplayCache, "gpu.displayCache", false, |
+ "Display pipeline state cache usage."); |
+#endif |
+ |
+typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; |
+ |
+struct GrVkResourceProvider::PipelineStateCache::Entry { |
+ |
+ Entry() : fPipelineState(nullptr), fLRUStamp(0) {} |
+ |
+ SkAutoTUnref<GrVkPipelineState> fPipelineState; |
+ unsigned int fLRUStamp; |
+}; |
+ |
+struct GrVkResourceProvider::PipelineStateCache::PipelineDescLess { |
+ bool operator() (const GrVkPipelineState::Desc& desc, const Entry* entry) { |
+ SkASSERT(entry->fPipelineState.get()); |
+ return GrVkPipelineState::Desc::Less(desc, entry->fPipelineState->getDesc()); |
+ } |
+ |
+ bool operator() (const Entry* entry, const GrVkPipelineState::Desc& desc) { |
+ SkASSERT(entry->fPipelineState.get()); |
+ return GrVkPipelineState::Desc::Less(entry->fPipelineState->getDesc(), desc); |
+ } |
+}; |
+ |
+GrVkResourceProvider::PipelineStateCache::PipelineStateCache(GrVkGpu* gpu) |
+ : fCount(0) |
+ , fCurrLRUStamp(0) |
+ , fGpu(gpu) |
+#ifdef PIPELINE_STATE_CACHE_STATS |
+ , fTotalRequests(0) |
+ , fCacheMisses(0) |
+ , fHashMisses(0) |
+#endif |
+{ |
+ for (int i = 0; i < 1 << kHashBits; ++i) { |
+ fHashTable[i] = nullptr; |
+ } |
+} |
+ |
+GrVkResourceProvider::PipelineStateCache::~PipelineStateCache() { |
+ SkASSERT(0 == fCount); |
+ // dump stats |
+#ifdef PIPELINE_STATE_CACHE_STATS |
+ if (c_DisplayCache) { |
+ 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); |
+ int cacheHits = fTotalRequests - fCacheMisses; |
+ SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / cacheHits : 0.f); |
+ SkDebugf("---------------------\n"); |
+ } |
+#endif |
+} |
+ |
+void GrVkResourceProvider::PipelineStateCache::reset() { |
+ for (int i = 0; i < fCount; ++i) { |
+ delete fEntries[i]; |
+ fEntries[i] = nullptr; |
+ } |
+ fCount = 0; |
+ |
+ // zero out hash table |
+ for (int i = 0; i < 1 << kHashBits; i++) { |
+ fHashTable[i] = nullptr; |
+ } |
+ |
+ fCurrLRUStamp = 0; |
+} |
+ |
+void GrVkResourceProvider::PipelineStateCache::abandon() { |
+ for (int i = 0; i < fCount; ++i) { |
+ SkASSERT(fEntries[i]->fPipelineState.get()); |
+ fEntries[i]->fPipelineState->abandonGPUResources(); |
+ } |
+ this->reset(); |
+} |
+ |
+void GrVkResourceProvider::PipelineStateCache::release() { |
+ for (int i = 0; i < fCount; ++i) { |
+ SkASSERT(fEntries[i]->fPipelineState.get()); |
+ fEntries[i]->fPipelineState->freeGPUResources(fGpu); |
+ } |
+ this->reset(); |
+} |
+ |
+int GrVkResourceProvider::PipelineStateCache::search(const GrVkPipelineState::Desc& desc) const { |
+ PipelineDescLess less; |
+ return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less); |
+} |
+ |
+GrVkPipelineState* GrVkResourceProvider::PipelineStateCache::refPipelineState( |
+ const GrPipeline& pipeline, |
+ const GrPrimitiveProcessor& primProc, |
+ GrPrimitiveType primiteType, |
+ const GrVkRenderPass& renderPass) { |
+#ifdef PIPELINE_STATE_CACHE_STATS |
+ ++fTotalRequests; |
+#endif |
+ |
+ Entry* entry = nullptr; |
+ |
+ // 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, primiteType, &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()); |
+ |
+ uint32_t hashIdx = desc.fChecksum; |
+ hashIdx ^= hashIdx >> 16; |
+ if (kHashBits <= 8) { |
+ hashIdx ^= hashIdx >> 8; |
+ } |
+ hashIdx &= ((1 << kHashBits) - 1); |
+ Entry* hashedEntry = fHashTable[hashIdx]; |
+ if (hashedEntry && hashedEntry->fPipelineState->getDesc() == desc) { |
+ SkASSERT(hashedEntry->fPipelineState); |
+ entry = hashedEntry; |
+ } |
+ |
+ int entryIdx; |
+ if (nullptr == entry) { |
+ entryIdx = this->search(desc); |
+ if (entryIdx >= 0) { |
+ entry = fEntries[entryIdx]; |
+#ifdef PIPELINE_STATE_CACHE_STATS |
+ ++fHashMisses; |
+#endif |
+ } |
+ } |
+ |
+ if (nullptr == entry) { |
+ // We have a cache miss |
+#ifdef PIPELINE_STATE_CACHE_STATS |
+ ++fCacheMisses; |
+#endif |
+ GrVkPipelineState* pipelineState = |
+ GrVkPipelineStateBuilder::CreatePipelineState(fGpu, |
+ pipeline, |
+ primProc, |
+ primiteType, |
+ desc, |
+ renderPass); |
+ if (nullptr == pipelineState) { |
+ return nullptr; |
+ } |
+ int purgeIdx = 0; |
+ if (fCount < kMaxEntries) { |
+ entry = new Entry; |
+ purgeIdx = fCount++; |
+ fEntries[purgeIdx] = entry; |
+ } else { |
+ SkASSERT(fCount == kMaxEntries); |
+ purgeIdx = 0; |
+ for (int i = 1; i < kMaxEntries; ++i) { |
+ if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) { |
+ purgeIdx = i; |
+ } |
+ } |
+ entry = fEntries[purgeIdx]; |
+ int purgedHashIdx = entry->fPipelineState->getDesc().fChecksum & ((1 << kHashBits) - 1); |
+ if (fHashTable[purgedHashIdx] == entry) { |
+ fHashTable[purgedHashIdx] = nullptr; |
+ } |
+ entry->fPipelineState->freeGPUResources(fGpu); |
+ } |
+ SkASSERT(fEntries[purgeIdx] == entry); |
+ entry->fPipelineState.reset(pipelineState); |
+ // We need to shift fEntries around so that the entry currently at purgeIdx is placed |
+ // just before the entry at ~entryIdx (in order to keep fEntries sorted by descriptor). |
+ entryIdx = ~entryIdx; |
+ if (entryIdx < purgeIdx) { |
+ // Let E and P be the entries at index entryIdx and purgeIdx, respectively. |
+ // If the entries array looks like this: |
+ // aaaaEbbbbbPccccc |
+ // we rearrange it to look like this: |
+ // aaaaPEbbbbbccccc |
+ size_t copySize = (purgeIdx - entryIdx) * sizeof(Entry*); |
+ memmove(fEntries + entryIdx + 1, fEntries + entryIdx, copySize); |
+ fEntries[entryIdx] = entry; |
+ } else if (purgeIdx < entryIdx) { |
+ // If the entries array looks like this: |
+ // aaaaPbbbbbEccccc |
+ // we rearrange it to look like this: |
+ // aaaabbbbbPEccccc |
+ size_t copySize = (entryIdx - purgeIdx - 1) * sizeof(Entry*); |
+ memmove(fEntries + purgeIdx, fEntries + purgeIdx + 1, copySize); |
+ fEntries[entryIdx - 1] = entry; |
+ } |
+#ifdef SK_DEBUG |
+ SkASSERT(fEntries[0]->fPipelineState.get()); |
+ for (int i = 0; i < fCount - 1; ++i) { |
+ SkASSERT(fEntries[i + 1]->fPipelineState.get()); |
+ const GrVkPipelineState::Desc& a = fEntries[i]->fPipelineState->getDesc(); |
+ const GrVkPipelineState::Desc& b = fEntries[i + 1]->fPipelineState->getDesc(); |
+ SkASSERT(GrVkPipelineState::Desc::Less(a, b)); |
+ SkASSERT(!GrVkPipelineState::Desc::Less(b, a)); |
+ } |
+#endif |
+ } |
+ |
+ fHashTable[hashIdx] = entry; |
+ entry->fLRUStamp = fCurrLRUStamp; |
+ |
+ if (SK_MaxU32 == fCurrLRUStamp) { |
+ // wrap around! just trash our LRU, one time hit. |
+ for (int i = 0; i < fCount; ++i) { |
+ fEntries[i]->fLRUStamp = 0; |
+ } |
+ } |
+ ++fCurrLRUStamp; |
+ return SkRef(entry->fPipelineState.get()); |
+} |