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 |