'<(skia_src_path)/gpu/GrSurfacePriv.h',
'<(skia_src_path)/gpu/GrSurface.cpp',
'<(skia_src_path)/gpu/GrTemplates.h',
+ '<(skia_src_path)/gpu/GrTextBlobCache.cpp',
+ '<(skia_src_path)/gpu/GrTextBlobCache.h',
'<(skia_src_path)/gpu/GrTextContext.cpp',
'<(skia_src_path)/gpu/GrTextContext.h',
'<(skia_src_path)/gpu/GrFontCache.cpp',
static unsigned ScalarsPerGlyph(GlyphPositioning pos);
friend class GrAtlasTextContext;
+ friend class GrTextBlobCache;
friend class GrTextContext;
friend class SkBaseDevice;
friend class SkTextBlobBuilder;
class GrResourceEntry;
class GrResourceCache;
class GrTestTarget;
+class GrTextBlobCache;
class GrTextContext;
class GrTextureParams;
class GrVertexBuffer;
GrBatchFontCache* getBatchFontCache() { return fBatchFontCache; }
GrFontCache* getFontCache() { return fFontCache; }
GrLayerCache* getLayerCache() { return fLayerCache.get(); }
+ GrTextBlobCache* getTextBlobCache() { return fTextBlobCache; }
GrDrawTarget* getTextTarget();
const GrIndexBuffer* getQuadIndexBuffer() const;
GrAARectRenderer* getAARectRenderer() { return fAARectRenderer; }
GrBatchFontCache* fBatchFontCache;
GrFontCache* fFontCache;
SkAutoTDelete<GrLayerCache> fLayerCache;
+ SkAutoTDelete<GrTextBlobCache> fTextBlobCache;
GrPathRendererChain* fPathRendererChain;
GrSoftwarePathRenderer* fSoftwarePathRenderer;
#include "GrFontScaler.h"
#include "GrIndexBuffer.h"
#include "GrStrokeInfo.h"
+#include "GrTextBlobCache.h"
#include "GrTexturePriv.h"
#include "SkAutoKern.h"
GrAtlasTextContext::GrAtlasTextContext(GrContext* context,
SkGpuDevice* gpuDevice,
const SkDeviceProperties& properties)
- : INHERITED(context, gpuDevice, properties) {
+ : INHERITED(context, gpuDevice, properties) {
+ // We overallocate vertices in our textblobs based on the assumption that A8 has the greatest
+ // vertexStride
+ SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
+ vertex_attribute_changed);
fCurrStrike = NULL;
-}
-
-void GrAtlasTextContext::ClearCacheEntry(uint32_t key, BitmapTextBlob** blob) {
- (*blob)->unref();
-}
-
-GrAtlasTextContext::~GrAtlasTextContext() {
- fCache.foreach(&GrAtlasTextContext::ClearCacheEntry);
+ fCache = context->getTextBlobCache();
}
GrAtlasTextContext* GrAtlasTextContext::Create(GrContext* context,
return SkGlyphCache::DetachCache(run->fTypeface, run->fDescriptor.getDesc());
}
-inline void GrAtlasTextContext::BlobGlyphCount(int* glyphCount, int* runCount,
- const SkTextBlob* blob) {
- SkTextBlob::RunIterator itCounter(blob);
- for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
- *glyphCount += itCounter.glyphCount();
- }
-}
-
-GrAtlasTextContext::BitmapTextBlob* GrAtlasTextContext::CreateBlob(int glyphCount,
- int runCount) {
- // We allocate size for the BitmapTextBlob itself, plus size for the vertices array,
- // and size for the glyphIds array.
- SK_COMPILE_ASSERT(kGrayTextVASize >= kColorTextVASize && kGrayTextVASize >= kLCDTextVASize,
- vertex_attribute_changed);
- size_t verticesCount = glyphCount * kVerticesPerGlyph * kGrayTextVASize;
- size_t length = sizeof(BitmapTextBlob) +
- verticesCount +
- glyphCount * sizeof(GrGlyph::PackedID) +
- sizeof(BitmapTextBlob::Run) * runCount;
-
- BitmapTextBlob* cacheBlob = SkNEW_PLACEMENT(sk_malloc_throw(length), BitmapTextBlob);
-
- // setup offsets for vertices / glyphs
- cacheBlob->fVertices = sizeof(BitmapTextBlob) + reinterpret_cast<unsigned char*>(cacheBlob);
- cacheBlob->fGlyphIDs =
- reinterpret_cast<GrGlyph::PackedID*>(cacheBlob->fVertices + verticesCount);
- cacheBlob->fRuns = reinterpret_cast<BitmapTextBlob::Run*>(cacheBlob->fGlyphIDs + glyphCount);
-
- // Initialize runs
- for (int i = 0; i < runCount; i++) {
- SkNEW_PLACEMENT(&cacheBlob->fRuns[i], BitmapTextBlob::Run);
- }
- cacheBlob->fRunCount = runCount;
- return cacheBlob;
-}
-
void GrAtlasTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip,
const SkPaint& skPaint, const SkMatrix& viewMatrix,
const SkTextBlob* blob, SkScalar x, SkScalar y,
SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
- BitmapTextBlob* cacheBlob;
- BitmapTextBlob** foundBlob = fCache.find(blob->uniqueID());
+ uint32_t uniqueID = blob->uniqueID();
+ BitmapTextBlob* cacheBlob = fCache->find(uniqueID);
SkIRect clipRect;
clip.getConservativeBounds(rt->width(), rt->height(), &clipRect);
- if (foundBlob) {
- cacheBlob = *foundBlob;
+ if (cacheBlob) {
if (MustRegenerateBlob(*cacheBlob, skPaint, viewMatrix, x, y)) {
// We have to remake the blob because changes may invalidate our masks.
// TODO we could probably get away reuse most of the time if the pointer is unique,
// but we'd have to clear the subrun information
- cacheBlob->unref();
- int glyphCount = 0;
- int runCount = 0;
- BlobGlyphCount(&glyphCount, &runCount, blob);
- cacheBlob = CreateBlob(glyphCount, runCount);
- fCache.set(blob->uniqueID(), cacheBlob);
+ fCache->remove(cacheBlob);
+ cacheBlob = fCache->createCachedBlob(blob, kGrayTextVASize);
this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y, drawFilter,
clipRect);
+ } else {
+ fCache->makeMRU(cacheBlob);
}
} else {
- int glyphCount = 0;
- int runCount = 0;
- BlobGlyphCount(&glyphCount, &runCount, blob);
- cacheBlob = CreateBlob(glyphCount, runCount);
- fCache.set(blob->uniqueID(), cacheBlob);
+ cacheBlob = fCache->createCachedBlob(blob, kGrayTextVASize);
this->regenerateTextBlob(cacheBlob, skPaint, viewMatrix, blob, x, y, drawFilter, clipRect);
}
const char text[], size_t byteLength,
SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
int glyphCount = skPaint.countText(text, byteLength);
- SkAutoTUnref<BitmapTextBlob> blob(CreateBlob(glyphCount, 1));
+ SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
blob->fViewMatrix = viewMatrix;
blob->fX = x;
blob->fY = y;
const SkScalar pos[], int scalarsPerPosition,
const SkPoint& offset, const SkIRect& regionClipBounds) {
int glyphCount = skPaint.countText(text, byteLength);
- SkAutoTUnref<BitmapTextBlob> blob(CreateBlob(glyphCount, 1));
+ SkAutoTUnref<BitmapTextBlob> blob(fCache->createBlob(glyphCount, 1, kGrayTextVASize));
blob->fStyle = skPaint.getStyle();
blob->fViewMatrix = viewMatrix;
#include "GrBatchAtlas.h"
#include "GrGeometryProcessor.h"
#include "SkDescriptor.h"
+#include "GrMemoryPool.h"
#include "SkTextBlob.h"
-#include "SkTHash.h"
+#include "SkTInternalLList.h"
class GrBatchTextStrike;
class GrPipelineBuilder;
+class GrTextBlobCache;
/*
* This class implements GrTextContext using standard bitmap fonts, and can also process textblobs.
public:
static GrAtlasTextContext* Create(GrContext*, SkGpuDevice*, const SkDeviceProperties&);
- virtual ~GrAtlasTextContext();
-
private:
GrAtlasTextContext(GrContext*, SkGpuDevice*, const SkDeviceProperties&);
* TODO this is currently a bug
*/
struct BitmapTextBlob : public SkRefCnt {
+ SK_DECLARE_INTERNAL_LLIST_INTERFACE(BitmapTextBlob);
+
/*
* Each Run inside of the blob can have its texture coordinates regenerated if required.
* To determine if regeneration is necessary, fAtlasGeneration is used. If there have been
SkScalar fY;
SkPaint::Style fStyle;
int fRunCount;
+ uint32_t fUniqueID;
+ GrMemoryPool* fPool;
// all glyph / vertex offsets are into these pools.
unsigned char* fVertices;
GrGlyph::PackedID* fGlyphIDs;
Run* fRuns;
+ static const uint32_t& GetKey(const BitmapTextBlob& blob) {
+ return blob.fUniqueID;
+ }
+
static uint32_t Hash(const uint32_t& key) {
return SkChecksum::Mix(key);
}
- void operator delete(void* p) { sk_free(p); }
+ void operator delete(void* p) {
+ BitmapTextBlob* blob = reinterpret_cast<BitmapTextBlob*>(p);
+ blob->fPool->release(p);
+ }
void* operator new(size_t) {
SkFAIL("All blobs are created by placement new.");
return sk_malloc_throw(0);
typedef BitmapTextBlob::Run Run;
typedef Run::SubRunInfo PerSubRunInfo;
- BitmapTextBlob* CreateBlob(int glyphCount, int runCount);
-
void appendGlyph(BitmapTextBlob*, int runIndex, GrGlyph::PackedID, int left, int top,
GrColor color, GrFontScaler*, const SkIRect& clipRect);
const SkTextBlob* blob, SkScalar x, SkScalar y,
SkDrawFilter* drawFilter, const SkIRect& clipRect);
- // TODO this currently only uses the public interface of SkTextBlob, however, I may need to add
- // functionality to it while looping over the runs so I'm putting this here for the time being.
- // If this lands in Chrome without changes, move it to SkTextBlob.
- static inline void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob*);
-
GrBatchTextStrike* fCurrStrike;
+ GrTextBlobCache* fCache;
- // TODO use real cache
- static void ClearCacheEntry(uint32_t key, BitmapTextBlob**);
- SkTHashMap<uint32_t, BitmapTextBlob*, BitmapTextBlob::Hash> fCache;
-
+ friend class GrTextBlobCache;
friend class BitmapTextBatch;
typedef GrTextContext INHERITED;
#include "GrStencilAndCoverTextContext.h"
#include "GrStrokeInfo.h"
#include "GrSurfacePriv.h"
+#include "GrTextBlobCache.h"
#include "GrTexturePriv.h"
#include "GrTraceMarker.h"
#include "GrTracing.h"
// GrBatchFontCache will eventually replace GrFontCache
fBatchFontCache = SkNEW(GrBatchFontCache);
fBatchFontCache->init(this);
+
+ fTextBlobCache.reset(SkNEW(GrTextBlobCache));
}
GrContext::~GrContext() {
#include "GrDrawTargetCaps.h"
#include "GrPath.h"
#include "GrPipeline.h"
+#include "GrMemoryPool.h"
#include "GrRenderTarget.h"
#include "GrRenderTargetPriv.h"
#include "GrSurfacePriv.h"
GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) {
SkDEBUGCODE(fAllocationCnt = 0);
+ SkDEBUGCODE(fAllocBlockCnt = 0);
minAllocSize = SkTMax<size_t>(minAllocSize, 1 << 10);
fMinAllocSize = GrSizeAlignUp(minAllocSize + kPerAllocPad, kAlignment),
fPreallocSize = GrSizeAlignUp(preallocSize + kPerAllocPad, kAlignment);
fPreallocSize = SkTMax(fPreallocSize, fMinAllocSize);
+ fSize = fPreallocSize;
fHead = CreateBlock(fPreallocSize);
fTail = fHead;
SkASSERT(NULL == fTail->fNext);
fTail->fNext = block;
fTail = block;
+ fSize += block->fSize;
+ SkDEBUGCODE(++fAllocBlockCnt);
}
SkASSERT(fTail->fFreeSize >= size);
intptr_t ptr = fTail->fCurrPtr;
fTail->fCurrPtr += size;
fTail->fFreeSize -= size;
fTail->fLiveCount += 1;
+
SkDEBUGCODE(++fAllocationCnt);
VALIDATE;
return reinterpret_cast<void*>(ptr);
if (1 == block->fLiveCount) {
// the head block is special, it is reset rather than deleted
if (fHead == block) {
- fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) +
- kHeaderSize;
+ fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) + kHeaderSize;
fHead->fLiveCount = 0;
fHead->fFreeSize = fPreallocSize;
} else {
SkASSERT(fTail == block);
fTail = prev;
}
+ fSize -= block->fSize;
DeleteBlock(block);
+ SkDEBUGCODE(fAllocBlockCnt--);
}
} else {
--block->fLiveCount;
}
GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t size) {
+ size_t paddedSize = size + kHeaderSize;
BlockHeader* block =
- reinterpret_cast<BlockHeader*>(sk_malloc_throw(size + kHeaderSize));
+ reinterpret_cast<BlockHeader*>(sk_malloc_throw(paddedSize));
// we assume malloc gives us aligned memory
SkASSERT(!(reinterpret_cast<intptr_t>(block) % kAlignment));
block->fLiveCount = 0;
block->fFreeSize = size;
block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize;
block->fPrevPtr = 0; // gcc warns on assigning NULL to an intptr_t.
+ block->fSize = paddedSize;
return block;
}
} while ((block = block->fNext));
SkASSERT(allocCount == fAllocationCnt);
SkASSERT(prev == fTail);
+ SkASSERT(fAllocBlockCnt != 0 || fSize == fPreallocSize);
#endif
}
*/
bool isEmpty() const { return fTail == fHead && !fHead->fLiveCount; }
+ /**
+ * Returns the total allocated size of the GrMemoryPool
+ */
+ size_t size() const { return fSize; }
+
private:
struct BlockHeader;
intptr_t fCurrPtr; ///< ptr to the start of blocks free space.
intptr_t fPrevPtr; ///< ptr to the last allocation made
size_t fFreeSize; ///< amount of free space left in the block.
+ size_t fSize; ///< total allocated size of the block
};
enum {
kHeaderSize = GR_CT_ALIGN_UP(sizeof(BlockHeader), kAlignment),
kPerAllocPad = GR_CT_ALIGN_UP(sizeof(BlockHeader*), kAlignment),
};
+ size_t fSize;
size_t fPreallocSize;
size_t fMinAllocSize;
BlockHeader* fHead;
BlockHeader* fTail;
#ifdef SK_DEBUG
int fAllocationCnt;
+ int fAllocBlockCnt;
#endif
};
--- /dev/null
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrTextBlobCache.h"
+
+static const int kVerticesPerGlyph = 4;
+
+GrTextBlobCache::~GrTextBlobCache() {
+ SkTDynamicHash<BitmapTextBlob, uint32_t>::Iter iter(&fCache);
+ while (!iter.done()) {
+ (&(*iter))->unref();
+ ++iter;
+ }
+}
+
+GrAtlasTextContext::BitmapTextBlob* GrTextBlobCache::createBlob(int glyphCount, int runCount,
+ size_t maxVASize) {
+ // We allocate size for the BitmapTextBlob itself, plus size for the vertices array,
+ // and size for the glyphIds array.
+ size_t verticesCount = glyphCount * kVerticesPerGlyph * maxVASize;
+ size_t size = sizeof(BitmapTextBlob) +
+ verticesCount +
+ glyphCount * sizeof(GrGlyph::PackedID) +
+ sizeof(BitmapTextBlob::Run) * runCount;
+
+ BitmapTextBlob* cacheBlob = SkNEW_PLACEMENT(fPool.allocate(size), BitmapTextBlob);
+
+ // setup offsets for vertices / glyphs
+ cacheBlob->fVertices = sizeof(BitmapTextBlob) + reinterpret_cast<unsigned char*>(cacheBlob);
+ cacheBlob->fGlyphIDs =
+ reinterpret_cast<GrGlyph::PackedID*>(cacheBlob->fVertices + verticesCount);
+ cacheBlob->fRuns = reinterpret_cast<BitmapTextBlob::Run*>(cacheBlob->fGlyphIDs + glyphCount);
+
+ // Initialize runs
+ for (int i = 0; i < runCount; i++) {
+ SkNEW_PLACEMENT(&cacheBlob->fRuns[i], BitmapTextBlob::Run);
+ }
+ cacheBlob->fRunCount = runCount;
+ cacheBlob->fPool = &fPool;
+ return cacheBlob;
+}
--- /dev/null
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrTextBlobCache_DEFINED
+#define GrTextBlobCache_DEFINED
+
+#include "GrAtlasTextContext.h"
+#include "SkTDynamicHash.h"
+#include "SkTextBlob.h"
+
+class GrTextBlobCache {
+public:
+ typedef GrAtlasTextContext::BitmapTextBlob BitmapTextBlob;
+
+ GrTextBlobCache() : fPool(kPreAllocSize, kMinGrowthSize) {}
+ ~GrTextBlobCache();
+
+ // creates an uncached blob
+ BitmapTextBlob* createBlob(int glyphCount, int runCount, size_t maxVASize);
+
+ BitmapTextBlob* createCachedBlob(const SkTextBlob* blob, size_t maxVAStride) {
+ int glyphCount = 0;
+ int runCount = 0;
+ BlobGlyphCount(&glyphCount, &runCount, blob);
+ BitmapTextBlob* cacheBlob = this->createBlob(glyphCount, runCount, maxVAStride);
+ cacheBlob->fUniqueID = blob->uniqueID();
+ this->add(cacheBlob);
+ return cacheBlob;
+ }
+
+ BitmapTextBlob* find(uint32_t uniqueID) {
+ return fCache.find(uniqueID);
+ }
+
+ void remove(BitmapTextBlob* blob) {
+ fCache.remove(blob->fUniqueID);
+ fBlobList.remove(blob);
+ blob->unref();
+ }
+
+ void add(BitmapTextBlob* blob) {
+ fCache.add(blob);
+ fBlobList.addToHead(blob);
+
+ // If we are overbudget, then unref until we are below budget again
+ if (fPool.size() > kBudget) {
+ BitmapBlobList::Iter iter;
+ iter.init(fBlobList, BitmapBlobList::Iter::kTail_IterStart);
+ BitmapTextBlob* lruBlob = iter.get();
+ SkASSERT(lruBlob);
+ do {
+ fCache.remove(lruBlob->fUniqueID);
+ fBlobList.remove(lruBlob);
+ lruBlob->unref();
+ iter.prev();
+ } while (fPool.size() > kBudget && (lruBlob = iter.get()));
+ }
+ }
+
+ void makeMRU(BitmapTextBlob* blob) {
+ if (fBlobList.head() == blob) {
+ return;
+ }
+
+ fBlobList.remove(blob);
+ fBlobList.addToHead(blob);
+ }
+
+private:
+ // TODO move to SkTextBlob
+ void BlobGlyphCount(int* glyphCount, int* runCount, const SkTextBlob* blob) {
+ SkTextBlob::RunIterator itCounter(blob);
+ for (; !itCounter.done(); itCounter.next(), (*runCount)++) {
+ *glyphCount += itCounter.glyphCount();
+ }
+ }
+
+ typedef SkTInternalLList<BitmapTextBlob> BitmapBlobList;
+
+ // Budget was chosen to be ~4 megabytes. The min alloc and pre alloc sizes in the pool are
+ // based off of the largest cached textblob I have seen in the skps(a couple of kilobytes).
+ static const int kPreAllocSize = 1 << 17;
+ static const int kMinGrowthSize = 1 << 17;
+ static const int kBudget = 1 << 20;
+ BitmapBlobList fBlobList;
+ SkTDynamicHash<BitmapTextBlob, uint32_t> fCache;
+ GrMemoryPool fPool;
+};
+
+#endif