aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gn/tests.gni1
-rw-r--r--include/private/GrSurfaceProxy.h15
-rw-r--r--src/atlastext/SkAtlasTextTarget.cpp14
-rw-r--r--src/gpu/GrContext.cpp2
-rw-r--r--src/gpu/GrDeferredUpload.h3
-rw-r--r--src/gpu/GrDrawOpAtlas.cpp167
-rw-r--r--src/gpu/GrDrawOpAtlas.h34
-rw-r--r--src/gpu/GrOnFlushResourceProvider.h5
-rw-r--r--src/gpu/GrSurfaceProxy.cpp7
-rw-r--r--src/gpu/ops/GrAtlasTextOp.cpp6
-rw-r--r--src/gpu/ops/GrSmallPathRenderer.cpp30
-rw-r--r--src/gpu/text/GrAtlasGlyphCache.cpp27
-rw-r--r--src/gpu/text/GrAtlasGlyphCache.h24
-rw-r--r--src/gpu/text/GrAtlasTextBlob.h7
-rw-r--r--src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp10
-rw-r--r--tests/DrawOpAtlasTest.cpp132
16 files changed, 345 insertions, 139 deletions
diff --git a/gn/tests.gni b/gn/tests.gni
index 58b1ff05c6..02293a0b48 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -60,6 +60,7 @@ tests_sources = [
"$_tests/DiscardableMemoryTest.cpp",
"$_tests/DrawBitmapRectTest.cpp",
"$_tests/DrawFilterTest.cpp",
+ "$_tests/DrawOpAtlasTest.cpp",
"$_tests/DrawPathTest.cpp",
"$_tests/DrawTextTest.cpp",
"$_tests/DynamicHashTest.cpp",
diff --git a/include/private/GrSurfaceProxy.h b/include/private/GrSurfaceProxy.h
index cb8c46e78f..6beb11e46f 100644
--- a/include/private/GrSurfaceProxy.h
+++ b/include/private/GrSurfaceProxy.h
@@ -57,6 +57,17 @@ public:
}
#endif
+ void release() {
+ SkASSERT(1 == fRefCnt);
+ SkASSERT(0 == fPendingReads);
+ SkASSERT(0 == fPendingWrites);
+
+ SkASSERT(fTarget->internalHasUniqueRef());
+ SkASSERT(!fTarget->internalHasPendingIO());
+ fTarget->unref();
+ fTarget = nullptr;
+ }
+
void validate() const {
#ifdef SK_DEBUG
SkASSERT(fRefCnt >= 0);
@@ -129,7 +140,7 @@ protected:
}
virtual ~GrIORefProxy() {
// We don't unref 'fTarget' here since the 'unref' method will already
- // have forwarded on the unref call that got use here.
+ // have forwarded on the unref call that got us here.
}
// This GrIORefProxy was deferred before but has just been instantiated. To
@@ -279,6 +290,8 @@ public:
virtual bool instantiate(GrResourceProvider* resourceProvider) = 0;
+ void deInstantiate();
+
/**
* Helper that gets the width and height of the surface as a bounding rectangle.
*/
diff --git a/src/atlastext/SkAtlasTextTarget.cpp b/src/atlastext/SkAtlasTextTarget.cpp
index d80c7a45d7..f553ffad96 100644
--- a/src/atlastext/SkAtlasTextTarget.cpp
+++ b/src/atlastext/SkAtlasTextTarget.cpp
@@ -183,12 +183,18 @@ void GrAtlasTextOp::executeForTextTarget(SkAtlasTextTarget* target) {
FlushInfo flushInfo;
SkAutoGlyphCache glyphCache;
auto& context = target->context()->internal();
- auto* atlasGlyphCache = context.grContext()->contextPriv().getAtlasGlyphCache();
+ auto atlasGlyphCache = context.grContext()->contextPriv().getAtlasGlyphCache();
+ auto resourceProvider = context.grContext()->contextPriv().resourceProvider();
+ auto drawingManager = context.grContext()->contextPriv().drawingManager();
+
+ GrOnFlushResourceProvider onFlushResourceProvider(drawingManager);
+ atlasGlyphCache->preFlush(&onFlushResourceProvider, nullptr, 0, nullptr);
+
for (int i = 0; i < fGeoCount; ++i) {
GrAtlasTextBlob::VertexRegenerator regenerator(
- fGeoData[i].fBlob, fGeoData[i].fRun, fGeoData[i].fSubRun, fGeoData[i].fViewMatrix,
- fGeoData[i].fX, fGeoData[i].fY, fGeoData[i].fColor, &context, atlasGlyphCache,
- &glyphCache);
+ resourceProvider, fGeoData[i].fBlob, fGeoData[i].fRun, fGeoData[i].fSubRun,
+ fGeoData[i].fViewMatrix, fGeoData[i].fX, fGeoData[i].fY, fGeoData[i].fColor,
+ &context, atlasGlyphCache, &glyphCache);
GrAtlasTextBlob::VertexRegenerator::Result result;
do {
result = regenerator.regenerate();
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 3fb7a9c837..0af319715b 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -295,7 +295,7 @@ bool GrContext::init(const GrContextOptions& options) {
} else {
allowMultitexturing = GrDrawOpAtlas::AllowMultitexturing::kYes;
}
- fAtlasGlyphCache = new GrAtlasGlyphCache(this, options.fGlyphCacheTextureMaximumBytes,
+ fAtlasGlyphCache = new GrAtlasGlyphCache(fProxyProvider, options.fGlyphCacheTextureMaximumBytes,
allowMultitexturing);
this->contextPriv().addOnFlushCallbackObject(fAtlasGlyphCache);
diff --git a/src/gpu/GrDeferredUpload.h b/src/gpu/GrDeferredUpload.h
index fad5e930a1..d78363136d 100644
--- a/src/gpu/GrDeferredUpload.h
+++ b/src/gpu/GrDeferredUpload.h
@@ -96,9 +96,10 @@ public:
GrDeferredUploadToken nextDrawToken() const { return fLastIssuedToken.next(); }
private:
- // Only these two classes get to increment the token counters
+ // Only these three classes get to increment the token counters
friend class SkInternalAtlasTextContext;
friend class GrOpFlushState;
+ friend class TestingUploadTarget;
/** Issues the next token for a draw. */
GrDeferredUploadToken issueDrawToken() { return ++fLastIssuedToken; }
diff --git a/src/gpu/GrDrawOpAtlas.cpp b/src/gpu/GrDrawOpAtlas.cpp
index 622aa229bd..7539a7a05e 100644
--- a/src/gpu/GrDrawOpAtlas.cpp
+++ b/src/gpu/GrDrawOpAtlas.cpp
@@ -26,20 +26,20 @@
// must explicitly manage the lifetime of their backing proxies via the onFlushCallback system
// (which calls this method).
void GrDrawOpAtlas::instantiate(GrOnFlushResourceProvider* onFlushResourceProvider) {
- for (int i = 0; i < GrDrawOpAtlas::kMaxMultitexturePages; ++i) {
- if (fProxies[i] && !fProxies[i]->priv().isInstantiated()) {
- // If instantiation fails we expect the ops that rely on the atlas to be dropped
- onFlushResourceProvider->instatiateProxy(fProxies[i].get());
- }
+ for (uint32_t i = 0; i < fNumActivePages; ++i) {
+ // All the atlas pages are now instantiated at flush time in the activeNewPage method.
+ SkASSERT(fProxies[i] && fProxies[i]->priv().isInstantiated());
}
}
-std::unique_ptr<GrDrawOpAtlas> GrDrawOpAtlas::Make(GrContext* ctx, GrPixelConfig config, int width,
+std::unique_ptr<GrDrawOpAtlas> GrDrawOpAtlas::Make(GrProxyProvider* proxyProvider,
+ GrPixelConfig config, int width,
int height, int numPlotsX, int numPlotsY,
AllowMultitexturing allowMultitexturing,
GrDrawOpAtlas::EvictionFunc func, void* data) {
- std::unique_ptr<GrDrawOpAtlas> atlas(new GrDrawOpAtlas(ctx, config, width, height, numPlotsX,
- numPlotsY, allowMultitexturing));
+ std::unique_ptr<GrDrawOpAtlas> atlas(new GrDrawOpAtlas(proxyProvider, config, width, height,
+ numPlotsX, numPlotsY,
+ allowMultitexturing));
if (!atlas->getProxies()[0]) {
return nullptr;
}
@@ -53,7 +53,6 @@ static bool gDumpAtlasData = false;
#endif
////////////////////////////////////////////////////////////////////////////////
-
GrDrawOpAtlas::Plot::Plot(int pageIndex, int plotIndex, uint64_t genID, int offX, int offY,
int width, int height, GrPixelConfig config)
: fLastUpload(GrDeferredUploadToken::AlreadyFlushedToken())
@@ -178,16 +177,16 @@ void GrDrawOpAtlas::Plot::resetRects() {
///////////////////////////////////////////////////////////////////////////////
-GrDrawOpAtlas::GrDrawOpAtlas(GrContext* context, GrPixelConfig config, int width, int height,
+GrDrawOpAtlas::GrDrawOpAtlas(GrProxyProvider* proxyProvider,
+ GrPixelConfig config, int width, int height,
int numPlotsX, int numPlotsY, AllowMultitexturing allowMultitexturing)
- : fContext(context)
- , fPixelConfig(config)
+ : fPixelConfig(config)
, fTextureWidth(width)
, fTextureHeight(height)
, fAtlasGeneration(kInvalidAtlasGeneration + 1)
, fPrevFlushToken(GrDeferredUploadToken::AlreadyFlushedToken())
, fAllowMultitexturing(allowMultitexturing)
- , fNumPages(0) {
+ , fNumActivePages(0) {
fPlotWidth = fTextureWidth / numPlotsX;
fPlotHeight = fTextureHeight / numPlotsY;
SkASSERT(numPlotsX * numPlotsY <= BulkUseTokenUpdater::kMaxPlots);
@@ -196,7 +195,7 @@ GrDrawOpAtlas::GrDrawOpAtlas(GrContext* context, GrPixelConfig config, int width
fNumPlots = numPlotsX * numPlotsY;
- this->createNewPage();
+ this->createPages(proxyProvider);
}
inline void GrDrawOpAtlas::processEviction(AtlasID id) {
@@ -217,13 +216,8 @@ inline bool GrDrawOpAtlas::updatePlot(GrDeferredUploadTarget* target, AtlasID* i
// With c+14 we could move sk_sp into lamba to only ref once.
sk_sp<Plot> plotsp(SkRef(plot));
- // MDB TODO: this is currently fine since the atlas' proxy is always pre-instantiated.
- // Once it is deferred more care must be taken upon instantiation failure.
- if (!fProxies[pageIdx]->instantiate(fContext->contextPriv().resourceProvider())) {
- return false;
- }
-
GrTextureProxy* proxy = fProxies[pageIdx].get();
+ SkASSERT(proxy->priv().isInstantiated()); // This is occurring at flush time
GrDeferredUploadToken lastUploadToken = target->addASAPUpload(
[plotsp, proxy](GrDeferredTextureUploadWritePixelsFn& writePixels) {
@@ -243,8 +237,9 @@ inline bool GrDrawOpAtlas::updatePlot(GrDeferredUploadTarget* target, AtlasID* i
// are rare; i.e., we are not continually refreshing the frame.
static constexpr auto kRecentlyUsedCount = 256;
-bool GrDrawOpAtlas::addToAtlas(AtlasID* id, GrDeferredUploadTarget* target, int width, int height,
- const void* image, SkIPoint16* loc) {
+bool GrDrawOpAtlas::addToAtlas(GrResourceProvider* resourceProvider,
+ AtlasID* id, GrDeferredUploadTarget* target,
+ int width, int height, const void* image, SkIPoint16* loc) {
if (width > fPlotWidth || height > fPlotHeight) {
return false;
}
@@ -252,7 +247,7 @@ bool GrDrawOpAtlas::addToAtlas(AtlasID* id, GrDeferredUploadTarget* target, int
// Look through each page to see if we can upload without having to flush
// We prioritize this upload to the first pages, not the most recently used, to make it easier
// to remove unused pages in reverse page order.
- for (unsigned int pageIdx = 0; pageIdx < fNumPages; ++pageIdx) {
+ for (unsigned int pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
SkASSERT(fProxies[pageIdx]);
// look through all allocated plots for one we can share, in Most Recently Refed order
PlotList::Iter plotIter;
@@ -272,10 +267,10 @@ bool GrDrawOpAtlas::addToAtlas(AtlasID* id, GrDeferredUploadTarget* target, int
// We wait until we've grown to the full number of pages to begin evicting already flushed
// plots so that we can maximize the opportunity for reuse.
// As before we prioritize this upload to the first pages, not the most recently used.
- for (unsigned int pageIdx = 0; pageIdx < fNumPages; ++pageIdx) {
+ for (unsigned int pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
Plot* plot = fPages[pageIdx].fPlotList.tail();
SkASSERT(plot);
- if ((fNumPages == this->maxPages() &&
+ if ((fNumActivePages == this->maxPages() &&
plot->lastUseToken() < target->tokenTracker()->nextTokenToFlush()) ||
plot->flushesSinceLastUsed() >= kRecentlyUsedCount) {
this->processEvictionAndResetRects(plot);
@@ -290,9 +285,10 @@ bool GrDrawOpAtlas::addToAtlas(AtlasID* id, GrDeferredUploadTarget* target, int
}
// If the simple cases fail, try to create a new page and add to it
- if (this->createNewPage()) {
- unsigned int pageIdx = fNumPages-1;
- SkASSERT(fProxies[pageIdx]);
+ if (this->activateNewPage(resourceProvider)) {
+ unsigned int pageIdx = fNumActivePages-1;
+ SkASSERT(fProxies[pageIdx] && fProxies[pageIdx]->priv().isInstantiated());
+
Plot* plot = fPages[pageIdx].fPlotList.head();
SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp());
if (plot->addSubImage(width, height, image, loc)) {
@@ -307,7 +303,7 @@ bool GrDrawOpAtlas::addToAtlas(AtlasID* id, GrDeferredUploadTarget* target, int
// Try to find a plot that we can perform an inline upload to.
// We prioritize this upload in reverse order of pages to counterbalance the order above.
Plot* plot = nullptr;
- for (int pageIdx = (int)(fNumPages-1); pageIdx >= 0; --pageIdx) {
+ for (int pageIdx = (int)(fNumActivePages-1); pageIdx >= 0; --pageIdx) {
Plot* currentPlot = fPages[pageIdx].fPlotList.tail();
if (currentPlot->lastUseToken() != target->tokenTracker()->nextDrawToken()) {
plot = currentPlot;
@@ -339,11 +335,8 @@ bool GrDrawOpAtlas::addToAtlas(AtlasID* id, GrDeferredUploadTarget* target, int
// one it displaced most likely was uploaded ASAP.
// With c+14 we could move sk_sp into lambda to only ref once.
sk_sp<Plot> plotsp(SkRef(newPlot.get()));
- // MDB TODO: this is currently fine since the atlas' proxy is always pre-instantiated.
- // Once it is deferred more care must be taken upon instantiation failure.
- if (!fProxies[pageIdx]->instantiate(fContext->contextPriv().resourceProvider())) {
- return false;
- }
+
+ SkASSERT(fProxies[pageIdx]->priv().isInstantiated());
GrTextureProxy* proxy = fProxies[pageIdx].get();
GrDeferredUploadToken lastUploadToken = target->addInlineUpload(
@@ -358,7 +351,7 @@ bool GrDrawOpAtlas::addToAtlas(AtlasID* id, GrDeferredUploadTarget* target, int
}
void GrDrawOpAtlas::compact(GrDeferredUploadToken startTokenForNextFlush) {
- if (fNumPages <= 1) {
+ if (fNumActivePages <= 1) {
fPrevFlushToken = startTokenForNextFlush;
return;
}
@@ -366,7 +359,7 @@ void GrDrawOpAtlas::compact(GrDeferredUploadToken startTokenForNextFlush) {
// For all plots, reset number of flushes since used if used this frame.
PlotList::Iter plotIter;
bool atlasUsedThisFlush = false;
- for (uint32_t pageIndex = 0; pageIndex < fNumPages; ++pageIndex) {
+ for (uint32_t pageIndex = 0; pageIndex < fNumActivePages; ++pageIndex) {
plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
while (Plot* plot = plotIter.get()) {
// Reset number of flushes since used
@@ -385,7 +378,7 @@ void GrDrawOpAtlas::compact(GrDeferredUploadToken startTokenForNextFlush) {
// TODO: consider if we should also do this if it's been a long time since the last atlas use
if (atlasUsedThisFlush) {
SkTArray<Plot*> availablePlots;
- uint32_t lastPageIndex = fNumPages - 1;
+ uint32_t lastPageIndex = fNumActivePages - 1;
// For all plots but the last one, update number of flushes since used, and check to see
// if there are any in the first pages that the last page can safely upload to.
@@ -494,19 +487,15 @@ void GrDrawOpAtlas::compact(GrDeferredUploadToken startTokenForNextFlush) {
SkDebugf("delete %d\n", fNumPages-1);
}
#endif
- this->deleteLastPage();
+ this->deactivateLastPage();
}
}
fPrevFlushToken = startTokenForNextFlush;
}
-bool GrDrawOpAtlas::createNewPage() {
- if (fNumPages == this->maxPages()) {
- return false;
- }
-
- GrProxyProvider* proxyProvider = fContext->contextPriv().proxyProvider();
+bool GrDrawOpAtlas::createPages(GrProxyProvider* proxyProvider) {
+ SkASSERT(SkIsPow2(fTextureWidth) && SkIsPow2(fTextureHeight));
GrSurfaceDesc desc;
desc.fFlags = kNone_GrSurfaceFlags;
@@ -515,47 +504,83 @@ bool GrDrawOpAtlas::createNewPage() {
desc.fHeight = fTextureHeight;
desc.fConfig = fPixelConfig;
- SkASSERT(SkIsPow2(fTextureWidth) && SkIsPow2(fTextureHeight));
- fProxies[fNumPages] = proxyProvider->createProxy(desc, SkBackingFit::kExact, SkBudgeted::kYes,
- GrResourceProvider::kNoPendingIO_Flag);
- if (!fProxies[fNumPages]) {
- return false;
- }
-
int numPlotsX = fTextureWidth/fPlotWidth;
int numPlotsY = fTextureHeight/fPlotHeight;
- // set up allocated plots
- fPages[fNumPages].fPlotArray.reset(new sk_sp<Plot>[ numPlotsX * numPlotsY ]);
+ for (uint32_t i = 0; i < this->maxPages(); ++i) {
+ fProxies[i] = proxyProvider->createProxy(desc, SkBackingFit::kExact, SkBudgeted::kYes,
+ GrResourceProvider::kNoPendingIO_Flag);
+ if (!fProxies[i]) {
+ return false;
+ }
- sk_sp<Plot>* currPlot = fPages[fNumPages].fPlotArray.get();
- for (int y = numPlotsY - 1, r = 0; y >= 0; --y, ++r) {
- for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) {
- uint32_t plotIndex = r * numPlotsX + c;
- currPlot->reset(new Plot(fNumPages, plotIndex, 1, x, y, fPlotWidth, fPlotHeight,
- fPixelConfig));
+ // set up allocated plots
+ fPages[i].fPlotArray.reset(new sk_sp<Plot>[ numPlotsX * numPlotsY ]);
- // build LRU list
- fPages[fNumPages].fPlotList.addToHead(currPlot->get());
- ++currPlot;
+ sk_sp<Plot>* currPlot = fPages[i].fPlotArray.get();
+ for (int y = numPlotsY - 1, r = 0; y >= 0; --y, ++r) {
+ for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) {
+ uint32_t plotIndex = r * numPlotsX + c;
+ currPlot->reset(new Plot(i, plotIndex, 1, x, y, fPlotWidth, fPlotHeight,
+ fPixelConfig));
+
+ // build LRU list
+ fPages[i].fPlotList.addToHead(currPlot->get());
+ ++currPlot;
+ }
}
+
+ }
+
+ return true;
+}
+
+
+bool GrDrawOpAtlas::activateNewPage(GrResourceProvider* resourceProvider) {
+ if (fNumActivePages >= this->maxPages()) {
+ return false;
+ }
+
+ if (!fProxies[fNumActivePages]->instantiate(resourceProvider)) {
+ return false;
}
#ifdef DUMP_ATLAS_DATA
if (gDumpAtlasData) {
- SkDebugf("created %d\n", fNumPages);
+ SkDebugf("activated page#: %d\n", fNumActivePages);
}
#endif
- fNumPages++;
+
+ ++fNumActivePages;
return true;
}
-inline void GrDrawOpAtlas::deleteLastPage() {
- uint32_t lastPageIndex = fNumPages - 1;
- // clean out the plots
+
+inline void GrDrawOpAtlas::deactivateLastPage() {
+ SkASSERT(fNumActivePages);
+
+ uint32_t lastPageIndex = fNumActivePages - 1;
+
+ int numPlotsX = fTextureWidth/fPlotWidth;
+ int numPlotsY = fTextureHeight/fPlotHeight;
+
fPages[lastPageIndex].fPlotList.reset();
- fPages[lastPageIndex].fPlotArray.reset(nullptr);
- // remove ref to texture proxy
- fProxies[lastPageIndex].reset(nullptr);
- --fNumPages;
+ for (int y = numPlotsY - 1, r = 0; y >= 0; --y, ++r) {
+ for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) {
+ uint32_t plotIndex = r * numPlotsX + c;
+
+ Plot* currPlot = fPages[lastPageIndex].fPlotArray[plotIndex].get();
+ currPlot->resetRects();
+ currPlot->resetFlushesSinceLastUsed();
+
+ // rebuild the LRU list
+ SkDEBUGCODE(currPlot->fPrev = currPlot->fNext = nullptr);
+ SkDEBUGCODE(currPlot->fList = nullptr);
+ fPages[lastPageIndex].fPlotList.addToHead(currPlot);
+ }
+ }
+
+ // remove ref to the backing texture
+ fProxies[lastPageIndex]->deInstantiate();
+ --fNumActivePages;
}
diff --git a/src/gpu/GrDrawOpAtlas.h b/src/gpu/GrDrawOpAtlas.h
index a2c985928d..b849d9e067 100644
--- a/src/gpu/GrDrawOpAtlas.h
+++ b/src/gpu/GrDrawOpAtlas.h
@@ -91,7 +91,8 @@ public:
* eviction occurs
* @return An initialized GrDrawOpAtlas, or nullptr if creation fails
*/
- static std::unique_ptr<GrDrawOpAtlas> Make(GrContext*, GrPixelConfig, int width, int height,
+ static std::unique_ptr<GrDrawOpAtlas> Make(GrProxyProvider*, GrPixelConfig,
+ int width, int height,
int numPlotsX, int numPlotsY,
AllowMultitexturing allowMultitexturing,
GrDrawOpAtlas::EvictionFunc func, void* data);
@@ -108,19 +109,21 @@ public:
* 'setUseToken' with the currentToken from the GrDrawOp::Target, otherwise the next call to
* addToAtlas might cause the previous data to be overwritten before it has been read.
*/
- bool addToAtlas(AtlasID*, GrDeferredUploadTarget*, int width, int height, const void* image,
- SkIPoint16* loc);
+ bool addToAtlas(GrResourceProvider*, AtlasID*, GrDeferredUploadTarget*, int width, int height,
+ const void* image, SkIPoint16* loc);
- GrContext* context() const { return fContext; }
const sk_sp<GrTextureProxy>* getProxies() const { return fProxies; }
uint64_t atlasGeneration() const { return fAtlasGeneration; }
inline bool hasID(AtlasID id) {
+ if (kInvalidAtlasID == id) {
+ return false;
+ }
uint32_t plot = GetPlotIndexFromID(id);
SkASSERT(plot < fNumPlots);
uint32_t page = GetPageIndexFromID(id);
- SkASSERT(page < fNumPages);
+ SkASSERT(page < fNumActivePages);
return fPages[page].fPlotArray[plot]->genID() == GetGenerationFromID(id);
}
@@ -130,7 +133,7 @@ public:
uint32_t plotIdx = GetPlotIndexFromID(id);
SkASSERT(plotIdx < fNumPlots);
uint32_t pageIdx = GetPageIndexFromID(id);
- SkASSERT(pageIdx < fNumPages);
+ SkASSERT(pageIdx < fNumActivePages);
Plot* plot = fPages[pageIdx].fPlotArray[plotIdx].get();
this->makeMRU(plot, pageIdx);
plot->setLastUseToken(token);
@@ -142,7 +145,7 @@ public:
data->fData = userData;
}
- uint32_t pageCount() { return fNumPages; }
+ uint32_t numActivePages() { return fNumActivePages; }
/**
* A class which can be handed back to GrDrawOpAtlas for updating last use tokens in bulk. The
@@ -204,7 +207,7 @@ public:
const BulkUseTokenUpdater::PlotData& pd = updater.fPlotsToUpdate[i];
// it's possible we've added a plot to the updater and subsequently the plot's page
// was deleted -- so we check to prevent a crash
- if (pd.fPageIndex < fNumPages) {
+ if (pd.fPageIndex < fNumActivePages) {
Plot* plot = fPages[pd.fPageIndex].fPlotArray[pd.fPlotIndex].get();
this->makeMRU(plot, pd.fPageIndex);
plot->setLastUseToken(token);
@@ -225,12 +228,14 @@ public:
void instantiate(GrOnFlushResourceProvider*);
-private:
uint32_t maxPages() const {
return AllowMultitexturing::kYes == fAllowMultitexturing ? kMaxMultitexturePages : 1;
}
- GrDrawOpAtlas(GrContext*, GrPixelConfig config, int width, int height, int numPlotsX,
+ int numAllocated_TestingOnly() const;
+
+private:
+ GrDrawOpAtlas(GrProxyProvider*, GrPixelConfig, int width, int height, int numPlotsX,
int numPlotsY, AllowMultitexturing allowMultitexturing);
/**
@@ -354,8 +359,9 @@ private:
// the front and remove from the back there is no need for MRU.
}
- bool createNewPage();
- void deleteLastPage();
+ bool createPages(GrProxyProvider*);
+ bool activateNewPage(GrResourceProvider*);
+ void deactivateLastPage();
void processEviction(AtlasID);
inline void processEvictionAndResetRects(Plot* plot) {
@@ -363,7 +369,6 @@ private:
plot->resetRects();
}
- GrContext* fContext;
GrPixelConfig fPixelConfig;
int fTextureWidth;
int fTextureHeight;
@@ -392,7 +397,8 @@ private:
sk_sp<GrTextureProxy> fProxies[kMaxMultitexturePages];
Page fPages[kMaxMultitexturePages];
AllowMultitexturing fAllowMultitexturing;
- uint32_t fNumPages;
+
+ uint32_t fNumActivePages;
};
#endif
diff --git a/src/gpu/GrOnFlushResourceProvider.h b/src/gpu/GrOnFlushResourceProvider.h
index 456b1ce82f..75b9b0c2b1 100644
--- a/src/gpu/GrOnFlushResourceProvider.h
+++ b/src/gpu/GrOnFlushResourceProvider.h
@@ -66,6 +66,8 @@ public:
*/
class GrOnFlushResourceProvider {
public:
+ explicit GrOnFlushResourceProvider(GrDrawingManager* drawingMgr) : fDrawingMgr(drawingMgr) {}
+
sk_sp<GrRenderTargetContext> makeRenderTargetContext(const GrSurfaceDesc&,
sk_sp<SkColorSpace>,
const SkSurfaceProps*);
@@ -86,13 +88,10 @@ public:
const GrCaps* caps() const;
private:
- explicit GrOnFlushResourceProvider(GrDrawingManager* drawingMgr) : fDrawingMgr(drawingMgr) {}
GrOnFlushResourceProvider(const GrOnFlushResourceProvider&) = delete;
GrOnFlushResourceProvider& operator=(const GrOnFlushResourceProvider&) = delete;
GrDrawingManager* fDrawingMgr;
-
- friend class GrDrawingManager; // to construct this type.
};
#endif
diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp
index 800b44723c..7acc03b05a 100644
--- a/src/gpu/GrSurfaceProxy.cpp
+++ b/src/gpu/GrSurfaceProxy.cpp
@@ -208,6 +208,13 @@ bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int s
return true;
}
+void GrSurfaceProxy::deInstantiate() {
+ SkASSERT(this->priv().isInstantiated());
+
+ this->release();
+}
+
+
void GrSurfaceProxy::computeScratchKey(GrScratchKey* key) const {
SkASSERT(LazyState::kFully != this->lazyInstantiationState());
const GrRenderTargetProxy* rtp = this->asRenderTargetProxy();
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index 46f38099ec..891564e586 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -222,6 +222,8 @@ static void clip_quads(const SkIRect& clipRect, char* currVertex, const char* bl
}
void GrAtlasTextOp::onPrepareDraws(Target* target) {
+ auto resourceProvider = target->resourceProvider();
+
// if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
// TODO actually only invert if we don't have RGBA
SkMatrix localMatrix;
@@ -276,8 +278,8 @@ void GrAtlasTextOp::onPrepareDraws(Target* target) {
const Geometry& args = fGeoData[i];
Blob* blob = args.fBlob;
GrAtlasTextBlob::VertexRegenerator regenerator(
- blob, args.fRun, args.fSubRun, args.fViewMatrix, args.fX, args.fY, args.fColor,
- target->deferredUploadTarget(), fFontCache, &glyphCache);
+ resourceProvider, blob, args.fRun, args.fSubRun, args.fViewMatrix, args.fX, args.fY,
+ args.fColor, target->deferredUploadTarget(), fFontCache, &glyphCache);
GrAtlasTextBlob::VertexRegenerator::Result result;
do {
result = regenerator.regenerate();
diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp
index 3e7da52230..563fbd3de3 100644
--- a/src/gpu/ops/GrSmallPathRenderer.cpp
+++ b/src/gpu/ops/GrSmallPathRenderer.cpp
@@ -181,7 +181,7 @@ public:
fHelper.visitProxies(func);
const sk_sp<GrTextureProxy>* proxies = fAtlas->getProxies();
- for (uint32_t i = 0; i < fAtlas->pageCount(); ++i) {
+ for (uint32_t i = 0; i < fAtlas->numActivePages(); ++i) {
SkASSERT(proxies[i]);
func(proxies[i].get());
}
@@ -223,7 +223,7 @@ private:
flushInfo.fPipeline = fHelper.makePipeline(target);
// Setup GrGeometryProcessor
GrDrawOpAtlas* atlas = fAtlas;
- uint32_t atlasPageCount = atlas->pageCount();
+ uint32_t atlasPageCount = atlas->numActivePages();
if (!atlasPageCount) {
return;
}
@@ -400,6 +400,8 @@ private:
bool addDFPathToAtlas(GrMeshDrawOp::Target* target, FlushInfo* flushInfo,
GrDrawOpAtlas* atlas, ShapeData* shapeData, const GrShape& shape,
uint32_t dimension, SkScalar scale) const {
+ auto resourceProvider = target->resourceProvider();
+
const SkRect& bounds = shape.bounds();
// generate bounding rect for bitmap draw
@@ -488,10 +490,11 @@ private:
SkIPoint16 atlasLocation;
GrDrawOpAtlas::AtlasID id;
auto uploadTarget = target->deferredUploadTarget();
- if (!atlas->addToAtlas(&id, uploadTarget, width, height, dfStorage.get(), &atlasLocation)) {
+ if (!atlas->addToAtlas(resourceProvider, &id, uploadTarget, width, height,
+ dfStorage.get(), &atlasLocation)) {
this->flush(target, flushInfo);
- if (!atlas->addToAtlas(&id, uploadTarget, width, height, dfStorage.get(),
- &atlasLocation)) {
+ if (!atlas->addToAtlas(resourceProvider, &id, uploadTarget, width, height,
+ dfStorage.get(), &atlasLocation)) {
return false;
}
}
@@ -530,6 +533,8 @@ private:
bool addBMPathToAtlas(GrMeshDrawOp::Target* target, FlushInfo* flushInfo,
GrDrawOpAtlas* atlas, ShapeData* shapeData, const GrShape& shape,
const SkMatrix& ctm) const {
+ auto resourceProvider = target->resourceProvider();
+
const SkRect& bounds = shape.bounds();
if (bounds.isEmpty()) {
return false;
@@ -590,11 +595,11 @@ private:
SkIPoint16 atlasLocation;
GrDrawOpAtlas::AtlasID id;
auto uploadTarget = target->deferredUploadTarget();
- if (!atlas->addToAtlas(&id, uploadTarget, dst.width(), dst.height(), dst.addr(),
- &atlasLocation)) {
+ if (!atlas->addToAtlas(resourceProvider, &id, uploadTarget, dst.width(), dst.height(),
+ dst.addr(), &atlasLocation)) {
this->flush(target, flushInfo);
- if (!atlas->addToAtlas(&id, uploadTarget, dst.width(), dst.height(), dst.addr(),
- &atlasLocation)) {
+ if (!atlas->addToAtlas(resourceProvider, &id, uploadTarget, dst.width(), dst.height(),
+ dst.addr(), &atlasLocation)) {
return false;
}
}
@@ -696,7 +701,7 @@ private:
void flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
GrGeometryProcessor* gp = flushInfo->fGeometryProcessor.get();
- if (gp->numTextureSamplers() != (int)fAtlas->pageCount()) {
+ if (gp->numTextureSamplers() != (int)fAtlas->numActivePages()) {
// During preparation the number of atlas pages has increased.
// Update the proxies used in the GP to match.
if (fUsesDistanceField) {
@@ -788,7 +793,7 @@ bool GrSmallPathRenderer::onDrawPath(const DrawPathArgs& args) {
SkASSERT(!args.fShape->isEmpty());
SkASSERT(args.fShape->hasUnstyledKey());
if (!fAtlas) {
- fAtlas = GrDrawOpAtlas::Make(args.fContext,
+ fAtlas = GrDrawOpAtlas::Make(args.fContext->contextPriv().proxyProvider(),
kAlpha_8_GrPixelConfig,
ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
NUM_PLOTS_X, NUM_PLOTS_Y,
@@ -858,7 +863,8 @@ GR_DRAW_OP_TEST_DEFINE(SmallPathOp) {
if (context->uniqueID() != gTestStruct.fContextID) {
gTestStruct.fContextID = context->uniqueID();
gTestStruct.reset();
- gTestStruct.fAtlas = GrDrawOpAtlas::Make(context, kAlpha_8_GrPixelConfig,
+ gTestStruct.fAtlas = GrDrawOpAtlas::Make(context->contextPriv().proxyProvider(),
+ kAlpha_8_GrPixelConfig,
ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
NUM_PLOTS_X, NUM_PLOTS_Y,
GrDrawOpAtlas::AllowMultitexturing::kYes,
diff --git a/src/gpu/text/GrAtlasGlyphCache.cpp b/src/gpu/text/GrAtlasGlyphCache.cpp
index fb52250781..f7b1e1269a 100644
--- a/src/gpu/text/GrAtlasGlyphCache.cpp
+++ b/src/gpu/text/GrAtlasGlyphCache.cpp
@@ -9,6 +9,7 @@
#include "GrContext.h"
#include "GrDistanceFieldGenFromVector.h"
#include "GrGpu.h"
+#include "GrProxyProvider.h"
#include "GrRectanizer.h"
#include "SkAutoMalloc.h"
@@ -19,14 +20,14 @@
bool GrAtlasGlyphCache::initAtlas(GrMaskFormat format) {
int index = MaskFormatToAtlasIndex(format);
if (!fAtlases[index]) {
- GrPixelConfig config = MaskFormatToPixelConfig(format, *fContext->caps());
+ GrPixelConfig config = MaskFormatToPixelConfig(format, *fProxyProvider->caps());
int width = fAtlasConfigs[index].fWidth;
int height = fAtlasConfigs[index].fHeight;
int numPlotsX = fAtlasConfigs[index].numPlotsX();
int numPlotsY = fAtlasConfigs[index].numPlotsY();
- fAtlases[index] = GrDrawOpAtlas::Make(fContext, config, width, height, numPlotsX, numPlotsY,
- fAllowMultitexturing,
+ fAtlases[index] = GrDrawOpAtlas::Make(fProxyProvider, config, width, height,
+ numPlotsX, numPlotsY, fAllowMultitexturing,
&GrAtlasGlyphCache::HandleEviction, (void*)this);
if (!fAtlases[index]) {
return false;
@@ -35,11 +36,13 @@ bool GrAtlasGlyphCache::initAtlas(GrMaskFormat format) {
return true;
}
-GrAtlasGlyphCache::GrAtlasGlyphCache(GrContext* context, float maxTextureBytes,
+GrAtlasGlyphCache::GrAtlasGlyphCache(GrProxyProvider* proxyProvider, float maxTextureBytes,
GrDrawOpAtlas::AllowMultitexturing allowMultitexturing)
- : fContext(context), fAllowMultitexturing(allowMultitexturing), fPreserveStrike(nullptr) {
+ : fProxyProvider(proxyProvider)
+ , fAllowMultitexturing(allowMultitexturing)
+ , fPreserveStrike(nullptr) {
// Calculate RGBA size. Must be between 512 x 256 and MaxTextureSize x MaxTextureSize / 2
- int log2MaxTextureSize = SkPrevLog2(context->caps()->maxTextureSize());
+ int log2MaxTextureSize = SkPrevLog2(fProxyProvider->caps()->maxTextureSize());
int log2MaxDim = 9;
for (; log2MaxDim <= log2MaxTextureSize; ++log2MaxDim) {
int maxDim = 1 << log2MaxDim;
@@ -174,12 +177,12 @@ static bool save_pixels(GrContext* context, GrSurfaceProxy* sProxy, const char*
return true;
}
-void GrAtlasGlyphCache::dump() const {
+void GrAtlasGlyphCache::dump(GrContext* context) const {
static int gDumpCount = 0;
for (int i = 0; i < kMaskFormatCount; ++i) {
if (fAtlases[i]) {
const sk_sp<GrTextureProxy>* proxies = fAtlases[i]->getProxies();
- for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->pageCount(); ++pageIdx) {
+ for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->numActivePages(); ++pageIdx) {
SkASSERT(proxies[pageIdx]);
SkString filename;
#ifdef SK_BUILD_FOR_ANDROID
@@ -188,7 +191,7 @@ void GrAtlasGlyphCache::dump() const {
filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
#endif
- save_pixels(fContext, proxies[pageIdx].get(), filename.c_str());
+ save_pixels(context, proxies[pageIdx].get(), filename.c_str());
}
}
}
@@ -445,7 +448,8 @@ void GrAtlasTextStrike::removeID(GrDrawOpAtlas::AtlasID id) {
}
}
-bool GrAtlasTextStrike::addGlyphToAtlas(GrDeferredUploadTarget* target,
+bool GrAtlasTextStrike::addGlyphToAtlas(GrResourceProvider* resourceProvider,
+ GrDeferredUploadTarget* target,
GrAtlasGlyphCache* atlasGlyphCache,
GrGlyph* glyph,
SkGlyphCache* cache,
@@ -473,7 +477,8 @@ bool GrAtlasTextStrike::addGlyphToAtlas(GrDeferredUploadTarget* target,
}
}
- bool success = atlasGlyphCache->addToAtlas(this, &glyph->fID, target, expectedMaskFormat,
+ bool success = atlasGlyphCache->addToAtlas(resourceProvider, this, &glyph->fID, target,
+ expectedMaskFormat,
glyph->width(), glyph->height(),
storage.get(), &glyph->fAtlasLocation);
if (success) {
diff --git a/src/gpu/text/GrAtlasGlyphCache.h b/src/gpu/text/GrAtlasGlyphCache.h
index 6175342c26..84b7d1b249 100644
--- a/src/gpu/text/GrAtlasGlyphCache.h
+++ b/src/gpu/text/GrAtlasGlyphCache.h
@@ -64,8 +64,8 @@ public:
// happen.
// TODO we can handle some of these cases if we really want to, but the long term solution is to
// get the actual glyph image itself when we get the glyph metrics.
- bool addGlyphToAtlas(GrDeferredUploadTarget*, GrAtlasGlyphCache*, GrGlyph*, SkGlyphCache*,
- GrMaskFormat expectedMaskFormat);
+ bool addGlyphToAtlas(GrResourceProvider*, GrDeferredUploadTarget*, GrAtlasGlyphCache*, GrGlyph*,
+ SkGlyphCache*, GrMaskFormat expectedMaskFormat);
// testing
int countGlyphs() const { return fCache.count(); }
@@ -109,7 +109,8 @@ private:
*/
class GrAtlasGlyphCache : public GrOnFlushCallbackObject {
public:
- GrAtlasGlyphCache(GrContext*, float maxTextureBytes, GrDrawOpAtlas::AllowMultitexturing);
+ GrAtlasGlyphCache(GrProxyProvider*, float maxTextureBytes,
+ GrDrawOpAtlas::AllowMultitexturing);
~GrAtlasGlyphCache() override;
// The user of the cache may hold a long-lived ref to the returned strike. However, actions by
// another client of the cache may cause the strike to be purged while it is still reffed.
@@ -132,7 +133,7 @@ public:
SkASSERT(numProxies);
if (this->initAtlas(format)) {
- *numProxies = this->getAtlas(format)->pageCount();
+ *numProxies = this->getAtlas(format)->numActivePages();
return this->getAtlas(format)->getProxies();
}
*numProxies = 0;
@@ -165,11 +166,13 @@ public:
}
// add to texture atlas that matches this format
- bool addToAtlas(GrAtlasTextStrike* strike, GrDrawOpAtlas::AtlasID* id,
+ bool addToAtlas(GrResourceProvider* resourceProvider, GrAtlasTextStrike* strike,
+ GrDrawOpAtlas::AtlasID* id,
GrDeferredUploadTarget* target, GrMaskFormat format, int width, int height,
const void* image, SkIPoint16* loc) {
fPreserveStrike = strike;
- return this->getAtlas(format)->addToAtlas(id, target, width, height, image, loc);
+ return this->getAtlas(format)->addToAtlas(resourceProvider, id, target,
+ width, height, image, loc);
}
// Some clients may wish to verify the integrity of the texture backing store of the
@@ -190,8 +193,7 @@ public:
}
}
- void postFlush(GrDeferredUploadToken startTokenForNextFlush,
- const uint32_t* opListIDs, int numOpListIDs) override {
+ void postFlush(GrDeferredUploadToken startTokenForNextFlush, const uint32_t*, int) override {
for (int i = 0; i < kMaskFormatCount; ++i) {
if (fAtlases[i]) {
fAtlases[i]->compact(startTokenForNextFlush);
@@ -206,13 +208,11 @@ public:
///////////////////////////////////////////////////////////////////////////
// Functions intended debug only
#ifdef SK_DEBUG
- void dump() const;
+ void dump(GrContext*) const;
#endif
void setAtlasSizes_ForTesting(const GrDrawOpAtlasConfig configs[3]);
- GrContext* context() const { return fContext; }
-
private:
static GrPixelConfig MaskFormatToPixelConfig(GrMaskFormat format, const GrCaps& caps) {
switch (format) {
@@ -258,7 +258,7 @@ private:
static void HandleEviction(GrDrawOpAtlas::AtlasID, void*);
using StrikeHash = SkTDynamicHash<GrAtlasTextStrike, SkDescriptor>;
- GrContext* fContext;
+ GrProxyProvider* fProxyProvider;
StrikeHash fCache;
GrDrawOpAtlas::AllowMultitexturing fAllowMultitexturing;
std::unique_ptr<GrDrawOpAtlas> fAtlases[kMaskFormatCount];
diff --git a/src/gpu/text/GrAtlasTextBlob.h b/src/gpu/text/GrAtlasTextBlob.h
index 6e1d0b1bde..0fae3cd8c7 100644
--- a/src/gpu/text/GrAtlasTextBlob.h
+++ b/src/gpu/text/GrAtlasTextBlob.h
@@ -560,9 +560,9 @@ public:
* SkAutoGlyphCache is reused then it can save the cost of multiple detach/attach operations of
* SkGlyphCache.
*/
- VertexRegenerator(GrAtlasTextBlob* blob, int runIdx, int subRunIdx, const SkMatrix& viewMatrix,
- SkScalar x, SkScalar y, GrColor color, GrDeferredUploadTarget*,
- GrAtlasGlyphCache*, SkAutoGlyphCache*);
+ VertexRegenerator(GrResourceProvider*, GrAtlasTextBlob*, int runIdx, int subRunIdx,
+ const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
+ GrDeferredUploadTarget*, GrAtlasGlyphCache*, SkAutoGlyphCache*);
struct Result {
/**
@@ -589,6 +589,7 @@ private:
template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
Result doRegen();
+ GrResourceProvider* fResourceProvider;
const SkMatrix& fViewMatrix;
GrAtlasTextBlob* fBlob;
GrDeferredUploadTarget* fUploadTarget;
diff --git a/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp b/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
index 1a9c3d73e4..500ae31703 100644
--- a/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
+++ b/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
@@ -190,11 +190,13 @@ inline void regen_vertices(char* vertex, const GrGlyph* glyph, size_t vertexStri
}
}
-Regenerator::VertexRegenerator(GrAtlasTextBlob* blob, int runIdx, int subRunIdx,
+Regenerator::VertexRegenerator(GrResourceProvider* resourceProvider, GrAtlasTextBlob* blob,
+ int runIdx, int subRunIdx,
const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
GrDeferredUploadTarget* uploadTarget, GrAtlasGlyphCache* glyphCache,
SkAutoGlyphCache* lazyCache)
- : fViewMatrix(viewMatrix)
+ : fResourceProvider(resourceProvider)
+ , fViewMatrix(viewMatrix)
, fBlob(blob)
, fUploadTarget(uploadTarget)
, fGlyphCache(glyphCache)
@@ -274,8 +276,8 @@ Regenerator::Result Regenerator::doRegen() {
SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
if (!fGlyphCache->hasGlyph(glyph) &&
- !strike->addGlyphToAtlas(fUploadTarget, fGlyphCache, glyph, fLazyCache->get(),
- fSubRun->maskFormat())) {
+ !strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache, glyph,
+ fLazyCache->get(), fSubRun->maskFormat())) {
fBrokenRun = glyphIdx > 0;
result.fFinished = false;
return result;
diff --git a/tests/DrawOpAtlasTest.cpp b/tests/DrawOpAtlasTest.cpp
new file mode 100644
index 0000000000..a34618e38f
--- /dev/null
+++ b/tests/DrawOpAtlasTest.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContextPriv.h"
+#include "Test.h"
+#include "text/GrAtlasGlyphCache.h"
+
+static const int kNumPlots = 2;
+static const int kPlotSize = 32;
+static const int kAtlasSize = kNumPlots * kPlotSize;
+
+int GrDrawOpAtlas::numAllocated_TestingOnly() const {
+ int count = 0;
+ for (uint32_t i = 0; i < this->maxPages(); ++i) {
+ if (fProxies[i]->priv().isInstantiated()) {
+ ++count;
+ }
+ }
+
+ return count;
+}
+
+void EvictionFunc(GrDrawOpAtlas::AtlasID atlasID, void*) {
+ SkASSERT(0); // The unit test shouldn't exercise this code path
+}
+
+static void check(skiatest::Reporter* r, GrDrawOpAtlas* atlas,
+ uint32_t expectedActive, uint32_t expectedMax, int expectedAlloced) {
+ REPORTER_ASSERT(r, expectedActive == atlas->numActivePages());
+ REPORTER_ASSERT(r, expectedMax == atlas->maxPages());
+ REPORTER_ASSERT(r, expectedAlloced == atlas->numAllocated_TestingOnly());
+}
+
+class TestingUploadTarget : public GrDeferredUploadTarget {
+public:
+ TestingUploadTarget() { }
+
+ const GrTokenTracker* tokenTracker() final {
+ return &fTokenTracker;
+ }
+
+ GrDeferredUploadToken addInlineUpload(GrDeferredTextureUploadFn&&) final {
+ SkASSERT(0); // this test shouldn't invoke this code path
+ return fTokenTracker.nextDrawToken();
+ }
+
+ virtual GrDeferredUploadToken addASAPUpload(GrDeferredTextureUploadFn&& upload) final {
+ return fTokenTracker.nextTokenToFlush();
+ }
+
+ void issueDrawToken() { fTokenTracker.issueDrawToken(); }
+ void flushToken() { fTokenTracker.flushToken(); }
+
+private:
+ GrTokenTracker fTokenTracker;
+
+ typedef GrDeferredUploadTarget INHERITED;
+};
+
+static bool fill_plot(GrDrawOpAtlas* atlas,
+ GrResourceProvider* resourceProvider,
+ GrDeferredUploadTarget* target,
+ GrDrawOpAtlas::AtlasID* atlasID,
+ int alpha) {
+ SkImageInfo ii = SkImageInfo::MakeA8(kPlotSize, kPlotSize);
+
+ SkBitmap data;
+ data.allocPixels(ii);
+ data.eraseARGB(alpha, 0, 0, 0);
+
+ SkIPoint16 loc;
+ bool result = atlas->addToAtlas(resourceProvider, atlasID, target, kPlotSize, kPlotSize,
+ data.getAddr(0, 0), &loc);
+ return result;
+}
+
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DrawOpAtlas, reporter, ctxInfo) {
+ auto context = ctxInfo.grContext();
+ auto proxyProvider = context->contextPriv().proxyProvider();
+ auto resourceProvider = context->contextPriv().resourceProvider();
+ auto drawingManager = context->contextPriv().drawingManager();
+
+ GrOnFlushResourceProvider onFlushResourceProvider(drawingManager);
+ TestingUploadTarget uploadTarget;
+
+ std::unique_ptr<GrDrawOpAtlas> atlas = GrDrawOpAtlas::Make(
+ proxyProvider,
+ kAlpha_8_GrPixelConfig,
+ kAtlasSize, kAtlasSize,
+ kNumPlots, kNumPlots,
+ GrDrawOpAtlas::AllowMultitexturing::kYes,
+ EvictionFunc, nullptr);
+ check(reporter, atlas.get(), 0, 4, 0);
+
+ // Fill up the first level
+ GrDrawOpAtlas::AtlasID atlasIDs[kNumPlots * kNumPlots];
+ for (int i = 0; i < kNumPlots * kNumPlots; ++i) {
+ bool result = fill_plot(atlas.get(), resourceProvider, &uploadTarget, &atlasIDs[i], i*32);
+ REPORTER_ASSERT(reporter, result);
+ check(reporter, atlas.get(), 1, 4, 1);
+ }
+
+ atlas->instantiate(&onFlushResourceProvider);
+ check(reporter, atlas.get(), 1, 4, 1);
+
+ // Force allocation of a second level
+ GrDrawOpAtlas::AtlasID atlasID;
+ bool result = fill_plot(atlas.get(), resourceProvider, &uploadTarget, &atlasID, 4*32);
+ REPORTER_ASSERT(reporter, result);
+ check(reporter, atlas.get(), 2, 4, 2);
+
+ // Simulate a lot of draws using only the first plot. The last texture should be compacted.
+ for (int i = 0; i < 512; ++i) {
+ atlas->setLastUseToken(atlasIDs[0], uploadTarget.tokenTracker()->nextDrawToken());
+ uploadTarget.issueDrawToken();
+ uploadTarget.flushToken();
+ atlas->compact(uploadTarget.tokenTracker()->nextTokenToFlush());
+ }
+
+ check(reporter, atlas.get(), 1, 4, 1);
+}
+
+#endif