|
1 /* |
|
2 * Copyright 2011 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 "GrGpuGL.h" |
|
9 |
|
10 #include "GrEffect.h" |
|
11 #include "GrGLEffect.h" |
|
12 #include "SkRTConf.h" |
|
13 #include "SkTSearch.h" |
|
14 |
|
15 #ifdef PROGRAM_CACHE_STATS |
|
16 SK_CONF_DECLARE(bool, c_DisplayCache, "gpu.displayCache", false, |
|
17 "Display program cache usage."); |
|
18 #endif |
|
19 |
|
20 typedef GrGLUniformManager::UniformHandle UniformHandle; |
|
21 |
|
22 struct GrGpuGL::ProgramCache::Entry { |
|
23 SK_DECLARE_INST_COUNT_ROOT(Entry); |
|
24 Entry() : fProgram(NULL), fLRUStamp(0) {} |
|
25 |
|
26 SkAutoTUnref<GrGLProgram> fProgram; |
|
27 unsigned int fLRUStamp; |
|
28 }; |
|
29 |
|
30 struct GrGpuGL::ProgramCache::ProgDescLess { |
|
31 bool operator() (const GrGLProgramDesc& desc, const Entry* entry) { |
|
32 SkASSERT(NULL != entry->fProgram.get()); |
|
33 return GrGLProgramDesc::Less(desc, entry->fProgram->getDesc()); |
|
34 } |
|
35 |
|
36 bool operator() (const Entry* entry, const GrGLProgramDesc& desc) { |
|
37 SkASSERT(NULL != entry->fProgram.get()); |
|
38 return GrGLProgramDesc::Less(entry->fProgram->getDesc(), desc); |
|
39 } |
|
40 }; |
|
41 |
|
42 GrGpuGL::ProgramCache::ProgramCache(GrGpuGL* gpu) |
|
43 : fCount(0) |
|
44 , fCurrLRUStamp(0) |
|
45 , fGpu(gpu) |
|
46 #ifdef PROGRAM_CACHE_STATS |
|
47 , fTotalRequests(0) |
|
48 , fCacheMisses(0) |
|
49 , fHashMisses(0) |
|
50 #endif |
|
51 { |
|
52 for (int i = 0; i < 1 << kHashBits; ++i) { |
|
53 fHashTable[i] = NULL; |
|
54 } |
|
55 } |
|
56 |
|
57 GrGpuGL::ProgramCache::~ProgramCache() { |
|
58 for (int i = 0; i < fCount; ++i){ |
|
59 SkDELETE(fEntries[i]); |
|
60 } |
|
61 // dump stats |
|
62 #ifdef PROGRAM_CACHE_STATS |
|
63 if (c_DisplayCache) { |
|
64 SkDebugf("--- Program Cache ---\n"); |
|
65 SkDebugf("Total requests: %d\n", fTotalRequests); |
|
66 SkDebugf("Cache misses: %d\n", fCacheMisses); |
|
67 SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ? |
|
68 100.f * fCacheMisses / fTotalRequests : |
|
69 0.f); |
|
70 int cacheHits = fTotalRequests - fCacheMisses; |
|
71 SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / cacheHits : 0.f); |
|
72 SkDebugf("---------------------\n"); |
|
73 } |
|
74 #endif |
|
75 } |
|
76 |
|
77 void GrGpuGL::ProgramCache::abandon() { |
|
78 for (int i = 0; i < fCount; ++i) { |
|
79 SkASSERT(NULL != fEntries[i]->fProgram.get()); |
|
80 fEntries[i]->fProgram->abandon(); |
|
81 fEntries[i]->fProgram.reset(NULL); |
|
82 } |
|
83 fCount = 0; |
|
84 } |
|
85 |
|
86 int GrGpuGL::ProgramCache::search(const GrGLProgramDesc& desc) const { |
|
87 ProgDescLess less; |
|
88 return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less); |
|
89 } |
|
90 |
|
91 GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrGLProgramDesc& desc, |
|
92 const GrEffectStage* colorStages[], |
|
93 const GrEffectStage* coverageStages[]) { |
|
94 #ifdef PROGRAM_CACHE_STATS |
|
95 ++fTotalRequests; |
|
96 #endif |
|
97 |
|
98 Entry* entry = NULL; |
|
99 |
|
100 uint32_t hashIdx = desc.getChecksum(); |
|
101 hashIdx ^= hashIdx >> 16; |
|
102 if (kHashBits <= 8) { |
|
103 hashIdx ^= hashIdx >> 8; |
|
104 } |
|
105 hashIdx &=((1 << kHashBits) - 1); |
|
106 Entry* hashedEntry = fHashTable[hashIdx]; |
|
107 if (NULL != hashedEntry && hashedEntry->fProgram->getDesc() == desc) { |
|
108 SkASSERT(NULL != hashedEntry->fProgram); |
|
109 entry = hashedEntry; |
|
110 } |
|
111 |
|
112 int entryIdx; |
|
113 if (NULL == entry) { |
|
114 entryIdx = this->search(desc); |
|
115 if (entryIdx >= 0) { |
|
116 entry = fEntries[entryIdx]; |
|
117 #ifdef PROGRAM_CACHE_STATS |
|
118 ++fHashMisses; |
|
119 #endif |
|
120 } |
|
121 } |
|
122 |
|
123 if (NULL == entry) { |
|
124 // We have a cache miss |
|
125 #ifdef PROGRAM_CACHE_STATS |
|
126 ++fCacheMisses; |
|
127 #endif |
|
128 GrGLProgram* program = GrGLProgram::Create(fGpu, desc, colorStages, coverageStages); |
|
129 if (NULL == program) { |
|
130 return NULL; |
|
131 } |
|
132 int purgeIdx = 0; |
|
133 if (fCount < kMaxEntries) { |
|
134 entry = SkNEW(Entry); |
|
135 purgeIdx = fCount++; |
|
136 fEntries[purgeIdx] = entry; |
|
137 } else { |
|
138 SkASSERT(fCount == kMaxEntries); |
|
139 purgeIdx = 0; |
|
140 for (int i = 1; i < kMaxEntries; ++i) { |
|
141 if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) { |
|
142 purgeIdx = i; |
|
143 } |
|
144 } |
|
145 entry = fEntries[purgeIdx]; |
|
146 int purgedHashIdx = entry->fProgram->getDesc().getChecksum() & ((1 << kHashBits) - 1); |
|
147 if (fHashTable[purgedHashIdx] == entry) { |
|
148 fHashTable[purgedHashIdx] = NULL; |
|
149 } |
|
150 } |
|
151 SkASSERT(fEntries[purgeIdx] == entry); |
|
152 entry->fProgram.reset(program); |
|
153 // We need to shift fEntries around so that the entry currently at purgeIdx is placed |
|
154 // just before the entry at ~entryIdx (in order to keep fEntries sorted by descriptor). |
|
155 entryIdx = ~entryIdx; |
|
156 if (entryIdx < purgeIdx) { |
|
157 // Let E and P be the entries at index entryIdx and purgeIdx, respectively. |
|
158 // If the entries array looks like this: |
|
159 // aaaaEbbbbbPccccc |
|
160 // we rearrange it to look like this: |
|
161 // aaaaPEbbbbbccccc |
|
162 size_t copySize = (purgeIdx - entryIdx) * sizeof(Entry*); |
|
163 memmove(fEntries + entryIdx + 1, fEntries + entryIdx, copySize); |
|
164 fEntries[entryIdx] = entry; |
|
165 } else if (purgeIdx < entryIdx) { |
|
166 // If the entries array looks like this: |
|
167 // aaaaPbbbbbEccccc |
|
168 // we rearrange it to look like this: |
|
169 // aaaabbbbbPEccccc |
|
170 size_t copySize = (entryIdx - purgeIdx - 1) * sizeof(Entry*); |
|
171 memmove(fEntries + purgeIdx, fEntries + purgeIdx + 1, copySize); |
|
172 fEntries[entryIdx - 1] = entry; |
|
173 } |
|
174 #ifdef SK_DEBUG |
|
175 SkASSERT(NULL != fEntries[0]->fProgram.get()); |
|
176 for (int i = 0; i < fCount - 1; ++i) { |
|
177 SkASSERT(NULL != fEntries[i + 1]->fProgram.get()); |
|
178 const GrGLProgramDesc& a = fEntries[i]->fProgram->getDesc(); |
|
179 const GrGLProgramDesc& b = fEntries[i + 1]->fProgram->getDesc(); |
|
180 SkASSERT(GrGLProgramDesc::Less(a, b)); |
|
181 SkASSERT(!GrGLProgramDesc::Less(b, a)); |
|
182 } |
|
183 #endif |
|
184 } |
|
185 |
|
186 fHashTable[hashIdx] = entry; |
|
187 entry->fLRUStamp = fCurrLRUStamp; |
|
188 |
|
189 if (SK_MaxU32 == fCurrLRUStamp) { |
|
190 // wrap around! just trash our LRU, one time hit. |
|
191 for (int i = 0; i < fCount; ++i) { |
|
192 fEntries[i]->fLRUStamp = 0; |
|
193 } |
|
194 } |
|
195 ++fCurrLRUStamp; |
|
196 return entry->fProgram; |
|
197 } |
|
198 |
|
199 //////////////////////////////////////////////////////////////////////////////// |
|
200 |
|
201 void GrGpuGL::abandonResources(){ |
|
202 INHERITED::abandonResources(); |
|
203 fProgramCache->abandon(); |
|
204 fHWProgramID = 0; |
|
205 } |
|
206 |
|
207 //////////////////////////////////////////////////////////////////////////////// |
|
208 |
|
209 #define GL_CALL(X) GR_GL_CALL(this->glInterface(), X) |
|
210 |
|
211 bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstCopy) { |
|
212 const GrDrawState& drawState = this->getDrawState(); |
|
213 |
|
214 // GrGpu::setupClipAndFlushState should have already checked this and bailed if not true. |
|
215 SkASSERT(NULL != drawState.getRenderTarget()); |
|
216 |
|
217 if (kStencilPath_DrawType == type) { |
|
218 const GrRenderTarget* rt = this->getDrawState().getRenderTarget(); |
|
219 SkISize size; |
|
220 size.set(rt->width(), rt->height()); |
|
221 this->setProjectionMatrix(drawState.getViewMatrix(), size, rt->origin()); |
|
222 } else { |
|
223 this->flushMiscFixedFunctionState(); |
|
224 |
|
225 GrBlendCoeff srcCoeff; |
|
226 GrBlendCoeff dstCoeff; |
|
227 GrDrawState::BlendOptFlags blendOpts = drawState.getBlendOpts(false, &srcCoeff, &dstCoeff); |
|
228 if (GrDrawState::kSkipDraw_BlendOptFlag & blendOpts) { |
|
229 return false; |
|
230 } |
|
231 |
|
232 SkSTArray<8, const GrEffectStage*, true> colorStages; |
|
233 SkSTArray<8, const GrEffectStage*, true> coverageStages; |
|
234 GrGLProgramDesc desc; |
|
235 GrGLProgramDesc::Build(this->getDrawState(), |
|
236 kDrawPoints_DrawType == type, |
|
237 blendOpts, |
|
238 srcCoeff, |
|
239 dstCoeff, |
|
240 this, |
|
241 dstCopy, |
|
242 &colorStages, |
|
243 &coverageStages, |
|
244 &desc); |
|
245 |
|
246 fCurrentProgram.reset(fProgramCache->getProgram(desc, |
|
247 colorStages.begin(), |
|
248 coverageStages.begin())); |
|
249 if (NULL == fCurrentProgram.get()) { |
|
250 SkDEBUGFAIL("Failed to create program!"); |
|
251 return false; |
|
252 } |
|
253 |
|
254 SkASSERT(kDrawPath_DrawType != type || !fCurrentProgram->hasVertexShader()); |
|
255 |
|
256 fCurrentProgram.get()->ref(); |
|
257 |
|
258 GrGLuint programID = fCurrentProgram->programID(); |
|
259 if (fHWProgramID != programID) { |
|
260 GL_CALL(UseProgram(programID)); |
|
261 fHWProgramID = programID; |
|
262 } |
|
263 |
|
264 fCurrentProgram->overrideBlend(&srcCoeff, &dstCoeff); |
|
265 this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff); |
|
266 |
|
267 fCurrentProgram->setData(blendOpts, |
|
268 colorStages.begin(), |
|
269 coverageStages.begin(), |
|
270 dstCopy, |
|
271 &fSharedGLProgramState); |
|
272 } |
|
273 this->flushStencil(type); |
|
274 this->flushScissor(); |
|
275 this->flushAAState(type); |
|
276 |
|
277 SkIRect* devRect = NULL; |
|
278 SkIRect devClipBounds; |
|
279 if (drawState.isClipState()) { |
|
280 this->getClip()->getConservativeBounds(drawState.getRenderTarget(), &devClipBounds); |
|
281 devRect = &devClipBounds; |
|
282 } |
|
283 // This must come after textures are flushed because a texture may need |
|
284 // to be msaa-resolved (which will modify bound FBO state). |
|
285 this->flushRenderTarget(devRect); |
|
286 |
|
287 return true; |
|
288 } |
|
289 |
|
290 void GrGpuGL::setupGeometry(const DrawInfo& info, size_t* indexOffsetInBytes) { |
|
291 |
|
292 GrGLsizei stride = static_cast<GrGLsizei>(this->getDrawState().getVertexSize()); |
|
293 |
|
294 size_t vertexOffsetInBytes = stride * info.startVertex(); |
|
295 |
|
296 const GeometryPoolState& geoPoolState = this->getGeomPoolState(); |
|
297 |
|
298 GrGLVertexBuffer* vbuf; |
|
299 switch (this->getGeomSrc().fVertexSrc) { |
|
300 case kBuffer_GeometrySrcType: |
|
301 vbuf = (GrGLVertexBuffer*) this->getGeomSrc().fVertexBuffer; |
|
302 break; |
|
303 case kArray_GeometrySrcType: |
|
304 case kReserved_GeometrySrcType: |
|
305 this->finalizeReservedVertices(); |
|
306 vertexOffsetInBytes += geoPoolState.fPoolStartVertex * this->getGeomSrc().fVertexSize; |
|
307 vbuf = (GrGLVertexBuffer*) geoPoolState.fPoolVertexBuffer; |
|
308 break; |
|
309 default: |
|
310 vbuf = NULL; // suppress warning |
|
311 GrCrash("Unknown geometry src type!"); |
|
312 } |
|
313 |
|
314 SkASSERT(NULL != vbuf); |
|
315 SkASSERT(!vbuf->isLocked()); |
|
316 vertexOffsetInBytes += vbuf->baseOffset(); |
|
317 |
|
318 GrGLIndexBuffer* ibuf = NULL; |
|
319 if (info.isIndexed()) { |
|
320 SkASSERT(NULL != indexOffsetInBytes); |
|
321 |
|
322 switch (this->getGeomSrc().fIndexSrc) { |
|
323 case kBuffer_GeometrySrcType: |
|
324 *indexOffsetInBytes = 0; |
|
325 ibuf = (GrGLIndexBuffer*)this->getGeomSrc().fIndexBuffer; |
|
326 break; |
|
327 case kArray_GeometrySrcType: |
|
328 case kReserved_GeometrySrcType: |
|
329 this->finalizeReservedIndices(); |
|
330 *indexOffsetInBytes = geoPoolState.fPoolStartIndex * sizeof(GrGLushort); |
|
331 ibuf = (GrGLIndexBuffer*) geoPoolState.fPoolIndexBuffer; |
|
332 break; |
|
333 default: |
|
334 ibuf = NULL; // suppress warning |
|
335 GrCrash("Unknown geometry src type!"); |
|
336 } |
|
337 |
|
338 SkASSERT(NULL != ibuf); |
|
339 SkASSERT(!ibuf->isLocked()); |
|
340 *indexOffsetInBytes += ibuf->baseOffset(); |
|
341 } |
|
342 GrGLAttribArrayState* attribState = |
|
343 fHWGeometryState.bindArrayAndBuffersToDraw(this, vbuf, ibuf); |
|
344 |
|
345 if (!fCurrentProgram->hasVertexShader()) { |
|
346 int posIdx = this->getDrawState().positionAttributeIndex(); |
|
347 const GrVertexAttrib* vertexAttrib = this->getDrawState().getVertexAttribs() + posIdx; |
|
348 GrVertexAttribType attribType = vertexAttrib->fType; |
|
349 SkASSERT(!GrGLAttribTypeToLayout(attribType).fNormalized); |
|
350 SkASSERT(GrGLAttribTypeToLayout(attribType).fCount == 2); |
|
351 |
|
352 // Attrib at location 0 is defined to be bound to vertex in fixed-function pipe. Asserts |
|
353 // above should make sure position attribute goes to location 0 when below code is executed. |
|
354 |
|
355 attribState->set(this, |
|
356 0, |
|
357 vbuf, |
|
358 GrGLAttribTypeToLayout(attribType).fCount, |
|
359 GrGLAttribTypeToLayout(attribType).fType, |
|
360 GrGLAttribTypeToLayout(attribType).fNormalized, |
|
361 stride, |
|
362 reinterpret_cast<GrGLvoid*>( |
|
363 vertexOffsetInBytes + vertexAttrib->fOffset)); |
|
364 attribState->disableUnusedArrays(this, 1); |
|
365 } else { |
|
366 int vertexAttribCount = this->getDrawState().getVertexAttribCount(); |
|
367 uint32_t usedAttribArraysMask = 0; |
|
368 const GrVertexAttrib* vertexAttrib = this->getDrawState().getVertexAttribs(); |
|
369 |
|
370 for (int vertexAttribIndex = 0; vertexAttribIndex < vertexAttribCount; |
|
371 ++vertexAttribIndex, ++vertexAttrib) { |
|
372 |
|
373 usedAttribArraysMask |= (1 << vertexAttribIndex); |
|
374 GrVertexAttribType attribType = vertexAttrib->fType; |
|
375 attribState->set(this, |
|
376 vertexAttribIndex, |
|
377 vbuf, |
|
378 GrGLAttribTypeToLayout(attribType).fCount, |
|
379 GrGLAttribTypeToLayout(attribType).fType, |
|
380 GrGLAttribTypeToLayout(attribType).fNormalized, |
|
381 stride, |
|
382 reinterpret_cast<GrGLvoid*>( |
|
383 vertexOffsetInBytes + vertexAttrib->fOffset)); |
|
384 } |
|
385 attribState->disableUnusedArrays(this, usedAttribArraysMask); |
|
386 } |
|
387 } |