/*
* Copyright 2010 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrGpuResourcePriv.h"
#include "GrLayerAtlas.h"
#include "GrRectanizer.h"
#include "GrTextureProvider.h"
///////////////////////////////////////////////////////////////////////////////
GrLayerAtlas::Plot::Plot()
: fID(-1)
, fRects(nullptr) {
fOffset.set(0, 0);
}
GrLayerAtlas::Plot::~Plot() {
delete fRects;
}
void GrLayerAtlas::Plot::init(int id, int offX, int offY, int width, int height) {
fID = id;
fRects = GrRectanizer::Factory(width, height);
fOffset.set(offX * width, offY * height);
}
bool GrLayerAtlas::Plot::allocateRect(int width, int height, SkIPoint16* loc) {
if (!fRects->addRect(width, height, loc)) {
return false;
}
loc->fX += fOffset.fX;
loc->fY += fOffset.fY;
return true;
}
void GrLayerAtlas::Plot::reset() {
SkASSERT(fRects);
fRects->reset();
}
///////////////////////////////////////////////////////////////////////////////
GR_DECLARE_STATIC_UNIQUE_KEY(gLayerAtlasKey);
static const GrUniqueKey& get_layer_atlas_key() {
GR_DEFINE_STATIC_UNIQUE_KEY(gLayerAtlasKey);
return gLayerAtlasKey;
}
bool GrLayerAtlas::reattachBackingTexture() {
SkASSERT(!fTexture);
fTexture.reset(fTexProvider->findAndRefTextureByUniqueKey(get_layer_atlas_key()));
return SkToBool(fTexture);
}
void GrLayerAtlas::createBackingTexture() {
SkASSERT(!fTexture);
GrSurfaceDesc desc;
desc.fFlags = fFlags;
desc.fWidth = fBackingTextureSize.width();
desc.fHeight = fBackingTextureSize.height();
desc.fConfig = fPixelConfig;
fTexture.reset(fTexProvider->createTexture(desc, SkBudgeted::kYes, nullptr, 0));
fTexture->resourcePriv().setUniqueKey(get_layer_atlas_key());
}
GrLayerAtlas::GrLayerAtlas(GrTextureProvider* texProvider, GrPixelConfig config,
GrSurfaceFlags flags,
const SkISize& backingTextureSize,
int numPlotsX, int numPlotsY) {
fTexProvider = texProvider;
fPixelConfig = config;
fFlags = flags;
fBackingTextureSize = backingTextureSize;
int textureWidth = fBackingTextureSize.width();
int textureHeight = fBackingTextureSize.height();
int plotWidth = textureWidth / numPlotsX;
int plotHeight = textureHeight / numPlotsY;
SkASSERT(plotWidth * numPlotsX == textureWidth);
SkASSERT(plotHeight * numPlotsY == textureHeight);
// We currently do not support compressed atlases...
SkASSERT(!GrPixelConfigIsCompressed(config));
// set up allocated plots
fPlotArray = new Plot[numPlotsX * numPlotsY];
Plot* currPlot = fPlotArray;
for (int y = numPlotsY-1; y >= 0; --y) {
for (int x = numPlotsX-1; x >= 0; --x) {
currPlot->init(y*numPlotsX+x, x, y, plotWidth, plotHeight);
// build LRU list
fPlotList.addToHead(currPlot);
++currPlot;
}
}
}
void GrLayerAtlas::resetPlots() {
PlotIter iter;
for (Plot* plot = iter.init(fPlotList, PlotIter::kHead_IterStart); plot; plot = iter.next()) {
plot->reset();
}
}
GrLayerAtlas::~GrLayerAtlas() {
delete[] fPlotArray;
}
void GrLayerAtlas::makeMRU(Plot* plot) {
if (fPlotList.head() == plot) {
return;
}
fPlotList.remove(plot);
fPlotList.addToHead(plot);
};
GrLayerAtlas::Plot* GrLayerAtlas::addToAtlas(ClientPlotUsage* usage,
int width, int height, SkIPoint16* loc) {
// Iterate through the plots currently being used by this client and see if we can find a hole.
// The last one was most recently added and probably most empty.
// We want to consolidate the uses from individual clients to the same plot(s) so that
// when a specific client goes away they are more likely to completely empty a plot.
for (int i = usage->numPlots()-1; i >= 0; --i) {
Plot* plot = usage->plot(i);
if (plot->allocateRect(width, height, loc)) {
this->makeMRU(plot);
return plot;
}
}
// before we get a new plot, make sure we have a backing texture
if (nullptr == fTexture) {
this->createBackingTexture();
if (nullptr == fTexture) {
return nullptr;
}
}
// Now look through all allocated plots for one we can share, in MRU order
// TODO: its seems like traversing from emptiest to fullest would make more sense
PlotList::Iter plotIter;
plotIter.init(fPlotList, PlotList::Iter::kHead_IterStart);
Plot* plot;
while ((plot = plotIter.get())) {
if (plot->allocateRect(width, height, loc)) {
this->makeMRU(plot);
// new plot for atlas, put at end of array
usage->appendPlot(plot);
return plot;
}
plotIter.next();
}
// If the above fails, then the current plot list has no room
return nullptr;
}