Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(174)

Side by Side Diff: src/gpu/batches/GrAtlasTextBatch.cpp

Issue 1458233003: Factor out GrAtlasTextBatch fromt GrAtlasTextContext (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: remove newline Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright 2015 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 "GrAtlasTextBatch.h"
9
10 #include "GrBatchFontCache.h"
11 #include "GrBatchFlushState.h"
12 #include "GrBatchTest.h"
13 #include "GrResourceProvider.h"
14
15 #include "SkDistanceFieldGen.h"
16 #include "SkGlyphCache.h"
17
18 #include "effects/GrBitmapTextGeoProc.h"
19 #include "effects/GrDistanceFieldGeoProc.h"
20
21 static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
22 unsigned r = SkColorGetR(c);
23 unsigned g = SkColorGetG(c);
24 unsigned b = SkColorGetB(c);
25 return GrColorPackRGBA(r, g, b, 0xff);
26 }
27
28 static const int kDistanceAdjustLumShift = 5;
29
30 void GrAtlasTextBatch::getInvariantOutputColor(GrInitInvariantOutput* out) const {
31 if (kColorBitmapMask_MaskType == fMaskType) {
32 out->setUnknownFourComponents();
33 } else {
34 out->setKnownFourComponents(fBatch.fColor);
35 }
36 }
37
38 void GrAtlasTextBatch::getInvariantOutputCoverage(GrInitInvariantOutput* out) co nst {
39 switch (fMaskType) {
40 case kGrayscaleDistanceField_MaskType:
41 case kGrayscaleCoverageMask_MaskType:
42 out->setUnknownSingleComponent();
43 break;
44 case kLCDCoverageMask_MaskType:
45 case kLCDDistanceField_MaskType:
46 out->setUnknownOpaqueFourComponents();
47 out->setUsingLCDCoverage();
48 break;
49 case kColorBitmapMask_MaskType:
50 out->setKnownSingleComponent(0xff);
51 }
52 }
53
54 void GrAtlasTextBatch::initBatchTracker(const GrPipelineOptimizations& opt) {
55 // Handle any color overrides
56 if (!opt.readsColor()) {
57 fGeoData[0].fColor = GrColor_ILLEGAL;
58 }
59 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
60
61 // setup batch properties
62 fBatch.fColorIgnored = !opt.readsColor();
63 fBatch.fColor = fGeoData[0].fColor;
64 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
65 fBatch.fCoverageIgnored = !opt.readsCoverage();
66 }
67
68 void GrAtlasTextBatch::onPrepareDraws(Target* target) {
69 // if we have RGB, then we won't have any SkShaders so no need to use a loca lmatrix.
70 // TODO actually only invert if we don't have RGBA
71 SkMatrix localMatrix;
72 if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
73 SkDebugf("Cannot invert viewmatrix\n");
74 return;
75 }
76
77 GrTexture* texture = fFontCache->getTexture(this->maskFormat());
78 if (!texture) {
79 SkDebugf("Could not allocate backing texture for atlas\n");
80 return;
81 }
82
83 bool usesDistanceFields = this->usesDistanceFields();
84 GrMaskFormat maskFormat = this->maskFormat();
85 bool isLCD = this->isLCD();
86
87 SkAutoTUnref<const GrGeometryProcessor> gp;
88 if (usesDistanceFields) {
89 gp.reset(this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this ->color(),
90 texture));
91 } else {
92 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone _FilterMode);
93 gp.reset(GrBitmapTextGeoProc::Create(this->color(),
94 texture,
95 params,
96 maskFormat,
97 localMatrix,
98 this->usesLocalCoords()));
99 }
100
101 FlushInfo flushInfo;
102 flushInfo.fGlyphsToFlush = 0;
103 size_t vertexStride = gp->getVertexStride();
104 SkASSERT(vertexStride == (usesDistanceFields ?
105 GetVertexStrideDf(maskFormat, isLCD) :
106 GetVertexStride(maskFormat)));
107
108 target->initDraw(gp, this->pipeline());
109
110 int glyphCount = this->numGlyphs();
111 const GrVertexBuffer* vertexBuffer;
112
113 void* vertices = target->makeVertexSpace(vertexStride,
114 glyphCount * kVerticesPerGlyph,
115 &vertexBuffer,
116 &flushInfo.fVertexOffset);
117 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
118 flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer( ));
119 if (!vertices || !flushInfo.fVertexBuffer) {
120 SkDebugf("Could not allocate vertices\n");
121 return;
122 }
123
124 unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
125
126 // We cache some values to avoid going to the glyphcache for the same fontSc aler twice
127 // in a row
128 const SkDescriptor* desc = nullptr;
129 SkGlyphCache* cache = nullptr;
130 GrFontScaler* scaler = nullptr;
131 SkTypeface* typeface = nullptr;
132
133 for (int i = 0; i < fGeoCount; i++) {
134 Geometry& args = fGeoData[i];
135 Blob* blob = args.fBlob;
136 Run& run = blob->fRuns[args.fRun];
137 TextInfo& info = run.fSubRunInfo[args.fSubRun];
138
139 uint64_t currentAtlasGen = fFontCache->atlasGeneration(maskFormat);
140 bool regenerateTextureCoords = info.fAtlasGeneration != currentAtlasGen ||
141 info.fStrike->isAbandoned();
142 bool regenerateColors;
143 if (usesDistanceFields) {
144 regenerateColors = !isLCD && run.fColor != args.fColor;
145 } else {
146 regenerateColors = kA8_GrMaskFormat == maskFormat && run.fColor != a rgs.fColor;
147 }
148 bool regeneratePositions = args.fTransX != 0.f || args.fTransY != 0.f;
149 int glyphCount = info.fGlyphEndIndex - info.fGlyphStartIndex;
150
151 // We regenerate both texture coords and colors in the blob itself, and update the
152 // atlas generation. If we don't end up purging any unused plots, we ca n avoid
153 // regenerating the coords. We could take a finer grained approach to u pdating texture
154 // coords but its not clear if the extra bookkeeping would offset any ga ins.
155 // To avoid looping over the glyphs twice, we do one loop and conditiona lly update color
156 // or coords as needed. One final note, if we have to break a run for a n atlas eviction
157 // then we can't really trust the atlas has all of the correct data. At las evictions
158 // should be pretty rare, so we just always regenerate in those cases
159 if (regenerateTextureCoords || regenerateColors || regeneratePositions) {
160 // first regenerate texture coordinates / colors if need be
161 bool brokenRun = false;
162
163 // Because the GrBatchFontCache may evict the strike a blob depends on using for
164 // generating its texture coords, we have to track whether or not th e strike has
165 // been abandoned. If it hasn't been abandoned, then we can use the GrGlyph*s as is
166 // otherwise we have to get the new strike, and use that to get the correct glyphs.
167 // Because we do not have the packed ids, and thus can't look up our glyphs in the
168 // new strike, we instead keep our ref to the old strike and use the packed ids from
169 // it. These ids will still be valid as long as we hold the ref. W hen we are done
170 // updating our cache of the GrGlyph*s, we drop our ref on the old s trike
171 bool regenerateGlyphs = false;
172 GrBatchTextStrike* strike = nullptr;
173 if (regenerateTextureCoords) {
174 info.fBulkUseToken.reset();
175
176 // We can reuse if we have a valid strike and our descriptors / typeface are the
177 // same. The override descriptor is only for the non distance f ield text within
178 // a run
179 const SkDescriptor* newDesc = (run.fOverrideDescriptor && !usesD istanceFields) ?
180 run.fOverrideDescriptor->getDesc() :
181 run.fDescriptor.getDesc();
182 if (!cache || !SkTypeface::Equal(typeface, run.fTypeface) ||
183 !(desc->equals(*newDesc))) {
184 if (cache) {
185 SkGlyphCache::AttachCache(cache);
186 }
187 desc = newDesc;
188 cache = SkGlyphCache::DetachCache(run.fTypeface, desc);
189 scaler = GrTextContext::GetGrFontScaler(cache);
190 strike = info.fStrike;
191 typeface = run.fTypeface;
192 }
193
194 if (info.fStrike->isAbandoned()) {
195 regenerateGlyphs = true;
196 strike = fFontCache->getStrike(scaler);
197 } else {
198 strike = info.fStrike;
199 }
200 }
201
202 for (int glyphIdx = 0; glyphIdx < glyphCount; glyphIdx++) {
203 if (regenerateTextureCoords) {
204 size_t glyphOffset = glyphIdx + info.fGlyphStartIndex;
205
206 GrGlyph* glyph = blob->fGlyphs[glyphOffset];
207 GrGlyph::PackedID id = glyph->fPackedID;
208 const SkGlyph& skGlyph = scaler->grToSkGlyph(id);
209 if (regenerateGlyphs) {
210 // Get the id from the old glyph, and use the new strike to lookup
211 // the glyph.
212 blob->fGlyphs[glyphOffset] = strike->getGlyph(skGlyph, i d, maskFormat,
213 scaler);
214 }
215 glyph = blob->fGlyphs[glyphOffset];
216 SkASSERT(glyph);
217 SkASSERT(id == glyph->fPackedID);
218 // We want to be able to assert this but cannot for testing purposes.
219 // once skbug:4143 has landed we can revist this assert
220 //SkASSERT(glyph->fMaskFormat == this->maskFormat());
221
222 if (!fFontCache->hasGlyph(glyph) &&
223 !strike->addGlyphToAtlas(target, glyph, scaler, skGlyph, maskFormat)) {
224 this->flush(target, &flushInfo);
225 target->initDraw(gp, this->pipeline());
226 brokenRun = glyphIdx > 0;
227
228 SkDEBUGCODE(bool success =) strike->addGlyphToAtlas(targ et,
229 glyp h,
230 scal er,
231 skGl yph,
232 mask Format);
233 SkASSERT(success);
234 }
235 fFontCache->addGlyphToBulkAndSetUseToken(&info.fBulkUseToken , glyph,
236 target->currentToke n());
237
238 // Texture coords are the last vertex attribute so we get a pointer to the
239 // first one and then map with stride in regenerateTextureCo ords
240 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices );
241 vertex += info.fVertexStartIndex;
242 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
243 vertex += vertexStride - sizeof(SkIPoint16);
244
245 this->regenerateTextureCoords(glyph, vertex, vertexStride);
246 }
247
248 if (regenerateColors) {
249 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices );
250 vertex += info.fVertexStartIndex;
251 vertex += vertexStride * glyphIdx * kVerticesPerGlyph + size of(SkPoint);
252 this->regenerateColors(vertex, vertexStride, args.fColor);
253 }
254
255 if (regeneratePositions) {
256 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices );
257 vertex += info.fVertexStartIndex;
258 vertex += vertexStride * glyphIdx * kVerticesPerGlyph;
259 SkScalar transX = args.fTransX;
260 SkScalar transY = args.fTransY;
261 this->regeneratePositions(vertex, vertexStride, transX, tran sY);
262 }
263 flushInfo.fGlyphsToFlush++;
264 }
265
266 // We my have changed the color so update it here
267 run.fColor = args.fColor;
268 if (regenerateTextureCoords) {
269 if (regenerateGlyphs) {
270 info.fStrike.reset(SkRef(strike));
271 }
272 info.fAtlasGeneration = brokenRun ? GrBatchAtlas::kInvalidAtlasG eneration :
273 fFontCache->atlasGeneration( maskFormat);
274 }
275 } else {
276 flushInfo.fGlyphsToFlush += glyphCount;
277
278 // set use tokens for all of the glyphs in our subrun. This is only valid if we
279 // have a valid atlas generation
280 fFontCache->setUseTokenBulk(info.fBulkUseToken, target->currentToken (), maskFormat);
281 }
282
283 // now copy all vertices
284 size_t byteCount = info.fVertexEndIndex - info.fVertexStartIndex;
285 memcpy(currVertex, blob->fVertices + info.fVertexStartIndex, byteCount);
286
287 currVertex += byteCount;
288 }
289 // Make sure to attach the last cache if applicable
290 if (cache) {
291 SkGlyphCache::AttachCache(cache);
292 }
293 this->flush(target, &flushInfo);
294 }
295
296 void GrAtlasTextBatch::regenerateTextureCoords(GrGlyph* glyph, intptr_t vertex,
297 size_t vertexStride) {
298 int width = glyph->fBounds.width();
299 int height = glyph->fBounds.height();
300
301 int u0, v0, u1, v1;
302 if (this->usesDistanceFields()) {
303 u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
304 v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
305 u1 = u0 + width - 2 * SK_DistanceFieldInset;
306 v1 = v0 + height - 2 * SK_DistanceFieldInset;
307 } else {
308 u0 = glyph->fAtlasLocation.fX;
309 v0 = glyph->fAtlasLocation.fY;
310 u1 = u0 + width;
311 v1 = v0 + height;
312 }
313
314 SkIPoint16* textureCoords;
315 // V0
316 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
317 textureCoords->set(u0, v0);
318 vertex += vertexStride;
319
320 // V1
321 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
322 textureCoords->set(u0, v1);
323 vertex += vertexStride;
324
325 // V2
326 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
327 textureCoords->set(u1, v1);
328 vertex += vertexStride;
329
330 // V3
331 textureCoords = reinterpret_cast<SkIPoint16*>(vertex);
332 textureCoords->set(u1, v0);
333 }
334
335 void GrAtlasTextBatch::regenerateColors(intptr_t vertex, size_t vertexStride, Gr Color color) {
336 for (int i = 0; i < kVerticesPerGlyph; i++) {
337 SkColor* vcolor = reinterpret_cast<SkColor*>(vertex);
338 *vcolor = color;
339 vertex += vertexStride;
340 }
341 }
342
343 void GrAtlasTextBatch::regeneratePositions(intptr_t vertex, size_t vertexStride, SkScalar transX,
344 SkScalar transY) {
345 for (int i = 0; i < kVerticesPerGlyph; i++) {
346 SkPoint* point = reinterpret_cast<SkPoint*>(vertex);
347 point->fX += transX;
348 point->fY += transY;
349 vertex += vertexStride;
350 }
351 }
352
353 void GrAtlasTextBatch::flush(GrVertexBatch::Target* target, FlushInfo* flushInfo ) {
354 GrVertices vertices;
355 int maxGlyphsPerDraw = flushInfo->fIndexBuffer->maxQuads();
356 vertices.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer,
357 flushInfo->fIndexBuffer, flushInfo->fVertexOffset,
358 kVerticesPerGlyph, kIndicesPerGlyph, flushInfo->fGlyp hsToFlush,
359 maxGlyphsPerDraw);
360 target->draw(vertices);
361 flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
362 flushInfo->fGlyphsToFlush = 0;
363 }
364
365 bool GrAtlasTextBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
366 GrAtlasTextBatch* that = t->cast<GrAtlasTextBatch>();
367 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeli ne(),
368 that->bounds(), caps)) {
369 return false;
370 }
371
372 if (fMaskType != that->fMaskType) {
373 return false;
374 }
375
376 if (!this->usesDistanceFields()) {
377 // TODO we can often batch across LCD text if we have dual source blendi ng and don't
378 // have to use the blend constant
379 if (kGrayscaleCoverageMask_MaskType != fMaskType && this->color() != tha t->color()) {
380 return false;
381 }
382 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->vi ewMatrix())) {
383 return false;
384 }
385 } else {
386 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
387 return false;
388 }
389
390 if (fFilteredColor != that->fFilteredColor) {
391 return false;
392 }
393
394 if (fUseBGR != that->fUseBGR) {
395 return false;
396 }
397
398 // TODO see note above
399 if (kLCDDistanceField_MaskType == fMaskType && this->color() != that->co lor()) {
400 return false;
401 }
402 }
403
404 fBatch.fNumGlyphs += that->numGlyphs();
405
406 // Reallocate space for geo data if necessary and then import that's geo dat a.
407 int newGeoCount = that->fGeoCount + fGeoCount;
408 // We assume (and here enforce) that the allocation size is the smallest pow er of two that
409 // is greater than or equal to the number of geometries (and at least
410 // kMinGeometryAllocated).
411 int newAllocSize = GrNextPow2(newGeoCount);
412 int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount) );
413
414 if (newGeoCount > currAllocSize) {
415 fGeoData.realloc(newAllocSize);
416 }
417
418 memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof( Geometry));
419 // We steal the ref on the blobs from the other TextBatch and set its count to 0 so that
420 // it doesn't try to unref them.
421 #ifdef SK_DEBUG
422 for (int i = 0; i < that->fGeoCount; ++i) {
423 that->fGeoData.get()[i].fBlob = (Blob*)0x1;
424 }
425 #endif
426 that->fGeoCount = 0;
427 fGeoCount = newGeoCount;
428
429 this->joinBounds(that->bounds());
430 return true;
431 }
432
433 // TODO just use class params
434 // TODO trying to figure out why lcd is so whack
435 GrGeometryProcessor* GrAtlasTextBatch::setupDfProcessor(const SkMatrix& viewMatr ix,
436 SkColor filteredColor,
437 GrColor color, GrTexture * texture) {
438 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_F ilterMode);
439 bool isLCD = this->isLCD();
440 // set up any flags
441 uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffect Flag : 0;
442
443 // see if we need to create a new effect
444 if (isLCD) {
445 flags |= kUseLCD_DistanceFieldEffectFlag;
446 flags |= viewMatrix.rectStaysRect() ? kRectToRect_DistanceFieldEffectFla g : 0;
447 flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
448
449 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
450
451 float redCorrection =
452 (*fDistanceAdjustTable)[GrColorUnpackR(colorNoPreMul) >> kDistanceAd justLumShift];
453 float greenCorrection =
454 (*fDistanceAdjustTable)[GrColorUnpackG(colorNoPreMul) >> kDistanceAd justLumShift];
455 float blueCorrection =
456 (*fDistanceAdjustTable)[GrColorUnpackB(colorNoPreMul) >> kDistanceAd justLumShift];
457 GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
458 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(redCorrection,
459 greenCorrection,
460 blueCorrection);
461
462 return GrDistanceFieldLCDTextGeoProc::Create(color,
463 viewMatrix,
464 texture,
465 params,
466 widthAdjust,
467 flags,
468 this->usesLocalCoords());
469 } else {
470 flags |= kColorAttr_DistanceFieldEffectFlag;
471 #ifdef SK_GAMMA_APPLY_TO_A8
472 U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, f ilteredColor);
473 float correction = (*fDistanceAdjustTable)[lum >> kDistanceAdjustLumShif t];
474 return GrDistanceFieldA8TextGeoProc::Create(color,
475 viewMatrix,
476 texture,
477 params,
478 correction,
479 flags,
480 this->usesLocalCoords());
481 #else
482 return GrDistanceFieldA8TextGeoProc::Create(color,
483 viewMatrix,
484 texture,
485 params,
486 flags,
487 this->usesLocalCoords());
488 #endif
489 }
490
491 }
OLDNEW
« src/gpu/batches/GrAtlasTextBatch.h ('K') | « src/gpu/batches/GrAtlasTextBatch.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698