/* * 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 fTexture != nullptr; } 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; }