* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/GrDrawOpAtlas.h"
#include "include/gpu/GrContext.h"
#include "include/gpu/GrTexture.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrOnFlushResourceProvider.h"
#include "src/gpu/GrOpFlushState.h"
#include "src/gpu/GrProxyProvider.h"
#include "src/gpu/GrRectanizer.h"
#include "src/gpu/GrResourceProvider.h"
#include "src/gpu/GrSurfaceProxyPriv.h"
#include "src/gpu/GrTracing.h"
void GrDrawOpAtlas::instantiate(GrOnFlushResourceProvider* onFlushResourceProvider) {
for (uint32_t i = 0; i < fNumActivePages; ++i) {
SkASSERT(fProxies[i] && fProxies[i]->isInstantiated());
}
}
std::unique_ptr<GrDrawOpAtlas> GrDrawOpAtlas::Make(GrProxyProvider* proxyProvider,
const GrBackendFormat& format,
GrColorType colorType, int width,
int height, int plotWidth, int plotHeight,
AllowMultitexturing allowMultitexturing,
int atlasPageNum,
int plotOldThreshold,
GrDrawOpAtlas::EvictionFunc func, void* data) {
if (!format.isValid()) {
return nullptr;
}
std::unique_ptr<GrDrawOpAtlas> atlas(new GrDrawOpAtlas(proxyProvider, format, colorType, width,
height, plotWidth, plotHeight,
allowMultitexturing, atlasPageNum, plotOldThreshold));
if (!atlas->getProxies()[0]) {
return nullptr;
}
atlas->registerEvictionCallback(func, data);
return atlas;
}
#ifdef DUMP_ATLAS_DATA
static bool gDumpAtlasData = false;
#endif
GrDrawOpAtlas::Plot::Plot(int pageIndex, int plotIndex, uint64_t genID, int offX, int offY,
int width, int height, GrColorType colorType)
: fLastUpload(GrDeferredUploadToken::AlreadyFlushedToken())
, fLastUse(GrDeferredUploadToken::AlreadyFlushedToken())
, fFlushesSinceLastUse(0)
, fPageIndex(pageIndex)
, fPlotIndex(plotIndex)
, fGenID(genID)
, fID(CreateId(fPageIndex, fPlotIndex, fGenID))
, fData(nullptr)
, fWidth(width)
, fHeight(height)
, fX(offX)
, fY(offY)
, fRects(nullptr)
, fOffset(SkIPoint16::Make(fX * fWidth, fY * fHeight))
, fColorType(colorType)
, fBytesPerPixel(GrColorTypeBytesPerPixel(colorType))
#ifdef SK_DEBUG
, fDirty(false)
#endif
{
SkASSERT(((width*fBytesPerPixel) & 0x3) == 0);
SkASSERT(fBytesPerPixel != 3 && fBytesPerPixel <= 4);
fDirtyRect.setEmpty();
}
GrDrawOpAtlas::Plot::~Plot() {
sk_free(fData);
delete fRects;
}
bool GrDrawOpAtlas::Plot::addSubImage(int width, int height, const void* image, SkIPoint16* loc) {
SkASSERT(width <= fWidth && height <= fHeight);
if (!fRects) {
fRects = GrRectanizer::Factory(fWidth, fHeight);
}
if (!fRects->addRect(width, height, loc)) {
return false;
}
if (!fData) {
fData = reinterpret_cast<unsigned char*>(sk_calloc_throw(fBytesPerPixel * fWidth *
fHeight));
}
size_t rowBytes = width * fBytesPerPixel;
const unsigned char* imagePtr = (const unsigned char*)image;
unsigned char* dataPtr = fData;
dataPtr += fBytesPerPixel * fWidth * loc->fY;
dataPtr += fBytesPerPixel * loc->fX;
if (4 == fBytesPerPixel && kSkia8888_GrPixelConfig == kBGRA_8888_GrPixelConfig) {
for (int i = 0; i < height; ++i) {
SkOpts::RGBA_to_BGRA((uint32_t*)dataPtr, (const uint32_t*)imagePtr, width);
dataPtr += fBytesPerPixel * fWidth;
imagePtr += rowBytes;
}
} else {
for (int i = 0; i < height; ++i) {
memcpy(dataPtr, imagePtr, rowBytes);
dataPtr += fBytesPerPixel * fWidth;
imagePtr += rowBytes;
}
}
fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
loc->fX += fOffset.fX;
loc->fY += fOffset.fY;
SkDEBUGCODE(fDirty = true;)
return true;
}
void GrDrawOpAtlas::Plot::uploadToTexture(GrDeferredTextureUploadWritePixelsFn& writePixels,
GrTextureProxy* proxy) {
SkASSERT(fDirty && fData && proxy && proxy->peekTexture());
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
size_t rowBytes = fBytesPerPixel * fWidth;
const unsigned char* dataPtr = fData;
unsigned int clearBits = 0x3 / fBytesPerPixel;
fDirtyRect.fLeft &= ~clearBits;
fDirtyRect.fRight += clearBits;
fDirtyRect.fRight &= ~clearBits;
SkASSERT(fDirtyRect.fRight <= fWidth);
dataPtr += rowBytes * fDirtyRect.fTop;
dataPtr += fBytesPerPixel * fDirtyRect.fLeft;
writePixels(proxy, fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
fDirtyRect.width(), fDirtyRect.height(), fColorType, dataPtr, rowBytes);
fDirtyRect.setEmpty();
SkDEBUGCODE(fDirty = false;)
}
void GrDrawOpAtlas::Plot::resetRects() {
if (fRects) {
fRects->reset();
}
fGenID++;
fID = CreateId(fPageIndex, fPlotIndex, fGenID);
fLastUpload = GrDeferredUploadToken::AlreadyFlushedToken();
fLastUse = GrDeferredUploadToken::AlreadyFlushedToken();
if (fData) {
sk_bzero(fData, fBytesPerPixel * fWidth * fHeight);
}
fDirtyRect.setEmpty();
SkDEBUGCODE(fDirty = false;)
}
GrDrawOpAtlas::GrDrawOpAtlas(GrProxyProvider* proxyProvider, const GrBackendFormat& format,
GrColorType colorType, int width, int height,
int plotWidth, int plotHeight, AllowMultitexturing allowMultitexturing,
int atlasPageNum, int plotOldThreshold)
: fFormat(format)
, fColorType(colorType)
, fTextureWidth(width)
, fTextureHeight(height)
, fPlotWidth(plotWidth)
, fPlotHeight(plotHeight)
, fAtlasGeneration(kInvalidAtlasGeneration + 1)
, fPrevFlushToken(GrDeferredUploadToken::AlreadyFlushedToken())
, fMaxPages(AllowMultitexturing::kYes == allowMultitexturing ? ((atlasPageNum > 16) ? 16 : atlasPageNum) : 1)
, fNumActivePages(0)
, fPlotOldThreshold(plotOldThreshold) {
int numPlotsX = width/plotWidth;
int numPlotsY = height/plotHeight;
SkASSERT(numPlotsX * numPlotsY <= GrDrawOpAtlas::kMaxPlots);
SkASSERT(fPlotWidth * numPlotsX == fTextureWidth);
SkASSERT(fPlotHeight * numPlotsY == fTextureHeight);
fNumPlots = numPlotsX * numPlotsY;
this->createPages(proxyProvider);
SkDebugf("Texture[Width:%{public}d, Height:%{public}d, MaxPage:%{public}d], Plot[Width:%{public}d, Height:%{public}d, OldThreshold:%{public}d].", \
fTextureWidth, fTextureHeight, fMaxPages, fPlotWidth, fPlotHeight, fPlotOldThreshold);
}
inline void GrDrawOpAtlas::processEviction(AtlasID id) {
for (int i = 0; i < fEvictionCallbacks.count(); i++) {
(*fEvictionCallbacks[i].fFunc)(id, fEvictionCallbacks[i].fData);
}
++fAtlasGeneration;
}
inline bool GrDrawOpAtlas::updatePlot(GrDeferredUploadTarget* target, AtlasID* id, Plot* plot) {
int pageIdx = GetPageIndexFromID(plot->id());
this->makeMRU(plot, pageIdx);
if (plot->lastUploadToken() < target->tokenTracker()->nextTokenToFlush()) {
sk_sp<Plot> plotsp(SkRef(plot));
GrTextureProxy* proxy = fProxies[pageIdx].get();
SkASSERT(proxy->isInstantiated());
GrDeferredUploadToken lastUploadToken = target->addASAPUpload(
[plotsp, proxy](GrDeferredTextureUploadWritePixelsFn& writePixels) {
plotsp->uploadToTexture(writePixels, proxy);
});
plot->setLastUploadToken(lastUploadToken);
}
*id = plot->id();
return true;
}
bool GrDrawOpAtlas::uploadToPage(unsigned int pageIdx, AtlasID* id, GrDeferredUploadTarget* target,
int width, int height, const void* image, SkIPoint16* loc) {
SkASSERT(fProxies[pageIdx] && fProxies[pageIdx]->isInstantiated());
PlotList::Iter plotIter;
plotIter.init(fPages[pageIdx].fPlotList, PlotList::Iter::kHead_IterStart);
for (Plot* plot = plotIter.get(); plot; plot = plotIter.next()) {
SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp());
if (plot->addSubImage(width, height, image, loc)) {
return this->updatePlot(target, id, plot);
}
}
return false;
}
GrDrawOpAtlas::ErrorCode GrDrawOpAtlas::addToAtlas(GrResourceProvider* resourceProvider,
AtlasID* id, GrDeferredUploadTarget* target,
int width, int height,
const void* image, SkIPoint16* loc) {
if (width > fPlotWidth || height > fPlotHeight) {
return ErrorCode::kError;
}
for (unsigned int pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
if (this->uploadToPage(pageIdx, id, target, width, height, image, loc)) {
return ErrorCode::kSucceeded;
}
}
if (fNumActivePages == this->maxPages()) {
for (unsigned int pageIdx = 0; pageIdx < fNumActivePages; ++pageIdx) {
Plot* plot = fPages[pageIdx].fPlotList.tail();
SkASSERT(plot);
if (plot->lastUseToken() < target->tokenTracker()->nextTokenToFlush()) {
this->processEvictionAndResetRects(plot);
SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp());
SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, loc);
SkASSERT(verify);
if (!this->updatePlot(target, id, plot)) {
return ErrorCode::kError;
}
#ifdef SK_DEBUG_PAGE
SkDebugf("----- addToAtlas overwrite page# %{public}d.", pageIdx);
#endif
return ErrorCode::kSucceeded;
}
}
} else {
if (!this->activateNewPage(resourceProvider)) {
return ErrorCode::kError;
}
if (this->uploadToPage(fNumActivePages-1, id, target, width, height, image, loc)) {
return ErrorCode::kSucceeded;
} else {
return ErrorCode::kError;
}
}
if (!fNumActivePages) {
return ErrorCode::kError;
}
Plot* plot = nullptr;
for (int pageIdx = ((int)fNumActivePages)-1; pageIdx >= 0; --pageIdx) {
Plot* currentPlot = fPages[pageIdx].fPlotList.tail();
if (currentPlot->lastUseToken() != target->tokenTracker()->nextDrawToken()) {
plot = currentPlot;
break;
}
}
if (!plot) {
return ErrorCode::kTryAgain;
}
this->processEviction(plot->id());
int pageIdx = GetPageIndexFromID(plot->id());
fPages[pageIdx].fPlotList.remove(plot);
sk_sp<Plot>& newPlot = fPages[pageIdx].fPlotArray[plot->index()];
newPlot.reset(plot->clone());
fPages[pageIdx].fPlotList.addToHead(newPlot.get());
SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == newPlot->bpp());
SkDEBUGCODE(bool verify = )newPlot->addSubImage(width, height, image, loc);
SkASSERT(verify);
sk_sp<Plot> plotsp(SkRef(newPlot.get()));
GrTextureProxy* proxy = fProxies[pageIdx].get();
SkASSERT(proxy->isInstantiated());
GrDeferredUploadToken lastUploadToken = target->addInlineUpload(
[plotsp, proxy](GrDeferredTextureUploadWritePixelsFn& writePixels) {
plotsp->uploadToTexture(writePixels, proxy);
});
newPlot->setLastUploadToken(lastUploadToken);
*id = newPlot->id();
#ifdef SK_DEBUG_PAGE
SkDebugf("----- addToAtlas addInlineUpload page# %{public}d.", pageIdx);
#endif
return ErrorCode::kSucceeded;
}
void GrDrawOpAtlas::compactRadicals(GrDeferredUploadToken startTokenForNextFlush) {
if (fNumActivePages <= 1) {
return;
}
PlotList::Iter plotIter;
unsigned short usedAtlasLastFlush = 0;
for (uint32_t pageIndex = 0; pageIndex < fNumActivePages; ++pageIndex) {
plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
while (Plot* plot = plotIter.get()) {
if (plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
usedAtlasLastFlush |= (1 << pageIndex);
} else if (plot->lastUploadToken() != GrDeferredUploadToken::AlreadyFlushedToken()) {
this->processEvictionAndResetRects(plot);
}
plotIter.next();
}
}
int lastPageIndex = fNumActivePages - 1;
while (lastPageIndex > 0 && !(usedAtlasLastFlush & (1 << lastPageIndex))) {
deactivateLastPage();
lastPageIndex--;
}
}
void GrDrawOpAtlas::compact(GrDeferredUploadToken startTokenForNextFlush, bool isRadicals) {
if (isRadicals) {
compactRadicals(startTokenForNextFlush);
}
if (fNumActivePages <= 1) {
fPrevFlushToken = startTokenForNextFlush;
return;
}
int threshold = isRadicals ? 1 : fPlotOldThreshold;
PlotList::Iter plotIter;
bool atlasUsedThisFlush = false;
for (uint32_t pageIndex = 0; pageIndex < fNumActivePages; ++pageIndex) {
plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
while (Plot* plot = plotIter.get()) {
if (plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
plot->resetFlushesSinceLastUsed();
atlasUsedThisFlush = true;
}
plotIter.next();
}
}
if (atlasUsedThisFlush) {
SkTArray<Plot*> availablePlots;
uint32_t lastPageIndex = fNumActivePages - 1;
for (uint32_t pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex) {
#ifdef DUMP_ATLAS_DATA
if (gDumpAtlasData) {
SkDebugf("page %d: ", pageIndex);
}
#endif
plotIter.init(fPages[pageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
while (Plot* plot = plotIter.get()) {
if (!plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
plot->incFlushesSinceLastUsed();
}
#ifdef DUMP_ATLAS_DATA
if (gDumpAtlasData) {
SkDebugf("%d ", plot->flushesSinceLastUsed());
}
#endif
if (plot->flushesSinceLastUsed() > threshold) {
availablePlots.push_back() = plot;
}
plotIter.next();
}
#ifdef DUMP_ATLAS_DATA
if (gDumpAtlasData) {
SkDebugf("\n");
}
#endif
}
plotIter.init(fPages[lastPageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
unsigned int usedPlots = 0;
#ifdef DUMP_ATLAS_DATA
if (gDumpAtlasData) {
SkDebugf("page %d: ", lastPageIndex);
}
#endif
while (Plot* plot = plotIter.get()) {
if (!plot->lastUseToken().inInterval(fPrevFlushToken, startTokenForNextFlush)) {
plot->incFlushesSinceLastUsed();
}
#ifdef DUMP_ATLAS_DATA
if (gDumpAtlasData) {
SkDebugf("%d ", plot->flushesSinceLastUsed());
}
#endif
if (plot->flushesSinceLastUsed() <= threshold) {
usedPlots++;
} else if (plot->lastUseToken() != GrDeferredUploadToken::AlreadyFlushedToken()) {
this->processEvictionAndResetRects(plot);
}
plotIter.next();
}
#ifdef DUMP_ATLAS_DATA
if (gDumpAtlasData) {
SkDebugf("\n");
}
#endif
if (availablePlots.count() && usedPlots && usedPlots <= fNumPlots / 4) {
plotIter.init(fPages[lastPageIndex].fPlotList, PlotList::Iter::kHead_IterStart);
while (Plot* plot = plotIter.get()) {
if (plot->flushesSinceLastUsed() <= threshold) {
if (availablePlots.count() > 0) {
this->processEvictionAndResetRects(plot);
this->processEvictionAndResetRects(availablePlots.back());
availablePlots.pop_back();
--usedPlots;
}
if (!usedPlots || !availablePlots.count()) {
break;
}
}
plotIter.next();
}
}
if (!usedPlots) {
#ifdef DUMP_ATLAS_DATA
if (gDumpAtlasData) {
SkDebugf("delete %d\n", fNumPages-1);
}
#endif
#ifdef SK_DEBUG_PAGE
SkDebugf("----- availablePlots %{public}d.", availablePlots.count());
#endif
this->deactivateLastPage();
}
}
fPrevFlushToken = startTokenForNextFlush;
}
bool GrDrawOpAtlas::createPages(GrProxyProvider* proxyProvider) {
SkASSERT(SkIsPow2(fTextureWidth) && SkIsPow2(fTextureHeight));
GrSurfaceDesc desc;
desc.fWidth = fTextureWidth;
desc.fHeight = fTextureHeight;
desc.fConfig = GrColorTypeToPixelConfig(fColorType);
int numPlotsX = fTextureWidth/fPlotWidth;
int numPlotsY = fTextureHeight/fPlotHeight;
for (uint32_t i = 0; i < this->maxPages(); ++i) {
fProxies[i] = proxyProvider->createProxy(fFormat, desc, GrRenderable::kNo, 1,
kTopLeft_GrSurfaceOrigin, SkBackingFit::kExact,
SkBudgeted::kYes, GrProtected::kNo);
if (!fProxies[i]) {
return false;
}
fProxies[i]->priv().setIgnoredByResourceAllocator();
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(i, plotIndex, 1, x, y, fPlotWidth, fPlotHeight,
fColorType));
fPages[i].fPlotList.addToHead(currPlot->get());
++currPlot;
}
}
}
return true;
}
bool GrDrawOpAtlas::activateNewPage(GrResourceProvider* resourceProvider) {
SkASSERT(fNumActivePages < this->maxPages());
if (!fProxies[fNumActivePages]->instantiate(resourceProvider)) {
return false;
}
#ifdef DUMP_ATLAS_DATA
if (gDumpAtlasData) {
SkDebugf("activated page#: %d\n", fNumActivePages);
}
#endif
#ifdef SK_DEBUG_PAGE
SkDebugf("----- activated page#: %{public}d.", 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 r = 0; r < numPlotsY; ++r) {
for (int c = 0; c < numPlotsX; ++c) {
uint32_t plotIndex = r * numPlotsX + c;
Plot* currPlot = fPages[lastPageIndex].fPlotArray[plotIndex].get();
currPlot->resetRects();
currPlot->resetFlushesSinceLastUsed();
SkDEBUGCODE(currPlot->fPrev = currPlot->fNext = nullptr);
SkDEBUGCODE(currPlot->fList = nullptr);
fPages[lastPageIndex].fPlotList.addToHead(currPlot);
}
}
fProxies[lastPageIndex]->deinstantiate();
--fNumActivePages;
#ifdef SK_DEBUG_PAGE
SkDebugf("----- deactivateLastPage %{public}d.", lastPageIndex);
#endif
}
GrDrawOpAtlasConfig::GrDrawOpAtlasConfig(int maxTextureSize, size_t maxBytes) {
static const SkISize kARGBDimensions[] = {
{256, 256},
{512, 256},
{512, 512},
{1024, 512},
{1024, 1024},
{2048, 1024},
};
maxBytes >>= 18;
int index = maxBytes > 0
? SkTPin<int>(SkPrevLog2(maxBytes), 0, SK_ARRAY_COUNT(kARGBDimensions) - 1)
: 0;
SkASSERT(kARGBDimensions[index].width() <= kMaxAtlasDim);
SkASSERT(kARGBDimensions[index].height() <= kMaxAtlasDim);
fARGBDimensions.set(SkTMin<int>(kARGBDimensions[index].width(), maxTextureSize),
SkTMin<int>(kARGBDimensions[index].height(), maxTextureSize));
fMaxTextureSize = SkTMin<int>(maxTextureSize, kMaxAtlasDim);
}
int GrDrawOpAtlasConfig::resetAsSmallPage() {
size_t maxBytes = fARGBDimensions.width() * fARGBDimensions.height() * 4;
fARGBDimensions.set(512, 512);
return maxBytes / (fARGBDimensions.width() * fARGBDimensions.height());
}
SkISize GrDrawOpAtlasConfig::atlasDimensions(GrMaskFormat type) const {
if (kA8_GrMaskFormat == type) {
return { SkTMin<int>(2 * fARGBDimensions.width(), fMaxTextureSize),
SkTMin<int>(2 * fARGBDimensions.height(), fMaxTextureSize) };
} else {
return fARGBDimensions;
}
}
SkISize GrDrawOpAtlasConfig::plotDimensions(GrMaskFormat type) const {
if (kA8_GrMaskFormat == type) {
SkISize atlasDimensions = this->atlasDimensions(type);
int plotWidth = atlasDimensions.width() >= 1024 ? 512 : 256;
int plotHeight = atlasDimensions.height() >= 1024 ? 512 : 256;
return { plotWidth, plotHeight };
} else {
return { 256, 256 };
}
}
constexpr int GrDrawOpAtlasConfig::kMaxAtlasDim;