| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #include "GrVkResourceProvider.h" | 8 #include "GrVkResourceProvider.h" |
| 9 | 9 |
| 10 #include "GrVkGpu.h" | 10 #include "GrVkGpu.h" |
| 11 #include "GrProcessor.h" | 11 #include "GrProcessor.h" |
| 12 #include "GrVkPipelineState.h" | 12 #include "GrVkPipelineState.h" |
| 13 #include "GrVkPipelineStateBuilder.h" | 13 #include "GrVkPipelineStateBuilder.h" |
| 14 #include "SkRTConf.h" | 14 #include "SkRTConf.h" |
| 15 #include "SkTSearch.h" | |
| 16 #include "glsl/GrGLSLFragmentProcessor.h" | 15 #include "glsl/GrGLSLFragmentProcessor.h" |
| 17 #include "glsl/GrGLSLProgramDataManager.h" | 16 #include "glsl/GrGLSLProgramDataManager.h" |
| 18 | 17 |
| 19 #ifdef PIPELINE_STATE_CACHE_STATS | 18 #ifdef GR_PIPELINE_STATE_CACHE_STATS |
| 20 SK_CONF_DECLARE(bool, c_DisplayCache, "gpu.displayCache", false, | 19 SK_CONF_DECLARE(bool, c_DisplayVkPipelineCache, "gpu.displayyVkPipelineCache", f
alse, |
| 21 "Display pipeline state cache usage."); | 20 "Display pipeline state cache usage."); |
| 22 #endif | 21 #endif |
| 23 | 22 |
| 24 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; | |
| 25 | |
| 26 struct GrVkResourceProvider::PipelineStateCache::Entry { | 23 struct GrVkResourceProvider::PipelineStateCache::Entry { |
| 27 | 24 |
| 28 Entry() : fPipelineState(nullptr), fLRUStamp(0) {} | 25 Entry() : fPipelineState(nullptr) {} |
| 29 | 26 |
| 30 SkAutoTUnref<GrVkPipelineState> fPipelineState; | 27 static const GrVkPipelineState::Desc& GetKey(const Entry* entry) { |
| 31 unsigned int fLRUStamp; | 28 return entry->fPipelineState->getDesc(); |
| 32 }; | |
| 33 | |
| 34 struct GrVkResourceProvider::PipelineStateCache::PipelineDescLess { | |
| 35 bool operator() (const GrVkPipelineState::Desc& desc, const Entry* entry) { | |
| 36 SkASSERT(entry->fPipelineState.get()); | |
| 37 return GrVkPipelineState::Desc::Less(desc, entry->fPipelineState->getDes
c()); | |
| 38 } | 29 } |
| 39 | 30 |
| 40 bool operator() (const Entry* entry, const GrVkPipelineState::Desc& desc) { | 31 static uint32_t Hash(const GrVkPipelineState::Desc& key) { |
| 41 SkASSERT(entry->fPipelineState.get()); | 32 return key.fChecksum; |
| 42 return GrVkPipelineState::Desc::Less(entry->fPipelineState->getDesc(), d
esc); | |
| 43 } | 33 } |
| 34 |
| 35 sk_sp<GrVkPipelineState> fPipelineState; |
| 36 |
| 37 private: |
| 38 SK_DECLARE_INTERNAL_LLIST_INTERFACE(Entry); |
| 44 }; | 39 }; |
| 45 | 40 |
| 46 GrVkResourceProvider::PipelineStateCache::PipelineStateCache(GrVkGpu* gpu) | 41 GrVkResourceProvider::PipelineStateCache::PipelineStateCache(GrVkGpu* gpu) |
| 47 : fCount(0) | 42 : fCount(0) |
| 48 , fCurrLRUStamp(0) | |
| 49 , fGpu(gpu) | 43 , fGpu(gpu) |
| 50 #ifdef PIPELINE_STATE_CACHE_STATS | 44 #ifdef GR_PIPELINE_STATE_CACHE_STATS |
| 51 , fTotalRequests(0) | 45 , fTotalRequests(0) |
| 52 , fCacheMisses(0) | 46 , fCacheMisses(0) |
| 53 , fHashMisses(0) | |
| 54 #endif | 47 #endif |
| 55 { | 48 {} |
| 56 for (int i = 0; i < 1 << kHashBits; ++i) { | |
| 57 fHashTable[i] = nullptr; | |
| 58 } | |
| 59 } | |
| 60 | 49 |
| 61 GrVkResourceProvider::PipelineStateCache::~PipelineStateCache() { | 50 GrVkResourceProvider::PipelineStateCache::~PipelineStateCache() { |
| 62 SkASSERT(0 == fCount); | 51 SkASSERT(0 == fCount); |
| 63 // dump stats | 52 // dump stats |
| 64 #ifdef PIPELINE_STATE_CACHE_STATS | 53 #ifdef GR_PIPELINE_STATE_CACHE_STATS |
| 65 if (c_DisplayCache) { | 54 if (c_DisplayVkPipelineCache) { |
| 66 SkDebugf("--- Pipeline State Cache ---\n"); | 55 SkDebugf("--- Pipeline State Cache ---\n"); |
| 67 SkDebugf("Total requests: %d\n", fTotalRequests); | 56 SkDebugf("Total requests: %d\n", fTotalRequests); |
| 68 SkDebugf("Cache misses: %d\n", fCacheMisses); | 57 SkDebugf("Cache misses: %d\n", fCacheMisses); |
| 69 SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ? | 58 SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ? |
| 70 100.f * fCacheMisses / fTotalRequests : | 59 100.f * fCacheMisses / fTotalRequests : |
| 71 0.f); | 60 0.f); |
| 72 int cacheHits = fTotalRequests - fCacheMisses; | |
| 73 SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / c
acheHits : 0.f); | |
| 74 SkDebugf("---------------------\n"); | 61 SkDebugf("---------------------\n"); |
| 75 } | 62 } |
| 76 #endif | 63 #endif |
| 77 } | 64 } |
| 78 | 65 |
| 79 void GrVkResourceProvider::PipelineStateCache::reset() { | 66 void GrVkResourceProvider::PipelineStateCache::reset() { |
| 80 for (int i = 0; i < fCount; ++i) { | 67 fHashTable.foreach([](Entry** entry) { |
| 81 delete fEntries[i]; | 68 delete *entry; |
| 82 fEntries[i] = nullptr; | 69 }); |
| 83 } | 70 fHashTable.reset(); |
| 84 fCount = 0; | 71 fCount = 0; |
| 85 | |
| 86 // zero out hash table | |
| 87 for (int i = 0; i < 1 << kHashBits; i++) { | |
| 88 fHashTable[i] = nullptr; | |
| 89 } | |
| 90 | |
| 91 fCurrLRUStamp = 0; | |
| 92 } | 72 } |
| 93 | 73 |
| 94 void GrVkResourceProvider::PipelineStateCache::abandon() { | 74 void GrVkResourceProvider::PipelineStateCache::abandon() { |
| 95 for (int i = 0; i < fCount; ++i) { | 75 fHashTable.foreach([](Entry** entry) { |
| 96 SkASSERT(fEntries[i]->fPipelineState.get()); | 76 SkASSERT((*entry)->fPipelineState.get()); |
| 97 fEntries[i]->fPipelineState->abandonGPUResources(); | 77 (*entry)->fPipelineState->abandonGPUResources(); |
| 98 } | 78 }); |
| 79 |
| 99 this->reset(); | 80 this->reset(); |
| 100 } | 81 } |
| 101 | 82 |
| 102 void GrVkResourceProvider::PipelineStateCache::release() { | 83 void GrVkResourceProvider::PipelineStateCache::release() { |
| 103 for (int i = 0; i < fCount; ++i) { | 84 fHashTable.foreach([this](Entry** entry) { |
| 104 SkASSERT(fEntries[i]->fPipelineState.get()); | 85 SkASSERT((*entry)->fPipelineState.get()); |
| 105 fEntries[i]->fPipelineState->freeGPUResources(fGpu); | 86 (*entry)->fPipelineState->freeGPUResources(fGpu); |
| 106 } | 87 }); |
| 88 |
| 107 this->reset(); | 89 this->reset(); |
| 108 } | 90 } |
| 109 | 91 |
| 110 int GrVkResourceProvider::PipelineStateCache::search(const GrVkPipelineState::De
sc& desc) const { | 92 sk_sp<GrVkPipelineState> GrVkResourceProvider::PipelineStateCache::refPipelineSt
ate( |
| 111 PipelineDescLess less; | |
| 112 return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less); | |
| 113 } | |
| 114 | |
| 115 GrVkPipelineState* GrVkResourceProvider::PipelineStateCache::refPipelineState( | |
| 116 const GrPipeline&
pipeline, | 93 const GrPipeline&
pipeline, |
| 117 const GrPrimitive
Processor& primProc, | 94 const GrPrimitive
Processor& primProc, |
| 118 GrPrimitiveType p
rimiteType, | 95 GrPrimitiveType p
rimitiveType, |
| 119 const GrVkRenderP
ass& renderPass) { | 96 const GrVkRenderP
ass& renderPass) { |
| 120 #ifdef PIPELINE_STATE_CACHE_STATS | 97 #ifdef GR_PIPELINE_STATE_CACHE_STATS |
| 121 ++fTotalRequests; | 98 ++fTotalRequests; |
| 122 #endif | 99 #endif |
| 123 | |
| 124 Entry* entry = nullptr; | |
| 125 | |
| 126 // Get GrVkProgramDesc | 100 // Get GrVkProgramDesc |
| 127 GrVkPipelineState::Desc desc; | 101 GrVkPipelineState::Desc desc; |
| 128 if (!GrVkProgramDescBuilder::Build(&desc.fProgramDesc, | 102 if (!GrVkProgramDescBuilder::Build(&desc.fProgramDesc, |
| 129 primProc, | 103 primProc, |
| 130 pipeline, | 104 pipeline, |
| 131 *fGpu->vkCaps().glslCaps())) { | 105 *fGpu->vkCaps().glslCaps())) { |
| 132 GrCapsDebugf(fGpu->caps(), "Failed to build vk program descriptor!\n"); | 106 GrCapsDebugf(fGpu->caps(), "Failed to build vk program descriptor!\n"); |
| 133 return false; | 107 return false; |
| 134 } | 108 } |
| 135 | 109 |
| 136 // Get vulkan specific descriptor key | 110 // Get vulkan specific descriptor key |
| 137 GrVkPipelineState::BuildStateKey(pipeline, primiteType, &desc.fStateKey); | 111 GrVkPipelineState::BuildStateKey(pipeline, primitiveType, &desc.fStateKey); |
| 138 // Get checksum of entire PipelineDesc | 112 // Get checksum of entire PipelineDesc |
| 139 int keyLength = desc.fStateKey.count(); | 113 int keyLength = desc.fStateKey.count(); |
| 140 SkASSERT(0 == (keyLength % 4)); | 114 SkASSERT(0 == (keyLength % 4)); |
| 141 // Seed the checksum with the checksum of the programDesc then add the vulka
n key to it. | 115 // Seed the checksum with the checksum of the programDesc then add the vulka
n key to it. |
| 142 desc.fChecksum = SkChecksum::Murmur3(desc.fStateKey.begin(), keyLength, | 116 desc.fChecksum = SkChecksum::Murmur3(desc.fStateKey.begin(), keyLength, |
| 143 desc.fProgramDesc.getChecksum()); | 117 desc.fProgramDesc.getChecksum()); |
| 144 | 118 |
| 145 uint32_t hashIdx = desc.fChecksum; | 119 Entry* entry = nullptr; |
| 146 hashIdx ^= hashIdx >> 16; | 120 if (Entry** entryptr = fHashTable.find(desc)) { |
| 147 if (kHashBits <= 8) { | 121 SkASSERT(*entryptr); |
| 148 hashIdx ^= hashIdx >> 8; | 122 entry = *entryptr; |
| 149 } | 123 } |
| 150 hashIdx &= ((1 << kHashBits) - 1); | 124 if (!entry) { |
| 151 Entry* hashedEntry = fHashTable[hashIdx]; | 125 #ifdef GR_PIPELINE_STATE_CACHE_STATS |
| 152 if (hashedEntry && hashedEntry->fPipelineState->getDesc() == desc) { | |
| 153 SkASSERT(hashedEntry->fPipelineState); | |
| 154 entry = hashedEntry; | |
| 155 } | |
| 156 | |
| 157 int entryIdx; | |
| 158 if (nullptr == entry) { | |
| 159 entryIdx = this->search(desc); | |
| 160 if (entryIdx >= 0) { | |
| 161 entry = fEntries[entryIdx]; | |
| 162 #ifdef PIPELINE_STATE_CACHE_STATS | |
| 163 ++fHashMisses; | |
| 164 #endif | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 if (nullptr == entry) { | |
| 169 // We have a cache miss | |
| 170 #ifdef PIPELINE_STATE_CACHE_STATS | |
| 171 ++fCacheMisses; | 126 ++fCacheMisses; |
| 172 #endif | 127 #endif |
| 173 GrVkPipelineState* pipelineState = | 128 sk_sp<GrVkPipelineState> pipelineState( |
| 174 GrVkPipelineStateBuilder::CreatePipelineState(fGpu, | 129 GrVkPipelineStateBuilder::CreatePipelineState(fGpu, |
| 175 pipeline, | 130 pipeline, |
| 176 primProc, | 131 primProc, |
| 177 primiteType, | 132 primitiveType, |
| 178 desc, | 133 desc, |
| 179 renderPass); | 134 renderPass)); |
| 180 if (nullptr == pipelineState) { | 135 if (nullptr == pipelineState) { |
| 181 return nullptr; | 136 return nullptr; |
| 182 } | 137 } |
| 183 int purgeIdx = 0; | |
| 184 if (fCount < kMaxEntries) { | 138 if (fCount < kMaxEntries) { |
| 185 entry = new Entry; | 139 entry = new Entry; |
| 186 purgeIdx = fCount++; | 140 fCount++; |
| 187 fEntries[purgeIdx] = entry; | |
| 188 } else { | 141 } else { |
| 189 SkASSERT(fCount == kMaxEntries); | 142 SkASSERT(fCount == kMaxEntries); |
| 190 purgeIdx = 0; | 143 entry = fLRUList.head(); |
| 191 for (int i = 1; i < kMaxEntries; ++i) { | 144 fLRUList.remove(entry); |
| 192 if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) { | |
| 193 purgeIdx = i; | |
| 194 } | |
| 195 } | |
| 196 entry = fEntries[purgeIdx]; | |
| 197 int purgedHashIdx = entry->fPipelineState->getDesc().fChecksum & ((1
<< kHashBits) - 1); | |
| 198 if (fHashTable[purgedHashIdx] == entry) { | |
| 199 fHashTable[purgedHashIdx] = nullptr; | |
| 200 } | |
| 201 entry->fPipelineState->freeGPUResources(fGpu); | 145 entry->fPipelineState->freeGPUResources(fGpu); |
| 146 fHashTable.remove(entry->fPipelineState->getDesc()); |
| 202 } | 147 } |
| 203 SkASSERT(fEntries[purgeIdx] == entry); | 148 entry->fPipelineState = std::move(pipelineState); |
| 204 entry->fPipelineState.reset(pipelineState); | 149 fHashTable.set(entry); |
| 205 // We need to shift fEntries around so that the entry currently at purge
Idx is placed | 150 fLRUList.addToTail(entry); |
| 206 // just before the entry at ~entryIdx (in order to keep fEntries sorted
by descriptor). | 151 return entry->fPipelineState; |
| 207 entryIdx = ~entryIdx; | 152 } else { |
| 208 if (entryIdx < purgeIdx) { | 153 fLRUList.remove(entry); |
| 209 // Let E and P be the entries at index entryIdx and purgeIdx, respe
ctively. | 154 fLRUList.addToTail(entry); |
| 210 // If the entries array looks like this: | |
| 211 // aaaaEbbbbbPccccc | |
| 212 // we rearrange it to look like this: | |
| 213 // aaaaPEbbbbbccccc | |
| 214 size_t copySize = (purgeIdx - entryIdx) * sizeof(Entry*); | |
| 215 memmove(fEntries + entryIdx + 1, fEntries + entryIdx, copySize); | |
| 216 fEntries[entryIdx] = entry; | |
| 217 } else if (purgeIdx < entryIdx) { | |
| 218 // If the entries array looks like this: | |
| 219 // aaaaPbbbbbEccccc | |
| 220 // we rearrange it to look like this: | |
| 221 // aaaabbbbbPEccccc | |
| 222 size_t copySize = (entryIdx - purgeIdx - 1) * sizeof(Entry*); | |
| 223 memmove(fEntries + purgeIdx, fEntries + purgeIdx + 1, copySize); | |
| 224 fEntries[entryIdx - 1] = entry; | |
| 225 } | |
| 226 #ifdef SK_DEBUG | |
| 227 SkASSERT(fEntries[0]->fPipelineState.get()); | |
| 228 for (int i = 0; i < fCount - 1; ++i) { | |
| 229 SkASSERT(fEntries[i + 1]->fPipelineState.get()); | |
| 230 const GrVkPipelineState::Desc& a = fEntries[i]->fPipelineState->getD
esc(); | |
| 231 const GrVkPipelineState::Desc& b = fEntries[i + 1]->fPipelineState->
getDesc(); | |
| 232 SkASSERT(GrVkPipelineState::Desc::Less(a, b)); | |
| 233 SkASSERT(!GrVkPipelineState::Desc::Less(b, a)); | |
| 234 } | |
| 235 #endif | |
| 236 } | 155 } |
| 237 | 156 return entry->fPipelineState; |
| 238 fHashTable[hashIdx] = entry; | |
| 239 entry->fLRUStamp = fCurrLRUStamp; | |
| 240 | |
| 241 if (SK_MaxU32 == fCurrLRUStamp) { | |
| 242 // wrap around! just trash our LRU, one time hit. | |
| 243 for (int i = 0; i < fCount; ++i) { | |
| 244 fEntries[i]->fLRUStamp = 0; | |
| 245 } | |
| 246 } | |
| 247 ++fCurrLRUStamp; | |
| 248 return SkRef(entry->fPipelineState.get()); | |
| 249 } | 157 } |
| OLD | NEW |