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