| Index: src/gpu/GrLayerAtlas.cpp
 | 
| diff --git a/src/gpu/GrLayerAtlas.cpp b/src/gpu/GrLayerAtlas.cpp
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..3b30607c8cfc72cea328bbea9eb3023b89842098
 | 
| --- /dev/null
 | 
| +++ b/src/gpu/GrLayerAtlas.cpp
 | 
| @@ -0,0 +1,145 @@
 | 
| +
 | 
| +/*
 | 
| + * 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 "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();
 | 
| +}
 | 
| +
 | 
| +///////////////////////////////////////////////////////////////////////////////
 | 
| +
 | 
| +GrLayerAtlas::GrLayerAtlas(GrTextureProvider* texProvider, GrPixelConfig config, 
 | 
| +                           GrSurfaceFlags flags,
 | 
| +                           const SkISize& backingTextureSize,
 | 
| +                           int numPlotsX, int numPlotsY) {
 | 
| +    fTexProvider = texProvider;
 | 
| +    fPixelConfig = config;
 | 
| +    fFlags = flags;
 | 
| +    fBackingTextureSize = backingTextureSize;
 | 
| +    fTexture = nullptr;
 | 
| +
 | 
| +    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;
 | 
| +        }
 | 
| +    }
 | 
| +}
 | 
| +
 | 
| +GrLayerAtlas::~GrLayerAtlas() {
 | 
| +    SkSafeUnref(fTexture);
 | 
| +    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) {
 | 
| +        // TODO: Update this to use the cache rather than directly creating a texture.
 | 
| +        GrSurfaceDesc desc;
 | 
| +        desc.fFlags = fFlags;
 | 
| +        desc.fWidth = fBackingTextureSize.width();
 | 
| +        desc.fHeight = fBackingTextureSize.height();
 | 
| +        desc.fConfig = fPixelConfig;
 | 
| +
 | 
| +        fTexture = fTexProvider->createTexture(desc, true, nullptr, 0);
 | 
| +        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;
 | 
| +}
 | 
| +
 | 
| 
 |