Index: src/gpu/GrStencilAndCoverTextContext.cpp |
diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp |
index 92f885b16d57e1486e046009aa61854c20cf7897..3d600eea3a1732fac1689d20cb96c9973186968b 100644 |
--- a/src/gpu/GrStencilAndCoverTextContext.cpp |
+++ b/src/gpu/GrStencilAndCoverTextContext.cpp |
@@ -17,15 +17,27 @@ |
#include "SkDrawProcs.h" |
#include "SkGlyphCache.h" |
#include "SkGpuDevice.h" |
+#include "SkGrPriv.h" |
#include "SkPath.h" |
#include "SkTextMapStateProc.h" |
#include "SkTextFormatParams.h" |
#include "batches/GrDrawPathBatch.h" |
+template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) { |
+ SkASSERT(*val); |
+ delete *val; |
+} |
+ |
+template<typename T> static void delete_hash_table_entry(T* val) { |
+ SkASSERT(*val); |
+ delete *val; |
+} |
+ |
GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context, |
const SkSurfaceProps& surfaceProps) |
- : INHERITED(context, surfaceProps) { |
+ : INHERITED(context, surfaceProps), |
+ fCacheSize(0) { |
} |
GrStencilAndCoverTextContext* |
@@ -38,13 +50,11 @@ GrStencilAndCoverTextContext::Create(GrContext* context, const SkSurfaceProps& s |
} |
GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { |
+ fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>); |
+ fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>); |
} |
-bool GrStencilAndCoverTextContext::canDraw(const GrRenderTarget* rt, |
- const GrClip& clip, |
- const GrPaint& paint, |
- const SkPaint& skPaint, |
- const SkMatrix& viewMatrix) { |
+bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) { |
if (skPaint.getRasterizer()) { |
return false; |
} |
@@ -68,10 +78,12 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* |
const char text[], |
size_t byteLength, |
SkScalar x, SkScalar y, |
- const SkIRect& regionClipBounds) { |
+ const SkIRect& clipBounds) { |
TextRun run(skPaint); |
+ GrPipelineBuilder pipelineBuilder(paint, rt, clip); |
run.setText(text, byteLength, x, y, fContext, &fSurfaceProps); |
- run.draw(dc, rt, clip, paint, viewMatrix, regionClipBounds, fFallbackTextContext, skPaint); |
+ run.draw(dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds, |
+ fFallbackTextContext, skPaint); |
} |
void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt, |
@@ -84,10 +96,137 @@ void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarg |
const SkScalar pos[], |
int scalarsPerPosition, |
const SkPoint& offset, |
- const SkIRect& regionClipBounds) { |
+ const SkIRect& clipBounds) { |
TextRun run(skPaint); |
+ GrPipelineBuilder pipelineBuilder(paint, rt, clip); |
run.setPosText(text, byteLength, pos, scalarsPerPosition, offset, fContext, &fSurfaceProps); |
- run.draw(dc, rt, clip, paint, viewMatrix, regionClipBounds, fFallbackTextContext, skPaint); |
+ run.draw(dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds, |
+ fFallbackTextContext, skPaint); |
+} |
+ |
+void GrStencilAndCoverTextContext::drawTextBlob(GrDrawContext* dc, GrRenderTarget* rt, |
+ const GrClip& clip, const SkPaint& skPaint, |
+ const SkMatrix& viewMatrix, |
+ const SkTextBlob* skBlob, SkScalar x, SkScalar y, |
+ SkDrawFilter* drawFilter, |
+ const SkIRect& clipBounds) { |
+ if (!this->internalCanDraw(skPaint)) { |
+ fFallbackTextContext->drawTextBlob(dc, rt, clip, skPaint, viewMatrix, skBlob, x, y, |
+ drawFilter, clipBounds); |
+ return; |
+ } |
+ |
+ if (drawFilter || skPaint.getPathEffect()) { |
+ // This draw can't be cached. |
+ INHERITED::drawTextBlob(dc, rt, clip, skPaint, viewMatrix, skBlob, x, y, drawFilter, |
+ clipBounds); |
+ return; |
+ } |
+ |
+ if (fContext->abandoned()) { |
+ return; |
+ } |
+ |
+ GrPaint paint; |
+ if (!SkPaintToGrPaint(fContext, skPaint, viewMatrix, &paint)) { |
+ return; |
+ } |
+ |
+ const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint); |
+ GrPipelineBuilder pipelineBuilder(paint, rt, clip); |
+ |
+ TextBlob::Iter iter(blob); |
+ for (TextRun* run = iter.get(); run; run = iter.next()) { |
+ run->draw(dc, &pipelineBuilder, paint.getColor(), viewMatrix, x, y, clipBounds, |
+ fFallbackTextContext, skPaint); |
+ } |
+} |
+ |
+const GrStencilAndCoverTextContext::TextBlob& |
+GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob, |
+ const SkPaint& skPaint) { |
+ // The font-related parameters are baked into the text blob and will override this skPaint, so |
+ // the only remaining properties that can affect a TextBlob are the ones related to stroke. |
+ if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path. |
+ if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) { |
+ fLRUList.remove(*found); |
+ fLRUList.addToTail(*found); |
+ return **found; |
+ } |
+ TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint, fContext, |
+ &fSurfaceProps); |
+ this->purgeToFit(*blob); |
+ fBlobIdCache.set(skBlob->uniqueID(), blob); |
+ fLRUList.addToTail(blob); |
+ fCacheSize += blob->cpuMemorySize(); |
+ return *blob; |
+ } else { |
+ GrStrokeInfo stroke(skPaint); |
+ SkSTArray<4, uint32_t, true> key; |
+ key.reset(1 + stroke.computeUniqueKeyFragmentData32Cnt()); |
+ key[0] = skBlob->uniqueID(); |
+ stroke.asUniqueKeyFragment(&key[1]); |
+ if (TextBlob** found = fBlobKeyCache.find(key)) { |
+ fLRUList.remove(*found); |
+ fLRUList.addToTail(*found); |
+ return **found; |
+ } |
+ TextBlob* blob = new TextBlob(key, skBlob, skPaint, fContext, &fSurfaceProps); |
+ this->purgeToFit(*blob); |
+ fBlobKeyCache.set(blob); |
+ fLRUList.addToTail(blob); |
+ fCacheSize += blob->cpuMemorySize(); |
+ return *blob; |
+ } |
+} |
+ |
+void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) { |
+ static const int maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs. |
+ |
+ int maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize(); |
+ while (fCacheSize && fCacheSize > maxSizeForNewBlob) { |
+ TextBlob* lru = fLRUList.head(); |
+ if (1 == lru->key().count()) { |
+ // 1-length keys are unterstood to be the blob id. |
+ fBlobIdCache.remove(lru->key()[0]); |
+ } else { |
+ fBlobKeyCache.remove(lru->key()); |
+ } |
+ fLRUList.remove(lru); |
+ fCacheSize -= lru->cpuMemorySize(); |
+ delete lru; |
+ } |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////////////////////////// |
+ |
+void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob, const SkPaint& skPaint, |
+ GrContext* ctx, const SkSurfaceProps* props) { |
+ fCpuMemorySize = sizeof(TextBlob); |
+ SkPaint runPaint(skPaint); |
+ for (SkTextBlob::RunIterator iter(skBlob); !iter.done(); iter.next()) { |
+ iter.applyFontToPaint(&runPaint); // No need to re-seed the paint. |
+ TextRun* run = SkNEW_INSERT_AT_LLIST_TAIL(this, TextRun, (runPaint)); |
+ |
+ const char* text = reinterpret_cast<const char*>(iter.glyphs()); |
+ size_t byteLength = sizeof(uint16_t) * iter.glyphCount(); |
+ const SkPoint& runOffset = iter.offset(); |
+ |
+ switch (iter.positioning()) { |
+ case SkTextBlob::kDefault_Positioning: |
+ run->setText(text, byteLength, runOffset.fX, runOffset.fY, ctx, props); |
+ break; |
+ case SkTextBlob::kHorizontal_Positioning: |
+ run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY), |
+ ctx, props); |
+ break; |
+ case SkTextBlob::kFull_Positioning: |
+ run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0), ctx, props); |
+ break; |
+ } |
+ |
+ fCpuMemorySize += run->cpuMemorySize(); |
+ } |
} |
//////////////////////////////////////////////////////////////////////////////////////////////////// |
@@ -120,7 +259,8 @@ private: |
GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke) |
: fStroke(fontAndStroke), |
- fFont(fontAndStroke) { |
+ fFont(fontAndStroke), |
+ fTotalGlyphCount(0) { |
SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported. |
// Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path |
@@ -168,7 +308,7 @@ GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke) |
} |
// When drawing from canonically sized paths, the actual local coords are fTextRatio * coords. |
- fLocalMatrix.setScale(fTextRatio, fTextRatio); |
+ fLocalMatrixTemplate.setScale(fTextRatio, fTextRatio); |
} |
GrStencilAndCoverTextContext::TextRun::~TextRun() { |
@@ -182,9 +322,10 @@ void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t by |
SkAutoGlyphCacheNoGamma autoGlyphCache(fFont, surfaceProps, nullptr); |
SkGlyphCache* glyphCache = autoGlyphCache.getCache(); |
+ fTotalGlyphCount = fFont.countText(text, byteLength); |
fDraw.reset(GrPathRangeDraw::Create(this->createGlyphs(ctx, glyphCache), |
GrPathRendering::kTranslate_PathTransformType, |
- fFont.countText(text, byteLength))); |
+ fTotalGlyphCount)); |
SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc(); |
@@ -250,9 +391,10 @@ void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t |
SkAutoGlyphCacheNoGamma autoGlyphCache(fFont, surfaceProps, nullptr); |
SkGlyphCache* glyphCache = autoGlyphCache.getCache(); |
+ fTotalGlyphCount = fFont.countText(text, byteLength); |
fDraw.reset(GrPathRangeDraw::Create(this->createGlyphs(ctx, glyphCache), |
GrPathRendering::kTranslate_PathTransformType, |
- fFont.countText(text, byteLength))); |
+ fTotalGlyphCount)); |
SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc(); |
@@ -323,19 +465,19 @@ inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& gl |
} |
void GrStencilAndCoverTextContext::TextRun::draw(GrDrawContext* dc, |
- GrRenderTarget* rt, |
- const GrClip& clip, |
- const GrPaint& paint, |
+ GrPipelineBuilder* pipelineBuilder, |
+ GrColor color, |
const SkMatrix& viewMatrix, |
- const SkIRect& regionClipBounds, |
+ SkScalar x, SkScalar y, |
+ const SkIRect& clipBounds, |
GrTextContext* fallbackTextContext, |
const SkPaint& originalSkPaint) const { |
SkASSERT(fDraw); |
+ SkASSERT(pipelineBuilder->getRenderTarget()->isStencilBufferMultisampled() || |
+ !fFont.isAntiAlias()); |
if (fDraw->count()) { |
- GrPipelineBuilder pipelineBuilder(paint, rt, clip); |
- SkASSERT(rt->isStencilBufferMultisampled() || !paint.isAntiAlias()); |
- pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, paint.isAntiAlias()); |
+ pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias()); |
GR_STATIC_CONST_SAME_STENCIL(kStencilPass, |
kZero_StencilOp, |
@@ -345,12 +487,17 @@ void GrStencilAndCoverTextContext::TextRun::draw(GrDrawContext* dc, |
0x0000, |
0xffff); |
- *pipelineBuilder.stencil() = kStencilPass; |
+ *pipelineBuilder->stencil() = kStencilPass; |
SkMatrix drawMatrix(viewMatrix); |
+ drawMatrix.preTranslate(x, y); |
drawMatrix.preScale(fTextRatio, fTextRatio); |
- dc->drawPathsFromRange(&pipelineBuilder, drawMatrix, fLocalMatrix, paint.getColor(), fDraw, |
+ SkMatrix& localMatrix = fLocalMatrixTemplate; |
+ localMatrix.setTranslateX(x); |
+ localMatrix.setTranslateY(y); |
+ |
+ dc->drawPathsFromRange(pipelineBuilder, drawMatrix, localMatrix, color, fDraw, |
GrPathRendering::kWinding_FillType); |
} |
@@ -361,9 +508,21 @@ void GrStencilAndCoverTextContext::TextRun::draw(GrDrawContext* dc, |
fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio); |
} |
- fallbackTextContext->drawTextBlob(dc, rt, clip, fallbackSkPaint, viewMatrix, |
- fFallbackTextBlob, 0, 0, nullptr, regionClipBounds); |
+ fallbackTextContext->drawTextBlob(dc, pipelineBuilder->getRenderTarget(), |
+ pipelineBuilder->clip(), fallbackSkPaint, viewMatrix, |
+ fFallbackTextBlob, x, y, nullptr, clipBounds); |
+ } |
+} |
+ |
+int GrStencilAndCoverTextContext::TextRun::cpuMemorySize() const { |
+ int size = sizeof(TextRun) + fTotalGlyphCount * (sizeof(uint16_t) + 2 * sizeof(float)); |
+ if (fDraw) { |
+ size += sizeof(GrPathRangeDraw); |
+ } |
+ if (fFallbackTextBlob) { |
+ size += sizeof(SkTextBlob); |
} |
+ return size; |
} |
//////////////////////////////////////////////////////////////////////////////////////////////////// |