diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gpu/GrAtlasTextContext.cpp | 79 | ||||
-rw-r--r-- | src/gpu/GrAtlasTextContext.h | 32 | ||||
-rwxr-xr-x | src/gpu/GrContext.cpp | 3 | ||||
-rw-r--r-- | src/gpu/GrDrawTarget.cpp | 1 | ||||
-rw-r--r-- | src/gpu/GrMemoryPool.cpp | 15 | ||||
-rw-r--r-- | src/gpu/GrMemoryPool.h | 8 | ||||
-rw-r--r-- | src/gpu/GrTextBlobCache.cpp | 45 | ||||
-rw-r--r-- | src/gpu/GrTextBlobCache.h | 94 |
8 files changed, 197 insertions, 80 deletions
diff --git a/src/gpu/GrAtlasTextContext.cpp b/src/gpu/GrAtlasTextContext.cpp index 38edc8e558..d6bb3e0797 100644 --- a/src/gpu/GrAtlasTextContext.cpp +++ b/src/gpu/GrAtlasTextContext.cpp @@ -15,6 +15,7 @@ #include "GrFontScaler.h" #include "GrIndexBuffer.h" #include "GrStrokeInfo.h" +#include "GrTextBlobCache.h" #include "GrTexturePriv.h" #include "SkAutoKern.h" @@ -67,16 +68,13 @@ static size_t get_vertex_stride(GrMaskFormat maskFormat) { 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, @@ -110,72 +108,29 @@ inline SkGlyphCache* GrAtlasTextContext::setupCache(BitmapTextBlob::Run* run, 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); } @@ -269,7 +224,7 @@ void GrAtlasTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip, 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; @@ -383,7 +338,7 @@ void GrAtlasTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip, 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; diff --git a/src/gpu/GrAtlasTextContext.h b/src/gpu/GrAtlasTextContext.h index a75eb0e469..8fa6bb5d4a 100644 --- a/src/gpu/GrAtlasTextContext.h +++ b/src/gpu/GrAtlasTextContext.h @@ -13,11 +13,13 @@ #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. @@ -27,8 +29,6 @@ class GrAtlasTextContext : public GrTextContext { public: static GrAtlasTextContext* Create(GrContext*, SkGpuDevice*, const SkDeviceProperties&); - virtual ~GrAtlasTextContext(); - private: GrAtlasTextContext(GrContext*, SkGpuDevice*, const SkDeviceProperties&); @@ -59,6 +59,8 @@ private: * 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 @@ -128,17 +130,26 @@ private: 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); @@ -153,8 +164,6 @@ private: 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); @@ -190,17 +199,10 @@ private: 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; diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 40e714b152..89429f186d 100755 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -33,6 +33,7 @@ #include "GrStencilAndCoverTextContext.h" #include "GrStrokeInfo.h" #include "GrSurfacePriv.h" +#include "GrTextBlobCache.h" #include "GrTexturePriv.h" #include "GrTraceMarker.h" #include "GrTracing.h" @@ -136,6 +137,8 @@ void GrContext::initCommon() { // GrBatchFontCache will eventually replace GrFontCache fBatchFontCache = SkNEW(GrBatchFontCache); fBatchFontCache->init(this); + + fTextBlobCache.reset(SkNEW(GrTextBlobCache)); } GrContext::~GrContext() { diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp index 420f9eae66..7eef92290e 100644 --- a/src/gpu/GrDrawTarget.cpp +++ b/src/gpu/GrDrawTarget.cpp @@ -13,6 +13,7 @@ #include "GrDrawTargetCaps.h" #include "GrPath.h" #include "GrPipeline.h" +#include "GrMemoryPool.h" #include "GrRenderTarget.h" #include "GrRenderTargetPriv.h" #include "GrSurfacePriv.h" diff --git a/src/gpu/GrMemoryPool.cpp b/src/gpu/GrMemoryPool.cpp index 5009f20152..e59ed83d27 100644 --- a/src/gpu/GrMemoryPool.cpp +++ b/src/gpu/GrMemoryPool.cpp @@ -15,11 +15,13 @@ 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; @@ -50,6 +52,8 @@ void* GrMemoryPool::allocate(size_t size) { SkASSERT(NULL == fTail->fNext); fTail->fNext = block; fTail = block; + fSize += block->fSize; + SkDEBUGCODE(++fAllocBlockCnt); } SkASSERT(fTail->fFreeSize >= size); intptr_t ptr = fTail->fCurrPtr; @@ -61,6 +65,7 @@ void* GrMemoryPool::allocate(size_t size) { fTail->fCurrPtr += size; fTail->fFreeSize -= size; fTail->fLiveCount += 1; + SkDEBUGCODE(++fAllocationCnt); VALIDATE; return reinterpret_cast<void*>(ptr); @@ -73,8 +78,7 @@ void GrMemoryPool::release(void* p) { 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 { @@ -88,7 +92,9 @@ void GrMemoryPool::release(void* p) { SkASSERT(fTail == block); fTail = prev; } + fSize -= block->fSize; DeleteBlock(block); + SkDEBUGCODE(fAllocBlockCnt--); } } else { --block->fLiveCount; @@ -103,14 +109,16 @@ void GrMemoryPool::release(void* p) { } 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; } @@ -157,5 +165,6 @@ void GrMemoryPool::validate() { } while ((block = block->fNext)); SkASSERT(allocCount == fAllocationCnt); SkASSERT(prev == fTail); + SkASSERT(fAllocBlockCnt != 0 || fSize == fPreallocSize); #endif } diff --git a/src/gpu/GrMemoryPool.h b/src/gpu/GrMemoryPool.h index 5ab8958a8a..4de641d9fc 100644 --- a/src/gpu/GrMemoryPool.h +++ b/src/gpu/GrMemoryPool.h @@ -43,6 +43,11 @@ public: */ 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; @@ -60,6 +65,7 @@ private: 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 { @@ -68,12 +74,14 @@ private: 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 }; diff --git a/src/gpu/GrTextBlobCache.cpp b/src/gpu/GrTextBlobCache.cpp new file mode 100644 index 0000000000..9141b1712a --- /dev/null +++ b/src/gpu/GrTextBlobCache.cpp @@ -0,0 +1,45 @@ +/* + * 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; +} diff --git a/src/gpu/GrTextBlobCache.h b/src/gpu/GrTextBlobCache.h new file mode 100644 index 0000000000..7a2b2a79a1 --- /dev/null +++ b/src/gpu/GrTextBlobCache.h @@ -0,0 +1,94 @@ +/* + * 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 |