OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2016 Google Inc. |
| 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. |
| 6 */ |
| 7 |
| 8 #include "GrVkResourceProvider.h" |
| 9 |
| 10 #include "GrVkGpu.h" |
| 11 #include "GrProcessor.h" |
| 12 #include "GrVkPipelineState.h" |
| 13 #include "GrVkPipelineStateBuilder.h" |
| 14 #include "SkRTConf.h" |
| 15 #include "SkTSearch.h" |
| 16 #include "glsl/GrGLSLFragmentProcessor.h" |
| 17 #include "glsl/GrGLSLProgramDataManager.h" |
| 18 |
| 19 #ifdef PIPELINE_STATE_CACHE_STATS |
| 20 SK_CONF_DECLARE(bool, c_DisplayCache, "gpu.displayCache", false, |
| 21 "Display pipeline state cache usage."); |
| 22 #endif |
| 23 |
| 24 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; |
| 25 |
| 26 struct GrVkResourceProvider::PipelineStateCache::Entry { |
| 27 |
| 28 Entry() : fPipelineState(nullptr), fLRUStamp(0) {} |
| 29 |
| 30 SkAutoTUnref<GrVkPipelineState> fPipelineState; |
| 31 unsigned int fLRUStamp; |
| 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 } |
| 39 |
| 40 bool operator() (const Entry* entry, const GrVkPipelineState::Desc& desc) { |
| 41 SkASSERT(entry->fPipelineState.get()); |
| 42 return GrVkPipelineState::Desc::Less(entry->fPipelineState->getDesc(), d
esc); |
| 43 } |
| 44 }; |
| 45 |
| 46 GrVkResourceProvider::PipelineStateCache::PipelineStateCache(GrVkGpu* gpu) |
| 47 : fCount(0) |
| 48 , fCurrLRUStamp(0) |
| 49 , fGpu(gpu) |
| 50 #ifdef PIPELINE_STATE_CACHE_STATS |
| 51 , fTotalRequests(0) |
| 52 , fCacheMisses(0) |
| 53 , fHashMisses(0) |
| 54 #endif |
| 55 { |
| 56 for (int i = 0; i < 1 << kHashBits; ++i) { |
| 57 fHashTable[i] = nullptr; |
| 58 } |
| 59 } |
| 60 |
| 61 GrVkResourceProvider::PipelineStateCache::~PipelineStateCache() { |
| 62 SkASSERT(0 == fCount); |
| 63 // dump stats |
| 64 #ifdef PIPELINE_STATE_CACHE_STATS |
| 65 if (c_DisplayCache) { |
| 66 SkDebugf("--- Pipeline State Cache ---\n"); |
| 67 SkDebugf("Total requests: %d\n", fTotalRequests); |
| 68 SkDebugf("Cache misses: %d\n", fCacheMisses); |
| 69 SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ? |
| 70 100.f * fCacheMisses / fTotalRequests : |
| 71 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"); |
| 75 } |
| 76 #endif |
| 77 } |
| 78 |
| 79 void GrVkResourceProvider::PipelineStateCache::reset() { |
| 80 for (int i = 0; i < fCount; ++i) { |
| 81 delete fEntries[i]; |
| 82 fEntries[i] = nullptr; |
| 83 } |
| 84 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 } |
| 93 |
| 94 void GrVkResourceProvider::PipelineStateCache::abandon() { |
| 95 for (int i = 0; i < fCount; ++i) { |
| 96 SkASSERT(fEntries[i]->fPipelineState.get()); |
| 97 fEntries[i]->fPipelineState->abandonGPUResources(); |
| 98 } |
| 99 this->reset(); |
| 100 } |
| 101 |
| 102 void GrVkResourceProvider::PipelineStateCache::release() { |
| 103 for (int i = 0; i < fCount; ++i) { |
| 104 SkASSERT(fEntries[i]->fPipelineState.get()); |
| 105 fEntries[i]->fPipelineState->freeGPUResources(fGpu); |
| 106 } |
| 107 this->reset(); |
| 108 } |
| 109 |
| 110 int GrVkResourceProvider::PipelineStateCache::search(const GrVkPipelineState::De
sc& desc) const { |
| 111 PipelineDescLess less; |
| 112 return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less); |
| 113 } |
| 114 |
| 115 GrVkPipelineState* GrVkResourceProvider::PipelineStateCache::refPipelineState( |
| 116 const GrPipeline&
pipeline, |
| 117 const GrPrimitive
Processor& primProc, |
| 118 GrPrimitiveType p
rimiteType, |
| 119 const GrVkRenderP
ass& renderPass) { |
| 120 #ifdef PIPELINE_STATE_CACHE_STATS |
| 121 ++fTotalRequests; |
| 122 #endif |
| 123 |
| 124 Entry* entry = nullptr; |
| 125 |
| 126 // Get GrVkProgramDesc |
| 127 GrVkPipelineState::Desc desc; |
| 128 if (!GrVkProgramDescBuilder::Build(&desc.fProgramDesc, |
| 129 primProc, |
| 130 pipeline, |
| 131 *fGpu->vkCaps().glslCaps())) { |
| 132 GrCapsDebugf(fGpu->caps(), "Failed to build vk program descriptor!\n"); |
| 133 return false; |
| 134 } |
| 135 |
| 136 // Get vulkan specific descriptor key |
| 137 GrVkPipelineState::BuildStateKey(pipeline, primiteType, &desc.fStateKey); |
| 138 // Get checksum of entire PipelineDesc |
| 139 int keyLength = desc.fStateKey.count(); |
| 140 SkASSERT(0 == (keyLength % 4)); |
| 141 // 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, |
| 143 desc.fProgramDesc.getChecksum()); |
| 144 |
| 145 uint32_t hashIdx = desc.fChecksum; |
| 146 hashIdx ^= hashIdx >> 16; |
| 147 if (kHashBits <= 8) { |
| 148 hashIdx ^= hashIdx >> 8; |
| 149 } |
| 150 hashIdx &= ((1 << kHashBits) - 1); |
| 151 Entry* hashedEntry = fHashTable[hashIdx]; |
| 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; |
| 172 #endif |
| 173 GrVkPipelineState* pipelineState = |
| 174 GrVkPipelineStateBuilder::CreatePipelineState(fGpu, |
| 175 pipeline, |
| 176 primProc, |
| 177 primiteType, |
| 178 desc, |
| 179 renderPass); |
| 180 if (nullptr == pipelineState) { |
| 181 return nullptr; |
| 182 } |
| 183 int purgeIdx = 0; |
| 184 if (fCount < kMaxEntries) { |
| 185 entry = new Entry; |
| 186 purgeIdx = fCount++; |
| 187 fEntries[purgeIdx] = entry; |
| 188 } else { |
| 189 SkASSERT(fCount == kMaxEntries); |
| 190 purgeIdx = 0; |
| 191 for (int i = 1; i < kMaxEntries; ++i) { |
| 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); |
| 202 } |
| 203 SkASSERT(fEntries[purgeIdx] == entry); |
| 204 entry->fPipelineState.reset(pipelineState); |
| 205 // We need to shift fEntries around so that the entry currently at purge
Idx is placed |
| 206 // just before the entry at ~entryIdx (in order to keep fEntries sorted
by descriptor). |
| 207 entryIdx = ~entryIdx; |
| 208 if (entryIdx < purgeIdx) { |
| 209 // Let E and P be the entries at index entryIdx and purgeIdx, respe
ctively. |
| 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 } |
| 237 |
| 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 } |
OLD | NEW |