Separate creation time & flush time behavior in GrDrawOpAtlas (take 3)

This CL clarifies what is going on in the GrDrawOpAtlas and GrAtlasGlyphCache.

For the GrDrawOpAtlas:
  At creation time all the allowed pages are created (with their backing GrTextureProxies) but they aren't instantiated.

  The GrDrawOpAtlas::instantiate call is called in preFlushCB and allocates any pages known to be needed at the start of flush

  GrDrawOpAtlas::addToAtlas is called at flush time and, if a new page is activated, will instantiated it at that time.

  During compaction, an unused page will be deInstantiated but its Plots and backing GrTextureProxy will remain alive.

The GrAtlasGlyphCache reflects the changes to the GrDrawOpAtlas
  It now carries a GrProxyProvider for when it needs to create an atlas
  It passes in a GrResourceProvider* at flush time to allow instantiation.

  It does not, yet, allocate that GrDrawOpAtlases it might ever require.

TBR=bsalomon@google.com
Change-Id: I276d339d81e7b709140e082a7b58c5584f73ab70
Reviewed-on: https://skia-review.googlesource.com/111100
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
Robert Phillips 2018-03-01 10:24:02 -05:00 committed by Skia Commit-Bot
parent a63d6900d3
commit 4bc7011802
20 changed files with 478 additions and 231 deletions

View File

@ -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",

View File

@ -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.
*/

View File

@ -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();

View File

@ -293,7 +293,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);

View File

@ -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; }

View File

@ -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();
// set up allocated plots
fPages[i].fPlotArray.reset(new sk_sp<Plot>[ numPlotsX * numPlotsY ]);
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(fNumPages, plotIndex, 1, x, y, fPlotWidth, fPlotHeight,
currPlot->reset(new Plot(i, plotIndex, 1, x, y, fPlotWidth, fPlotHeight,
fPixelConfig));
// build LRU list
fPages[fNumPages].fPlotList.addToHead(currPlot->get());
fPages[i].fPlotList.addToHead(currPlot->get());
++currPlot;
}
}
#ifdef DUMP_ATLAS_DATA
if (gDumpAtlasData) {
SkDebugf("created %d\n", fNumPages);
}
#endif
fNumPages++;
return true;
}
inline void GrDrawOpAtlas::deleteLastPage() {
uint32_t lastPageIndex = fNumPages - 1;
// clean out the plots
fPages[lastPageIndex].fPlotList.reset();
fPages[lastPageIndex].fPlotArray.reset(nullptr);
// remove ref to texture proxy
fProxies[lastPageIndex].reset(nullptr);
--fNumPages;
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("activated page#: %d\n", fNumActivePages);
}
#endif
++fNumActivePages;
return true;
}
inline void GrDrawOpAtlas::deactivateLastPage() {
SkASSERT(fNumActivePages);
uint32_t lastPageIndex = fNumActivePages - 1;
int numPlotsX = fTextureWidth/fPlotWidth;
int numPlotsY = fTextureHeight/fPlotHeight;
fPages[lastPageIndex].fPlotList.reset();
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;
}

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -121,7 +121,8 @@ private:
///////////////////////////////////////////////////////////////////////////////
GrBitmapTextGeoProc::GrBitmapTextGeoProc(GrColor color,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params, GrMaskFormat format,
const SkMatrix& localMatrix, bool usesLocalCoords)
: INHERITED(kGrBitmapTextGeoProc_ClassID)
@ -130,6 +131,8 @@ GrBitmapTextGeoProc::GrBitmapTextGeoProc(GrColor color,
, fUsesLocalCoords(usesLocalCoords)
, fInColor(nullptr)
, fMaskFormat(format) {
SkASSERT(numProxies <= kMaxTextures);
fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
bool hasVertexColor = kA8_GrMaskFormat == fMaskFormat ||
@ -139,18 +142,23 @@ GrBitmapTextGeoProc::GrBitmapTextGeoProc(GrColor color,
}
fInTextureCoords = &this->addVertexAttrib("inTextureCoords", kUShort2_GrVertexAttribType);
for (int i = 0; i < kMaxTextures; ++i) {
if (proxies[i]) {
for (int i = 0; i < numProxies; ++i) {
SkASSERT(proxies[i]);
fTextureSamplers[i].reset(std::move(proxies[i]), params);
this->addTextureSampler(&fTextureSamplers[i]);
}
}
}
void GrBitmapTextGeoProc::addNewProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures],
void GrBitmapTextGeoProc::addNewProxies(const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params) {
for (int i = 0; i < kMaxTextures; ++i) {
if (proxies[i] && !fTextureSamplers[i].isInitialized()) {
SkASSERT(numProxies <= kMaxTextures);
for (int i = 0; i < numProxies; ++i) {
SkASSERT(proxies[i]);
if (!fTextureSamplers[i].isInitialized()) {
fTextureSamplers[i].reset(std::move(proxies[i]), params);
this->addTextureSampler(&fTextureSamplers[i]);
}
@ -201,7 +209,7 @@ sk_sp<GrGeometryProcessor> GrBitmapTextGeoProc::TestCreate(GrProcessorTestData*
break;
}
return GrBitmapTextGeoProc::Make(GrRandomColor(d->fRandom), proxies, samplerState,
return GrBitmapTextGeoProc::Make(GrRandomColor(d->fRandom), proxies, 1, samplerState,
format, GrTest::TestMatrix(d->fRandom),
d->fRandom->nextBool());
}

View File

@ -21,14 +21,14 @@ class GrInvariantOutput;
*/
class GrBitmapTextGeoProc : public GrGeometryProcessor {
public:
static constexpr int kMaxTextures = 4;
static sk_sp<GrGeometryProcessor> Make(GrColor color,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& p, GrMaskFormat format,
const SkMatrix& localMatrix, bool usesLocalCoords) {
return sk_sp<GrGeometryProcessor>(
new GrBitmapTextGeoProc(color, proxies, p, format,
new GrBitmapTextGeoProc(color, proxies, numProxies, p, format,
localMatrix, usesLocalCoords));
}
@ -45,14 +45,16 @@ public:
const SkMatrix& localMatrix() const { return fLocalMatrix; }
bool usesLocalCoords() const { return fUsesLocalCoords; }
void addNewProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], const GrSamplerState& p);
void addNewProxies(const sk_sp<GrTextureProxy>* proxies, int numProxies, const GrSamplerState&);
void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override;
private:
GrBitmapTextGeoProc(GrColor, const sk_sp<GrTextureProxy> proxies[kMaxTextures],
static constexpr int kMaxTextures = 4;
GrBitmapTextGeoProc(GrColor, const sk_sp<GrTextureProxy>* proxies, int numProxies,
const GrSamplerState& params, GrMaskFormat format,
const SkMatrix& localMatrix, bool usesLocalCoords);

View File

@ -207,7 +207,8 @@ private:
///////////////////////////////////////////////////////////////////////////////
GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params,
#ifdef SK_GAMMA_APPLY_TO_A8
float distanceAdjust,
@ -221,6 +222,8 @@ GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(
, fFlags(flags & kNonLCD_DistanceFieldEffectMask)
, fInColor(nullptr)
, fLocalMatrix(localMatrix) {
SkASSERT(numProxies <= kMaxTextures);
SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
if (flags & kPerspective_DistanceFieldEffectFlag) {
fInPosition = &this->addVertexAttrib("inPosition", kFloat3_GrVertexAttribType);
@ -229,18 +232,23 @@ GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc(
}
fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
fInTextureCoords = &this->addVertexAttrib("inTextureCoords", kUShort2_GrVertexAttribType);
for (int i = 0; i < kMaxTextures; ++i) {
if (proxies[i]) {
for (int i = 0; i < numProxies; ++i) {
SkASSERT(proxies[i]);
fTextureSamplers[i].reset(std::move(proxies[i]), params);
this->addTextureSampler(&fTextureSamplers[i]);
}
}
}
void GrDistanceFieldA8TextGeoProc::addNewProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures],
void GrDistanceFieldA8TextGeoProc::addNewProxies(const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params) {
for (int i = 0; i < kMaxTextures; ++i) {
if (proxies[i] && !fTextureSamplers[i].isInitialized()) {
SkASSERT(numProxies <= kMaxTextures);
for (int i = 0; i < numProxies; ++i) {
SkASSERT(proxies[i]);
if (!fTextureSamplers[i].isInitialized()) {
fTextureSamplers[i].reset(std::move(proxies[i]), params);
this->addTextureSampler(&fTextureSamplers[i]);
}
@ -287,7 +295,7 @@ sk_sp<GrGeometryProcessor> GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorT
#ifdef SK_GAMMA_APPLY_TO_A8
float lum = d->fRandom->nextF();
#endif
return GrDistanceFieldA8TextGeoProc::Make(proxies,
return GrDistanceFieldA8TextGeoProc::Make(proxies, 1,
samplerState,
#ifdef SK_GAMMA_APPLY_TO_A8
lum,
@ -493,29 +501,37 @@ private:
///////////////////////////////////////////////////////////////////////////////
GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc(
const SkMatrix& matrix,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params,
uint32_t flags)
: INHERITED(kGrDistanceFieldPathGeoProc_ClassID)
, fMatrix(matrix)
, fFlags(flags & kNonLCD_DistanceFieldEffectMask)
, fInColor(nullptr) {
SkASSERT(numProxies <= kMaxTextures);
SkASSERT(!(flags & ~kNonLCD_DistanceFieldEffectMask));
fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType);
fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
fInTextureCoords = &this->addVertexAttrib("inTextureCoords", kUShort2_GrVertexAttribType);
for (int i = 0; i < kMaxTextures; ++i) {
if (proxies[i]) {
for (int i = 0; i < numProxies; ++i) {
SkASSERT(proxies[i]);
fTextureSamplers[i].reset(std::move(proxies[i]), params);
this->addTextureSampler(&fTextureSamplers[i]);
}
}
}
void GrDistanceFieldPathGeoProc::addNewProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures],
void GrDistanceFieldPathGeoProc::addNewProxies(const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params) {
for (int i = 0; i < kMaxTextures; ++i) {
if (proxies[i] && !fTextureSamplers[i].isInitialized()) {
SkASSERT(numProxies <= kMaxTextures);
for (int i = 0; i < numProxies; ++i) {
SkASSERT(proxies[i]);
if (!fTextureSamplers[i].isInitialized()) {
fTextureSamplers[i].reset(std::move(proxies[i]), params);
this->addTextureSampler(&fTextureSamplers[i]);
}
@ -560,7 +576,7 @@ sk_sp<GrGeometryProcessor> GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTes
}
return GrDistanceFieldPathGeoProc::Make(GrTest::TestMatrix(d->fRandom),
proxies,
proxies, 1,
samplerState,
flags);
}
@ -787,7 +803,8 @@ private:
///////////////////////////////////////////////////////////////////////////////
GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc(
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params,
DistanceAdjust distanceAdjust,
uint32_t flags,
@ -796,6 +813,8 @@ GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc(
, fDistanceAdjust(distanceAdjust)
, fFlags(flags & kLCD_DistanceFieldEffectMask)
, fLocalMatrix(localMatrix) {
SkASSERT(numProxies <= kMaxTextures);
SkASSERT(!(flags & ~kLCD_DistanceFieldEffectMask) && (flags & kUseLCD_DistanceFieldEffectFlag));
if (fFlags & kPerspective_DistanceFieldEffectFlag) {
fInPosition = &this->addVertexAttrib("inPosition", kFloat3_GrVertexAttribType);
@ -804,19 +823,24 @@ GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc(
}
fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType);
fInTextureCoords = &this->addVertexAttrib("inTextureCoords", kUShort2_GrVertexAttribType);
for (int i = 0; i < kMaxTextures; ++i) {
if (proxies[i]) {
for (int i = 0; i < numProxies; ++i) {
SkASSERT(proxies[i]);
fTextureSamplers[i].reset(std::move(proxies[i]), params);
this->addTextureSampler(&fTextureSamplers[i]);
}
}
}
void GrDistanceFieldLCDTextGeoProc::addNewProxies(const sk_sp<GrTextureProxy> prox[kMaxTextures],
void GrDistanceFieldLCDTextGeoProc::addNewProxies(const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params) {
for (int i = 0; i < kMaxTextures; ++i) {
if (prox[i] && !fTextureSamplers[i].isInitialized()) {
fTextureSamplers[i].reset(std::move(prox[i]), params);
SkASSERT(numProxies <= kMaxTextures);
for (int i = 0; i < numProxies; ++i) {
SkASSERT(proxies[i]);
if (!fTextureSamplers[i].isInitialized()) {
fTextureSamplers[i].reset(std::move(proxies[i]), params);
this->addTextureSampler(&fTextureSamplers[i]);
}
}
@ -859,6 +883,6 @@ sk_sp<GrGeometryProcessor> GrDistanceFieldLCDTextGeoProc::TestCreate(GrProcessor
}
flags |= d->fRandom->nextBool() ? kBGR_DistanceFieldEffectFlag : 0;
SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
return GrDistanceFieldLCDTextGeoProc::Make(proxies, samplerState, wa, flags, localMatrix);
return GrDistanceFieldLCDTextGeoProc::Make(proxies, 1, samplerState, wa, flags, localMatrix);
}
#endif

View File

@ -53,22 +53,22 @@ enum GrDistanceFieldEffectFlags {
*/
class GrDistanceFieldA8TextGeoProc : public GrGeometryProcessor {
public:
static constexpr int kMaxTextures = 4;
/** The local matrix should be identity if local coords are not required by the GrPipeline. */
#ifdef SK_GAMMA_APPLY_TO_A8
static sk_sp<GrGeometryProcessor> Make(const sk_sp<GrTextureProxy> proxies[kMaxTextures],
static sk_sp<GrGeometryProcessor> Make(const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params, float lum, uint32_t flags,
const SkMatrix& localMatrixIfUsesLocalCoords) {
return sk_sp<GrGeometryProcessor>(new GrDistanceFieldA8TextGeoProc(
proxies, params, lum, flags, localMatrixIfUsesLocalCoords));
proxies, numProxies, params, lum, flags, localMatrixIfUsesLocalCoords));
}
#else
static sk_sp<GrGeometryProcessor> Make(const sk_sp<GrTextureProxy> proxies[kMaxTextures],
static sk_sp<GrGeometryProcessor> Make(const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params, uint32_t flags,
const SkMatrix& localMatrixIfUsesLocalCoords) {
return sk_sp<GrGeometryProcessor>(new GrDistanceFieldA8TextGeoProc(
proxies, params, flags, localMatrixIfUsesLocalCoords));
proxies, numProxies, params, flags, localMatrixIfUsesLocalCoords));
}
#endif
@ -85,20 +85,23 @@ public:
#endif
uint32_t getFlags() const { return fFlags; }
void addNewProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], const GrSamplerState& p);
void addNewProxies(const sk_sp<GrTextureProxy>* proxies, int numProxies, const GrSamplerState&);
void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
private:
GrDistanceFieldA8TextGeoProc(const sk_sp<GrTextureProxy> proxies[kMaxTextures],
GrDistanceFieldA8TextGeoProc(const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params,
#ifdef SK_GAMMA_APPLY_TO_A8
float distanceAdjust,
#endif
uint32_t flags, const SkMatrix& localMatrix);
static constexpr int kMaxTextures = 4;
TextureSampler fTextureSamplers[kMaxTextures];
#ifdef SK_GAMMA_APPLY_TO_A8
float fDistanceAdjust;
@ -122,14 +125,14 @@ private:
*/
class GrDistanceFieldPathGeoProc : public GrGeometryProcessor {
public:
static constexpr int kMaxTextures = 4;
/** The local matrix should be identity if local coords are not required by the GrPipeline. */
static sk_sp<GrGeometryProcessor> Make(const SkMatrix& matrix,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params, uint32_t flags) {
return sk_sp<GrGeometryProcessor>(
new GrDistanceFieldPathGeoProc(matrix, proxies, params, flags));
new GrDistanceFieldPathGeoProc(matrix, proxies, numProxies, params, flags));
}
~GrDistanceFieldPathGeoProc() override {}
@ -142,15 +145,18 @@ public:
const SkMatrix& matrix() const { return fMatrix; }
uint32_t getFlags() const { return fFlags; }
void addNewProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], const GrSamplerState& p);
void addNewProxies(const sk_sp<GrTextureProxy>* proxies, int numProxies, const GrSamplerState&);
void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
private:
static constexpr int kMaxTextures = 4;
GrDistanceFieldPathGeoProc(const SkMatrix& matrix,
const sk_sp<GrTextureProxy> proxies[kMaxTextures],
const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState&, uint32_t flags);
SkMatrix fMatrix; // view matrix if perspective, local matrix otherwise
@ -188,15 +194,14 @@ public:
}
};
static constexpr int kMaxTextures = 4;
static sk_sp<GrGeometryProcessor> Make(const sk_sp<GrTextureProxy> proxies[kMaxTextures],
static sk_sp<GrGeometryProcessor> Make(const sk_sp<GrTextureProxy>* proxies,
int numProxies,
const GrSamplerState& params,
DistanceAdjust distanceAdjust,
uint32_t flags,
const SkMatrix& localMatrixIfUsesLocalCoords) {
return sk_sp<GrGeometryProcessor>(new GrDistanceFieldLCDTextGeoProc(
proxies, params, distanceAdjust, flags, localMatrixIfUsesLocalCoords));
proxies, numProxies, params, distanceAdjust, flags, localMatrixIfUsesLocalCoords));
}
~GrDistanceFieldLCDTextGeoProc() override {}
@ -210,17 +215,19 @@ public:
uint32_t getFlags() const { return fFlags; }
const SkMatrix& localMatrix() const { return fLocalMatrix; }
void addNewProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], const GrSamplerState& p);
void addNewProxies(const sk_sp<GrTextureProxy>* proxies, int numProxies, const GrSamplerState&);
void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
private:
GrDistanceFieldLCDTextGeoProc(const sk_sp<GrTextureProxy> proxies[kMaxTextures],
GrDistanceFieldLCDTextGeoProc(const sk_sp<GrTextureProxy>* proxies, int numProxies,
const GrSamplerState& params, DistanceAdjust wa, uint32_t flags,
const SkMatrix& localMatrix);
static constexpr int kMaxTextures = 4;
TextureSampler fTextureSamplers[kMaxTextures];
DistanceAdjust fDistanceAdjust;
uint32_t fFlags;

View File

@ -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;
@ -234,7 +236,7 @@ void GrAtlasTextOp::onPrepareDraws(Target* target) {
unsigned int atlasPageCount;
const sk_sp<GrTextureProxy>* proxies = fFontCache->getProxies(maskFormat, &atlasPageCount);
if (!atlasPageCount || !proxies[0]) {
if (!proxies[0]) {
SkDebugf("Could not allocate backing texture for atlas\n");
return;
}
@ -248,7 +250,7 @@ void GrAtlasTextOp::onPrepareDraws(Target* target) {
SkDEBUGCODE(dfPerspective = fGeoData[0].fViewMatrix.hasPerspective());
} else {
flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
this->color(), proxies, GrSamplerState::ClampNearest(), maskFormat,
this->color(), proxies, atlasPageCount, GrSamplerState::ClampNearest(), maskFormat,
localMatrix, this->usesLocalCoords());
}
@ -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();
@ -328,14 +330,14 @@ void GrAtlasTextOp::flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) co
if (this->usesDistanceFields()) {
if (this->isLCD()) {
reinterpret_cast<GrDistanceFieldLCDTextGeoProc*>(gp)->addNewProxies(
proxies, GrSamplerState::ClampBilerp());
proxies, numProxies, GrSamplerState::ClampBilerp());
} else {
reinterpret_cast<GrDistanceFieldA8TextGeoProc*>(gp)->addNewProxies(
proxies, GrSamplerState::ClampBilerp());
proxies, numProxies, GrSamplerState::ClampBilerp());
}
} else {
reinterpret_cast<GrBitmapTextGeoProc*>(gp)->addNewProxies(
proxies, GrSamplerState::ClampNearest());
proxies, numProxies, GrSamplerState::ClampNearest());
}
}
@ -427,7 +429,7 @@ bool GrAtlasTextOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
// (see comments in GrAtlasTextContext::ComputeCanonicalColor)
sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor() const {
unsigned int numProxies;
const sk_sp<GrTextureProxy>* p = fFontCache->getProxies(this->maskFormat(), &numProxies);
const sk_sp<GrTextureProxy>* proxies = fFontCache->getProxies(this->maskFormat(), &numProxies);
bool isLCD = this->isLCD();
SkMatrix localMatrix = SkMatrix::I();
@ -451,7 +453,8 @@ sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor() const {
GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(
redCorrection, greenCorrection, blueCorrection);
return GrDistanceFieldLCDTextGeoProc::Make(p, GrSamplerState::ClampBilerp(), widthAdjust,
return GrDistanceFieldLCDTextGeoProc::Make(proxies, numProxies,
GrSamplerState::ClampBilerp(), widthAdjust,
fDFGPFlags, localMatrix);
} else {
#ifdef SK_GAMMA_APPLY_TO_A8
@ -462,10 +465,12 @@ sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor() const {
correction = fDistanceAdjustTable->getAdjustment(lum >> kDistanceAdjustLumShift,
fUseGammaCorrectDistanceTable);
}
return GrDistanceFieldA8TextGeoProc::Make(p, GrSamplerState::ClampBilerp(),
return GrDistanceFieldA8TextGeoProc::Make(proxies, numProxies,
GrSamplerState::ClampBilerp(),
correction, fDFGPFlags, localMatrix);
#else
return GrDistanceFieldA8TextGeoProc::Make(p, GrSamplerState::ClampBilerp(),
return GrDistanceFieldA8TextGeoProc::Make(proxies, numProxies,
GrSamplerState::ClampBilerp(),
fDFGPFlags, localMatrix);
#endif
}

View File

@ -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());
}
@ -222,11 +222,6 @@ private:
FlushInfo flushInfo;
flushInfo.fPipeline = fHelper.makePipeline(target);
// Setup GrGeometryProcessor
GrDrawOpAtlas* atlas = fAtlas;
uint32_t atlasPageCount = atlas->pageCount();
if (!atlasPageCount) {
return;
}
const SkMatrix& ctm = fShapes[0].fViewMatrix;
if (fUsesDistanceField) {
uint32_t flags = 0;
@ -249,7 +244,8 @@ private:
matrix = &SkMatrix::I();
}
flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(
*matrix, atlas->getProxies(), GrSamplerState::ClampBilerp(), flags);
*matrix, fAtlas->getProxies(), fAtlas->numActivePages(),
GrSamplerState::ClampBilerp(), flags);
} else {
SkMatrix invert;
if (fHelper.usesLocalCoords()) {
@ -260,8 +256,9 @@ private:
}
flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
this->color(), atlas->getProxies(), GrSamplerState::ClampNearest(),
kA8_GrMaskFormat, invert, fHelper.usesLocalCoords());
this->color(), fAtlas->getProxies(), fAtlas->numActivePages(),
GrSamplerState::ClampNearest(), kA8_GrMaskFormat, invert,
fHelper.usesLocalCoords());
}
// allocate vertices
@ -335,7 +332,7 @@ private:
// check to see if df path is cached
ShapeData::Key key(args.fShape, SkScalarCeilToInt(desiredDimension));
shapeData = fShapeCache->find(key);
if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
if (nullptr == shapeData || !fAtlas->hasID(shapeData->fID)) {
// Remove the stale cache entry
if (shapeData) {
fShapeCache->remove(shapeData->fKey);
@ -347,7 +344,7 @@ private:
shapeData = new ShapeData;
if (!this->addDFPathToAtlas(target,
&flushInfo,
atlas,
fAtlas,
shapeData,
args.fShape,
SkScalarCeilToInt(desiredDimension),
@ -360,7 +357,7 @@ private:
// check to see if bitmap path is cached
ShapeData::Key key(args.fShape, args.fViewMatrix);
shapeData = fShapeCache->find(key);
if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
if (nullptr == shapeData || !fAtlas->hasID(shapeData->fID)) {
// Remove the stale cache entry
if (shapeData) {
fShapeCache->remove(shapeData->fKey);
@ -371,7 +368,7 @@ private:
shapeData = new ShapeData;
if (!this->addBMPathToAtlas(target,
&flushInfo,
atlas,
fAtlas,
shapeData,
args.fShape,
args.fViewMatrix)) {
@ -382,9 +379,9 @@ private:
}
auto uploadTarget = target->deferredUploadTarget();
atlas->setLastUseToken(shapeData->fID, uploadTarget->tokenTracker()->nextDrawToken());
fAtlas->setLastUseToken(shapeData->fID, uploadTarget->tokenTracker()->nextDrawToken());
this->writePathVertices(atlas,
this->writePathVertices(fAtlas,
offset,
args.fColor,
vertexStride,
@ -400,6 +397,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 +487,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 +530,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 +592,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,15 +698,15 @@ 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) {
reinterpret_cast<GrDistanceFieldPathGeoProc*>(gp)->addNewProxies(
fAtlas->getProxies(), GrSamplerState::ClampBilerp());
fAtlas->getProxies(), fAtlas->numActivePages(), GrSamplerState::ClampBilerp());
} else {
reinterpret_cast<GrBitmapTextGeoProc*>(gp)->addNewProxies(
fAtlas->getProxies(), GrSamplerState::ClampNearest());
fAtlas->getProxies(), fAtlas->numActivePages(), GrSamplerState::ClampNearest());
}
}
@ -788,7 +790,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 +860,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,

View File

@ -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) {

View File

@ -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];

View File

@ -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;

View File

@ -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;

132
tests/DrawOpAtlasTest.cpp Normal file
View File

@ -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