blob: b34fc33bef41e7447112758f9faca43945a6a102 [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
bsalomon@google.combcdbbe62011-04-12 15:40:00 +0000149 resetContext();
junov@google.comf93e7172011-03-31 21:26:24 +0000150
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();
junov@google.comf93e7172011-03-31 21:26:24 +0000179
junov@google.comf93e7172011-03-31 21:26:24 +0000180 fHWGeometryState.fVertexLayout = 0;
181 fHWGeometryState.fVertexOffset = ~0;
182 GR_GL(DisableVertexAttribArray(COL_ATTR_LOCATION));
183 for (int t = 0; t < kMaxTexCoords; ++t) {
184 GR_GL(DisableVertexAttribArray(TEX_ATTR_LOCATION(t)));
185 }
186 GR_GL(EnableVertexAttribArray(POS_ATTR_LOCATION));
187
188 fHWProgramID = 0;
189}
190
191void GrGpuGLShaders::flushViewMatrix() {
192 GrAssert(NULL != fCurrDrawState.fRenderTarget);
193 GrMatrix m (
194 GrIntToScalar(2) / fCurrDrawState.fRenderTarget->width(), 0, -GR_Scalar1,
195 0,-GrIntToScalar(2) / fCurrDrawState.fRenderTarget->height(), GR_Scalar1,
196 0, 0, GrMatrix::I()[8]);
197 m.setConcat(m, fCurrDrawState.fViewMatrix);
198
199 // ES doesn't allow you to pass true to the transpose param,
200 // so do our own transpose
201 GrScalar mt[] = {
202 m[GrMatrix::kScaleX],
203 m[GrMatrix::kSkewY],
204 m[GrMatrix::kPersp0],
205 m[GrMatrix::kSkewX],
206 m[GrMatrix::kScaleY],
207 m[GrMatrix::kPersp1],
208 m[GrMatrix::kTransX],
209 m[GrMatrix::kTransY],
210 m[GrMatrix::kPersp2]
211 };
212#if ATTRIBUTE_MATRIX
213 GR_GL(VertexAttrib4fv(VIEWMAT_ATTR_LOCATION+0, mt+0));
214 GR_GL(VertexAttrib4fv(VIEWMAT_ATTR_LOCATION+1, mt+3));
215 GR_GL(VertexAttrib4fv(VIEWMAT_ATTR_LOCATION+2, mt+6));
216#else
217 GR_GL(UniformMatrix3fv(fProgramData->fUniLocations.fViewMatrixUni,1,false,mt));
218#endif
219}
220
221void GrGpuGLShaders::flushTextureMatrix(int stage) {
222 GrAssert(NULL != fCurrDrawState.fTextures[stage]);
223
224 GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[stage];
225
226 GrMatrix m = getSamplerMatrix(stage);
227 GrSamplerState::SampleMode mode =
228 fCurrDrawState.fSamplerStates[0].getSampleMode();
229 AdjustTextureMatrix(texture, mode, &m);
230
231 // ES doesn't allow you to pass true to the transpose param,
232 // so do our own transpose
233 GrScalar mt[] = {
234 m[GrMatrix::kScaleX],
235 m[GrMatrix::kSkewY],
236 m[GrMatrix::kPersp0],
237 m[GrMatrix::kSkewX],
238 m[GrMatrix::kScaleY],
239 m[GrMatrix::kPersp1],
240 m[GrMatrix::kTransX],
241 m[GrMatrix::kTransY],
242 m[GrMatrix::kPersp2]
243 };
244#if ATTRIBUTE_MATRIX
245 GR_GL(VertexAttrib4fv(TEXMAT_ATTR_LOCATION(0)+0, mt+0));
246 GR_GL(VertexAttrib4fv(TEXMAT_ATTR_LOCATION(0)+1, mt+3));
247 GR_GL(VertexAttrib4fv(TEXMAT_ATTR_LOCATION(0)+2, mt+6));
248#else
249 GR_GL(UniformMatrix3fv(fProgramData->fUniLocations.fStages[stage].fTextureMatrixUni,
250 1, false, mt));
251#endif
252}
253
254void GrGpuGLShaders::flushRadial2(int stage) {
255
256 const GrSamplerState& sampler = fCurrDrawState.fSamplerStates[stage];
257
258 GrScalar centerX1 = sampler.getRadial2CenterX1();
259 GrScalar radius0 = sampler.getRadial2Radius0();
260
261 GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1;
262
263 float unis[6] = {
264 GrScalarToFloat(a),
265 1 / (2.f * unis[0]),
266 GrScalarToFloat(centerX1),
267 GrScalarToFloat(radius0),
268 GrScalarToFloat(GrMul(radius0, radius0)),
269 sampler.isRadial2PosRoot() ? 1.f : -1.f
270 };
271 GR_GL(Uniform1fv(fProgramData->fUniLocations.fStages[stage].fRadial2Uni,
272 6,
273 unis));
274}
275
276bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
277 if (!flushGLStateCommon(type)) {
278 return false;
279 }
280
281 if (fDirtyFlags.fRenderTargetChanged) {
282 // our coords are in pixel space and the GL matrices map to NDC
283 // so if the viewport changed, our matrix is now wrong.
284#if ATTRIBUTE_MATRIX
285 fHWDrawState.fViewMatrix = GrMatrix::InvalidMatrix();
286#else
287 // we assume all shader matrices may be wrong after viewport changes
288 fProgramCache->invalidateViewMatrices();
289#endif
290 }
291
292 if (fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit) {
293 // invalidate the immediate mode color
294 fHWDrawState.fColor = GrColor_ILLEGAL;
295 } else {
296 if (fHWDrawState.fColor != fCurrDrawState.fColor) {
297 // OpenGL ES only supports the float varities of glVertexAttrib
298 float c[] = {
299 GrColorUnpackR(fCurrDrawState.fColor) / 255.f,
300 GrColorUnpackG(fCurrDrawState.fColor) / 255.f,
301 GrColorUnpackB(fCurrDrawState.fColor) / 255.f,
302 GrColorUnpackA(fCurrDrawState.fColor) / 255.f
303 };
304 GR_GL(VertexAttrib4fv(COL_ATTR_LOCATION, c));
305 fHWDrawState.fColor = fCurrDrawState.fColor;
306 }
307 }
308
309 buildProgram(type);
310 fProgramData = fProgramCache->getProgramData(fCurrentProgram, this);
311
312 if (fHWProgramID != fProgramData->fProgramID) {
313 GR_GL(UseProgram(fProgramData->fProgramID));
314 fHWProgramID = fProgramData->fProgramID;
315 }
316
317 if (!fCurrentProgram.doGLSetup(type, fProgramData)) {
318 return false;
319 }
320
321#if ATTRIBUTE_MATRIX
322 GrMatrix& currViewMatrix = fHWDrawState.fViewMatrix;
323#else
324 GrMatrix& currViewMatrix = fProgramData->fViewMatrix;
325#endif
326
327 if (currViewMatrix != fCurrDrawState.fViewMatrix) {
328 flushViewMatrix();
329 currViewMatrix = fCurrDrawState.fViewMatrix;
330 }
331
332 for (int s = 0; s < kNumStages; ++s) {
333 GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
334 if (NULL != texture) {
335 if (-1 != fProgramData->fUniLocations.fStages[s].fTextureMatrixUni &&
336 (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
337 getHWSamplerMatrix(s) != getSamplerMatrix(s))) {
338 flushTextureMatrix(s);
339 recordHWSamplerMatrix(s, getSamplerMatrix(s));
340 }
341 }
342
343 const GrSamplerState& sampler = fCurrDrawState.fSamplerStates[s];
344 if (-1 != fProgramData->fUniLocations.fStages[s].fRadial2Uni &&
345 (fProgramData->fRadial2CenterX1[s] != sampler.getRadial2CenterX1() ||
346 fProgramData->fRadial2Radius0[s] != sampler.getRadial2Radius0() ||
347 fProgramData->fRadial2PosRoot[s] != sampler.isRadial2PosRoot())) {
348
349 flushRadial2(s);
350
351 fProgramData->fRadial2CenterX1[s] = sampler.getRadial2CenterX1();
352 fProgramData->fRadial2Radius0[s] = sampler.getRadial2Radius0();
353 fProgramData->fRadial2PosRoot[s] = sampler.isRadial2PosRoot();
354 }
355 }
356 resetDirtyFlags();
357 return true;
358}
359
360void GrGpuGLShaders::postDraw() {
361 fCurrentProgram.doGLPost();
362}
363
364void GrGpuGLShaders::setupGeometry(int* startVertex,
365 int* startIndex,
366 int vertexCount,
367 int indexCount) {
368
369 int newColorOffset;
370 int newTexCoordOffsets[kMaxTexCoords];
371
372 GrGLsizei newStride = VertexSizeAndOffsetsByIdx(fGeometrySrc.fVertexLayout,
373 newTexCoordOffsets,
374 &newColorOffset);
375 int oldColorOffset;
376 int oldTexCoordOffsets[kMaxTexCoords];
377 GrGLsizei oldStride = VertexSizeAndOffsetsByIdx(fHWGeometryState.fVertexLayout,
378 oldTexCoordOffsets,
379 &oldColorOffset);
380 bool indexed = NULL != startIndex;
381
382 int extraVertexOffset;
383 int extraIndexOffset;
384 setBuffers(indexed, &extraVertexOffset, &extraIndexOffset);
385
386 GrGLenum scalarType;
387 bool texCoordNorm;
388 if (fGeometrySrc.fVertexLayout & kTextFormat_VertexLayoutBit) {
389 scalarType = GrGLTextType;
390 texCoordNorm = GR_GL_TEXT_TEXTURE_NORMALIZED;
391 } else {
392 scalarType = GrGLType;
393 texCoordNorm = false;
394 }
395
396 size_t vertexOffset = (*startVertex + extraVertexOffset) * newStride;
397 *startVertex = 0;
398 if (indexed) {
399 *startIndex += extraIndexOffset;
400 }
401
402 // all the Pointers must be set if any of these are true
403 bool allOffsetsChange = fHWGeometryState.fArrayPtrsDirty ||
404 vertexOffset != fHWGeometryState.fVertexOffset ||
405 newStride != oldStride;
406
407 // position and tex coord offsets change if above conditions are true
408 // or the type/normalization changed based on text vs nontext type coords.
409 bool posAndTexChange = allOffsetsChange ||
410 (((GrGLTextType != GrGLType) || GR_GL_TEXT_TEXTURE_NORMALIZED) &&
411 (kTextFormat_VertexLayoutBit &
412 (fHWGeometryState.fVertexLayout ^
413 fGeometrySrc.fVertexLayout)));
414
415 if (posAndTexChange) {
416 GR_GL(VertexAttribPointer(POS_ATTR_LOCATION, 2, scalarType,
417 false, newStride, (GrGLvoid*)vertexOffset));
418 fHWGeometryState.fVertexOffset = vertexOffset;
419 }
420
421 for (int t = 0; t < kMaxTexCoords; ++t) {
422 if (newTexCoordOffsets[t] > 0) {
423 GrGLvoid* texCoordOffset = (GrGLvoid*)(vertexOffset + newTexCoordOffsets[t]);
424 if (oldTexCoordOffsets[t] <= 0) {
425 GR_GL(EnableVertexAttribArray(TEX_ATTR_LOCATION(t)));
426 GR_GL(VertexAttribPointer(TEX_ATTR_LOCATION(t), 2, scalarType,
427 texCoordNorm, newStride, texCoordOffset));
428 } else if (posAndTexChange ||
429 newTexCoordOffsets[t] != oldTexCoordOffsets[t]) {
430 GR_GL(VertexAttribPointer(TEX_ATTR_LOCATION(t), 2, scalarType,
431 texCoordNorm, newStride, texCoordOffset));
432 }
433 } else if (oldTexCoordOffsets[t] > 0) {
434 GR_GL(DisableVertexAttribArray(TEX_ATTR_LOCATION(t)));
435 }
436 }
437
438 if (newColorOffset > 0) {
439 GrGLvoid* colorOffset = (int8_t*)(vertexOffset + newColorOffset);
440 if (oldColorOffset <= 0) {
441 GR_GL(EnableVertexAttribArray(COL_ATTR_LOCATION));
442 GR_GL(VertexAttribPointer(COL_ATTR_LOCATION, 4,
443 GR_GL_UNSIGNED_BYTE,
444 true, newStride, colorOffset));
445 } else if (allOffsetsChange || newColorOffset != oldColorOffset) {
446 GR_GL(VertexAttribPointer(COL_ATTR_LOCATION, 4,
447 GR_GL_UNSIGNED_BYTE,
448 true, newStride, colorOffset));
449 }
450 } else if (oldColorOffset > 0) {
451 GR_GL(DisableVertexAttribArray(COL_ATTR_LOCATION));
452 }
453
454 fHWGeometryState.fVertexLayout = fGeometrySrc.fVertexLayout;
455 fHWGeometryState.fArrayPtrsDirty = false;
456}
457
458void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
459 // Must initialize all fields or cache will have false negatives!
460 fCurrentProgram.fProgramDesc.fVertexLayout = fGeometrySrc.fVertexLayout;
461
462 fCurrentProgram.fProgramDesc.fOptFlags = 0;
463 if (kPoints_PrimitiveType != type) {
464 fCurrentProgram.fProgramDesc.fOptFlags |= GrGLProgram::ProgramDesc::kNotPoints_OptFlagBit;
465 }
466#if GR_AGGRESSIVE_SHADER_OPTS
467 if (!(fCurrentProgram.fProgramDesc.fVertexLayout & kColor_VertexLayoutBit) &&
468 (0xffffffff == fCurrDrawState.fColor)) {
469 fCurrentProgram.fProgramDesc.fOptFlags |= GrGLProgram::ProgramDesc::kVertexColorAllOnes_OptFlagBit;
470 }
471#endif
472
473 for (int s = 0; s < kNumStages; ++s) {
474 GrGLProgram::ProgramDesc::StageDesc& stage = fCurrentProgram.fProgramDesc.fStages[s];
475
476 stage.fEnabled = VertexUsesStage(s, fGeometrySrc.fVertexLayout);
477
478 if (stage.fEnabled) {
479 GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
480 GrAssert(NULL != texture);
481 // we matrix to invert when orientation is TopDown, so make sure
482 // we aren't in that case before flagging as identity.
483 if (TextureMatrixIsIdentity(texture, fCurrDrawState.fSamplerStates[s])) {
484 stage.fOptFlags = GrGLProgram::ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit;
485 } else if (!getSamplerMatrix(s).hasPerspective()) {
486 stage.fOptFlags = GrGLProgram::ProgramDesc::StageDesc::kNoPerspective_OptFlagBit;
487 } else {
488 stage.fOptFlags = 0;
489 }
490 switch (fCurrDrawState.fSamplerStates[s].getSampleMode()) {
491 case GrSamplerState::kNormal_SampleMode:
492 stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kIdentity_CoordMapping;
493 break;
494 case GrSamplerState::kRadial_SampleMode:
495 stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kRadialGradient_CoordMapping;
496 break;
497 case GrSamplerState::kRadial2_SampleMode:
498 stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping;
499 break;
500 case GrSamplerState::kSweep_SampleMode:
501 stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kSweepGradient_CoordMapping;
502 break;
503 default:
504 GrAssert(!"Unexpected sample mode!");
505 break;
506 }
507
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000508 if (GrPixelConfigIsAlphaOnly(texture->config())) {
junov@google.comf93e7172011-03-31 21:26:24 +0000509 stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kAlpha_Modulation;
510 } else {
511 stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kColor_Modulation;
512 }
513
514 if (fCurrDrawState.fEffects[s]) {
515 fCurrentProgram.fStageEffects[s] = GrGLEffect::Create(fCurrDrawState.fEffects[s]);
516 } else {
517 delete fCurrentProgram.fStageEffects[s];
518 fCurrentProgram.fStageEffects[s] = NULL;
519 }
520 } else {
521 stage.fOptFlags = 0;
522 stage.fCoordMapping = (GrGLProgram::ProgramDesc::StageDesc::CoordMapping)0;
523 stage.fModulation = (GrGLProgram::ProgramDesc::StageDesc::Modulation)0;
524 fCurrentProgram.fStageEffects[s] = NULL;
525 }
526 }
527}
528
529
530