blob: e4d9b3edb6a599d9b3e6397a210df9b598e3d397 [file] [log] [blame]
junov@google.comf93e7172011-03-31 21:26:24 +00001/*
2 Copyright 2011 Google Inc.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17#include "GrBinHashKey.h"
18#include "GrGLEffect.h"
19#include "GrGLProgram.h"
20#include "GrGpuGLShaders.h"
21#include "GrGpuVertex.h"
22#include "GrMemory.h"
23#include "GrNoncopyable.h"
24#include "GrStringBuilder.h"
25
26#define ATTRIBUTE_MATRIX 0
27#define PRINT_SHADERS 0
28#define SKIP_CACHE_CHECK true
29#define GR_UINT32_MAX static_cast<uint32_t>(-1)
30
31#if ATTRIBUTE_MATRIX
32#define VIEWMAT_ATTR_LOCATION (3 + GrDrawTarget::kMaxTexCoords)
33#define TEXMAT_ATTR_LOCATION(X) (6 + GrDrawTarget::kMaxTexCoords + 3 * (X))
34#define BOGUS_MATRIX_UNI_LOCATION 1000
35#endif
36
37#include "GrTHashCache.h"
38
39class GrGpuGLShaders::ProgramCache : public ::GrNoncopyable {
40private:
41 class Entry;
42
43#if GR_DEBUG
44 typedef GrBinHashKey<Entry, 4> ProgramHashKey; // Flex the dynamic allocation muscle in debug
45#else
46 typedef GrBinHashKey<Entry, 32> ProgramHashKey;
47#endif
48
49 class Entry : public ::GrNoncopyable {
50 public:
51 Entry() {}
52 private:
53 void copyAndTakeOwnership(Entry& entry) {
54 fProgramData.copyAndTakeOwnership(entry.fProgramData);
55 fKey.copyAndTakeOwnership(entry.fKey); // ownership transfer
56 fLRUStamp = entry.fLRUStamp;
57 }
58
59 public:
60 int compare(const ProgramHashKey& key) const { return fKey.compare(key); }
61
62 public:
63 GrGLProgram::CachedData fProgramData;
64 ProgramHashKey fKey;
65 unsigned int fLRUStamp;
66 };
67
68 GrTHashTable<Entry, ProgramHashKey, 8> fHashCache;
69
70 enum {
71 kMaxEntries = 32
72 };
73 Entry fEntries[kMaxEntries];
74 int fCount;
75 unsigned int fCurrLRUStamp;
76
77public:
78 ProgramCache()
79 : fCount(0)
80 , fCurrLRUStamp(0) {
81 }
82
83 ~ProgramCache() {
84 for (int i = 0; i < fCount; ++i) {
85 GrGpuGLShaders::DeleteProgram(&fEntries[i].fProgramData);
86 }
87 }
88
89 void abandon() {
90 fCount = 0;
91 }
92
93 void invalidateViewMatrices() {
94 for (int i = 0; i < fCount; ++i) {
95 // set to illegal matrix
96 fEntries[i].fProgramData.fViewMatrix = GrMatrix::InvalidMatrix();
97 }
98 }
99
100 GrGLProgram::CachedData* getProgramData(const GrGLProgram& desc,
101 const GrDrawTarget* target) {
102 ProgramHashKey key;
103 while (key.doPass()) {
104 desc.buildKey(key);
105 }
106 Entry* entry = fHashCache.find(key);
107 if (NULL == entry) {
108 if (fCount < kMaxEntries) {
109 entry = fEntries + fCount;
110 ++fCount;
111 } else {
112 GrAssert(kMaxEntries == fCount);
113 entry = fEntries;
114 for (int i = 1; i < kMaxEntries; ++i) {
115 if (fEntries[i].fLRUStamp < entry->fLRUStamp) {
116 entry = fEntries + i;
117 }
118 }
119 fHashCache.remove(entry->fKey, entry);
120 GrGpuGLShaders::DeleteProgram(&entry->fProgramData);
121 }
122 entry->fKey.copyAndTakeOwnership(key);
123 desc.genProgram(&entry->fProgramData, target);
124 fHashCache.insert(entry->fKey, entry);
125 }
126
127 entry->fLRUStamp = fCurrLRUStamp;
128 if (GR_UINT32_MAX == fCurrLRUStamp) {
129 // wrap around! just trash our LRU, one time hit.
130 for (int i = 0; i < fCount; ++i) {
131 fEntries[i].fLRUStamp = 0;
132 }
133 }
134 ++fCurrLRUStamp;
135 return &entry->fProgramData;
136 }
137};
138
139void GrGpuGLShaders::DeleteProgram(GrGLProgram::CachedData* programData) {
140 GR_GL(DeleteShader(programData->fVShaderID));
141 GR_GL(DeleteShader(programData->fFShaderID));
142 GR_GL(DeleteProgram(programData->fProgramID));
143 GR_DEBUGCODE(memset(programData, 0, sizeof(*programData));)
144}
145
146
147GrGpuGLShaders::GrGpuGLShaders() {
148
149 resetContextHelper();
150
151 fProgramData = NULL;
152 fProgramCache = new ProgramCache();
153}
154
155GrGpuGLShaders::~GrGpuGLShaders() {
156 delete fProgramCache;
157}
158
159const GrMatrix& GrGpuGLShaders::getHWSamplerMatrix(int stage) {
160#if ATTRIBUTE_MATRIX
161 return fHWDrawState.fSamplerStates[stage].getMatrix();
162#else
163 GrAssert(fProgramData);
164 return fProgramData->fTextureMatrices[stage];
165#endif
166}
167
168void GrGpuGLShaders::recordHWSamplerMatrix(int stage, const GrMatrix& matrix) {
169#if ATTRIBUTE_MATRIX
170 fHWDrawState.fSamplerStates[stage].setMatrix(matrix);
171#else
172 GrAssert(fProgramData);
173 fProgramData->fTextureMatrices[stage] = matrix;
174#endif
175}
176
177void GrGpuGLShaders::resetContext() {
178 INHERITED::resetContext();
179 resetContextHelper();
180}
181
182void GrGpuGLShaders::resetContextHelper() {
183 fHWGeometryState.fVertexLayout = 0;
184 fHWGeometryState.fVertexOffset = ~0;
185 GR_GL(DisableVertexAttribArray(COL_ATTR_LOCATION));
186 for (int t = 0; t < kMaxTexCoords; ++t) {
187 GR_GL(DisableVertexAttribArray(TEX_ATTR_LOCATION(t)));
188 }
189 GR_GL(EnableVertexAttribArray(POS_ATTR_LOCATION));
190
191 fHWProgramID = 0;
192}
193
194void GrGpuGLShaders::flushViewMatrix() {
195 GrAssert(NULL != fCurrDrawState.fRenderTarget);
196 GrMatrix m (
197 GrIntToScalar(2) / fCurrDrawState.fRenderTarget->width(), 0, -GR_Scalar1,
198 0,-GrIntToScalar(2) / fCurrDrawState.fRenderTarget->height(), GR_Scalar1,
199 0, 0, GrMatrix::I()[8]);
200 m.setConcat(m, fCurrDrawState.fViewMatrix);
201
202 // ES doesn't allow you to pass true to the transpose param,
203 // so do our own transpose
204 GrScalar mt[] = {
205 m[GrMatrix::kScaleX],
206 m[GrMatrix::kSkewY],
207 m[GrMatrix::kPersp0],
208 m[GrMatrix::kSkewX],
209 m[GrMatrix::kScaleY],
210 m[GrMatrix::kPersp1],
211 m[GrMatrix::kTransX],
212 m[GrMatrix::kTransY],
213 m[GrMatrix::kPersp2]
214 };
215#if ATTRIBUTE_MATRIX
216 GR_GL(VertexAttrib4fv(VIEWMAT_ATTR_LOCATION+0, mt+0));
217 GR_GL(VertexAttrib4fv(VIEWMAT_ATTR_LOCATION+1, mt+3));
218 GR_GL(VertexAttrib4fv(VIEWMAT_ATTR_LOCATION+2, mt+6));
219#else
220 GR_GL(UniformMatrix3fv(fProgramData->fUniLocations.fViewMatrixUni,1,false,mt));
221#endif
222}
223
224void GrGpuGLShaders::flushTextureMatrix(int stage) {
225 GrAssert(NULL != fCurrDrawState.fTextures[stage]);
226
227 GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[stage];
228
229 GrMatrix m = getSamplerMatrix(stage);
230 GrSamplerState::SampleMode mode =
231 fCurrDrawState.fSamplerStates[0].getSampleMode();
232 AdjustTextureMatrix(texture, mode, &m);
233
234 // ES doesn't allow you to pass true to the transpose param,
235 // so do our own transpose
236 GrScalar mt[] = {
237 m[GrMatrix::kScaleX],
238 m[GrMatrix::kSkewY],
239 m[GrMatrix::kPersp0],
240 m[GrMatrix::kSkewX],
241 m[GrMatrix::kScaleY],
242 m[GrMatrix::kPersp1],
243 m[GrMatrix::kTransX],
244 m[GrMatrix::kTransY],
245 m[GrMatrix::kPersp2]
246 };
247#if ATTRIBUTE_MATRIX
248 GR_GL(VertexAttrib4fv(TEXMAT_ATTR_LOCATION(0)+0, mt+0));
249 GR_GL(VertexAttrib4fv(TEXMAT_ATTR_LOCATION(0)+1, mt+3));
250 GR_GL(VertexAttrib4fv(TEXMAT_ATTR_LOCATION(0)+2, mt+6));
251#else
252 GR_GL(UniformMatrix3fv(fProgramData->fUniLocations.fStages[stage].fTextureMatrixUni,
253 1, false, mt));
254#endif
255}
256
257void GrGpuGLShaders::flushRadial2(int stage) {
258
259 const GrSamplerState& sampler = fCurrDrawState.fSamplerStates[stage];
260
261 GrScalar centerX1 = sampler.getRadial2CenterX1();
262 GrScalar radius0 = sampler.getRadial2Radius0();
263
264 GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1;
265
266 float unis[6] = {
267 GrScalarToFloat(a),
268 1 / (2.f * unis[0]),
269 GrScalarToFloat(centerX1),
270 GrScalarToFloat(radius0),
271 GrScalarToFloat(GrMul(radius0, radius0)),
272 sampler.isRadial2PosRoot() ? 1.f : -1.f
273 };
274 GR_GL(Uniform1fv(fProgramData->fUniLocations.fStages[stage].fRadial2Uni,
275 6,
276 unis));
277}
278
279bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
280 if (!flushGLStateCommon(type)) {
281 return false;
282 }
283
284 if (fDirtyFlags.fRenderTargetChanged) {
285 // our coords are in pixel space and the GL matrices map to NDC
286 // so if the viewport changed, our matrix is now wrong.
287#if ATTRIBUTE_MATRIX
288 fHWDrawState.fViewMatrix = GrMatrix::InvalidMatrix();
289#else
290 // we assume all shader matrices may be wrong after viewport changes
291 fProgramCache->invalidateViewMatrices();
292#endif
293 }
294
295 if (fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit) {
296 // invalidate the immediate mode color
297 fHWDrawState.fColor = GrColor_ILLEGAL;
298 } else {
299 if (fHWDrawState.fColor != fCurrDrawState.fColor) {
300 // OpenGL ES only supports the float varities of glVertexAttrib
301 float c[] = {
302 GrColorUnpackR(fCurrDrawState.fColor) / 255.f,
303 GrColorUnpackG(fCurrDrawState.fColor) / 255.f,
304 GrColorUnpackB(fCurrDrawState.fColor) / 255.f,
305 GrColorUnpackA(fCurrDrawState.fColor) / 255.f
306 };
307 GR_GL(VertexAttrib4fv(COL_ATTR_LOCATION, c));
308 fHWDrawState.fColor = fCurrDrawState.fColor;
309 }
310 }
311
312 buildProgram(type);
313 fProgramData = fProgramCache->getProgramData(fCurrentProgram, this);
314
315 if (fHWProgramID != fProgramData->fProgramID) {
316 GR_GL(UseProgram(fProgramData->fProgramID));
317 fHWProgramID = fProgramData->fProgramID;
318 }
319
320 if (!fCurrentProgram.doGLSetup(type, fProgramData)) {
321 return false;
322 }
323
324#if ATTRIBUTE_MATRIX
325 GrMatrix& currViewMatrix = fHWDrawState.fViewMatrix;
326#else
327 GrMatrix& currViewMatrix = fProgramData->fViewMatrix;
328#endif
329
330 if (currViewMatrix != fCurrDrawState.fViewMatrix) {
331 flushViewMatrix();
332 currViewMatrix = fCurrDrawState.fViewMatrix;
333 }
334
335 for (int s = 0; s < kNumStages; ++s) {
336 GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
337 if (NULL != texture) {
338 if (-1 != fProgramData->fUniLocations.fStages[s].fTextureMatrixUni &&
339 (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
340 getHWSamplerMatrix(s) != getSamplerMatrix(s))) {
341 flushTextureMatrix(s);
342 recordHWSamplerMatrix(s, getSamplerMatrix(s));
343 }
344 }
345
346 const GrSamplerState& sampler = fCurrDrawState.fSamplerStates[s];
347 if (-1 != fProgramData->fUniLocations.fStages[s].fRadial2Uni &&
348 (fProgramData->fRadial2CenterX1[s] != sampler.getRadial2CenterX1() ||
349 fProgramData->fRadial2Radius0[s] != sampler.getRadial2Radius0() ||
350 fProgramData->fRadial2PosRoot[s] != sampler.isRadial2PosRoot())) {
351
352 flushRadial2(s);
353
354 fProgramData->fRadial2CenterX1[s] = sampler.getRadial2CenterX1();
355 fProgramData->fRadial2Radius0[s] = sampler.getRadial2Radius0();
356 fProgramData->fRadial2PosRoot[s] = sampler.isRadial2PosRoot();
357 }
358 }
359 resetDirtyFlags();
360 return true;
361}
362
363void GrGpuGLShaders::postDraw() {
364 fCurrentProgram.doGLPost();
365}
366
367void GrGpuGLShaders::setupGeometry(int* startVertex,
368 int* startIndex,
369 int vertexCount,
370 int indexCount) {
371
372 int newColorOffset;
373 int newTexCoordOffsets[kMaxTexCoords];
374
375 GrGLsizei newStride = VertexSizeAndOffsetsByIdx(fGeometrySrc.fVertexLayout,
376 newTexCoordOffsets,
377 &newColorOffset);
378 int oldColorOffset;
379 int oldTexCoordOffsets[kMaxTexCoords];
380 GrGLsizei oldStride = VertexSizeAndOffsetsByIdx(fHWGeometryState.fVertexLayout,
381 oldTexCoordOffsets,
382 &oldColorOffset);
383 bool indexed = NULL != startIndex;
384
385 int extraVertexOffset;
386 int extraIndexOffset;
387 setBuffers(indexed, &extraVertexOffset, &extraIndexOffset);
388
389 GrGLenum scalarType;
390 bool texCoordNorm;
391 if (fGeometrySrc.fVertexLayout & kTextFormat_VertexLayoutBit) {
392 scalarType = GrGLTextType;
393 texCoordNorm = GR_GL_TEXT_TEXTURE_NORMALIZED;
394 } else {
395 scalarType = GrGLType;
396 texCoordNorm = false;
397 }
398
399 size_t vertexOffset = (*startVertex + extraVertexOffset) * newStride;
400 *startVertex = 0;
401 if (indexed) {
402 *startIndex += extraIndexOffset;
403 }
404
405 // all the Pointers must be set if any of these are true
406 bool allOffsetsChange = fHWGeometryState.fArrayPtrsDirty ||
407 vertexOffset != fHWGeometryState.fVertexOffset ||
408 newStride != oldStride;
409
410 // position and tex coord offsets change if above conditions are true
411 // or the type/normalization changed based on text vs nontext type coords.
412 bool posAndTexChange = allOffsetsChange ||
413 (((GrGLTextType != GrGLType) || GR_GL_TEXT_TEXTURE_NORMALIZED) &&
414 (kTextFormat_VertexLayoutBit &
415 (fHWGeometryState.fVertexLayout ^
416 fGeometrySrc.fVertexLayout)));
417
418 if (posAndTexChange) {
419 GR_GL(VertexAttribPointer(POS_ATTR_LOCATION, 2, scalarType,
420 false, newStride, (GrGLvoid*)vertexOffset));
421 fHWGeometryState.fVertexOffset = vertexOffset;
422 }
423
424 for (int t = 0; t < kMaxTexCoords; ++t) {
425 if (newTexCoordOffsets[t] > 0) {
426 GrGLvoid* texCoordOffset = (GrGLvoid*)(vertexOffset + newTexCoordOffsets[t]);
427 if (oldTexCoordOffsets[t] <= 0) {
428 GR_GL(EnableVertexAttribArray(TEX_ATTR_LOCATION(t)));
429 GR_GL(VertexAttribPointer(TEX_ATTR_LOCATION(t), 2, scalarType,
430 texCoordNorm, newStride, texCoordOffset));
431 } else if (posAndTexChange ||
432 newTexCoordOffsets[t] != oldTexCoordOffsets[t]) {
433 GR_GL(VertexAttribPointer(TEX_ATTR_LOCATION(t), 2, scalarType,
434 texCoordNorm, newStride, texCoordOffset));
435 }
436 } else if (oldTexCoordOffsets[t] > 0) {
437 GR_GL(DisableVertexAttribArray(TEX_ATTR_LOCATION(t)));
438 }
439 }
440
441 if (newColorOffset > 0) {
442 GrGLvoid* colorOffset = (int8_t*)(vertexOffset + newColorOffset);
443 if (oldColorOffset <= 0) {
444 GR_GL(EnableVertexAttribArray(COL_ATTR_LOCATION));
445 GR_GL(VertexAttribPointer(COL_ATTR_LOCATION, 4,
446 GR_GL_UNSIGNED_BYTE,
447 true, newStride, colorOffset));
448 } else if (allOffsetsChange || newColorOffset != oldColorOffset) {
449 GR_GL(VertexAttribPointer(COL_ATTR_LOCATION, 4,
450 GR_GL_UNSIGNED_BYTE,
451 true, newStride, colorOffset));
452 }
453 } else if (oldColorOffset > 0) {
454 GR_GL(DisableVertexAttribArray(COL_ATTR_LOCATION));
455 }
456
457 fHWGeometryState.fVertexLayout = fGeometrySrc.fVertexLayout;
458 fHWGeometryState.fArrayPtrsDirty = false;
459}
460
461void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
462 // Must initialize all fields or cache will have false negatives!
463 fCurrentProgram.fProgramDesc.fVertexLayout = fGeometrySrc.fVertexLayout;
464
465 fCurrentProgram.fProgramDesc.fOptFlags = 0;
466 if (kPoints_PrimitiveType != type) {
467 fCurrentProgram.fProgramDesc.fOptFlags |= GrGLProgram::ProgramDesc::kNotPoints_OptFlagBit;
468 }
469#if GR_AGGRESSIVE_SHADER_OPTS
470 if (!(fCurrentProgram.fProgramDesc.fVertexLayout & kColor_VertexLayoutBit) &&
471 (0xffffffff == fCurrDrawState.fColor)) {
472 fCurrentProgram.fProgramDesc.fOptFlags |= GrGLProgram::ProgramDesc::kVertexColorAllOnes_OptFlagBit;
473 }
474#endif
475
476 for (int s = 0; s < kNumStages; ++s) {
477 GrGLProgram::ProgramDesc::StageDesc& stage = fCurrentProgram.fProgramDesc.fStages[s];
478
479 stage.fEnabled = VertexUsesStage(s, fGeometrySrc.fVertexLayout);
480
481 if (stage.fEnabled) {
482 GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
483 GrAssert(NULL != texture);
484 // we matrix to invert when orientation is TopDown, so make sure
485 // we aren't in that case before flagging as identity.
486 if (TextureMatrixIsIdentity(texture, fCurrDrawState.fSamplerStates[s])) {
487 stage.fOptFlags = GrGLProgram::ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit;
488 } else if (!getSamplerMatrix(s).hasPerspective()) {
489 stage.fOptFlags = GrGLProgram::ProgramDesc::StageDesc::kNoPerspective_OptFlagBit;
490 } else {
491 stage.fOptFlags = 0;
492 }
493 switch (fCurrDrawState.fSamplerStates[s].getSampleMode()) {
494 case GrSamplerState::kNormal_SampleMode:
495 stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kIdentity_CoordMapping;
496 break;
497 case GrSamplerState::kRadial_SampleMode:
498 stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kRadialGradient_CoordMapping;
499 break;
500 case GrSamplerState::kRadial2_SampleMode:
501 stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping;
502 break;
503 case GrSamplerState::kSweep_SampleMode:
504 stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kSweepGradient_CoordMapping;
505 break;
506 default:
507 GrAssert(!"Unexpected sample mode!");
508 break;
509 }
510
511 if (GrTexture::kAlpha_8_PixelConfig == texture->config()) {
512 stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kAlpha_Modulation;
513 } else {
514 stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kColor_Modulation;
515 }
516
517 if (fCurrDrawState.fEffects[s]) {
518 fCurrentProgram.fStageEffects[s] = GrGLEffect::Create(fCurrDrawState.fEffects[s]);
519 } else {
520 delete fCurrentProgram.fStageEffects[s];
521 fCurrentProgram.fStageEffects[s] = NULL;
522 }
523 } else {
524 stage.fOptFlags = 0;
525 stage.fCoordMapping = (GrGLProgram::ProgramDesc::StageDesc::CoordMapping)0;
526 stage.fModulation = (GrGLProgram::ProgramDesc::StageDesc::Modulation)0;
527 fCurrentProgram.fStageEffects[s] = NULL;
528 }
529 }
530}
531
532
533