michael@0: michael@0: /* michael@0: * Copyright 2010 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "GrAtlas.h" michael@0: #include "GrContext.h" michael@0: #include "GrGpu.h" michael@0: #include "GrRectanizer.h" michael@0: michael@0: #if 0 michael@0: #define GR_PLOT_WIDTH 8 michael@0: #define GR_PLOT_HEIGHT 4 michael@0: #define GR_ATLAS_WIDTH 256 michael@0: #define GR_ATLAS_HEIGHT 256 michael@0: michael@0: #define GR_ATLAS_TEXTURE_WIDTH (GR_PLOT_WIDTH * GR_ATLAS_WIDTH) michael@0: #define GR_ATLAS_TEXTURE_HEIGHT (GR_PLOT_HEIGHT * GR_ATLAS_HEIGHT) michael@0: michael@0: #else michael@0: michael@0: #define GR_ATLAS_TEXTURE_WIDTH 1024 michael@0: #define GR_ATLAS_TEXTURE_HEIGHT 2048 michael@0: michael@0: #define GR_ATLAS_WIDTH 256 michael@0: #define GR_ATLAS_HEIGHT 256 michael@0: michael@0: #define GR_PLOT_WIDTH (GR_ATLAS_TEXTURE_WIDTH / GR_ATLAS_WIDTH) michael@0: #define GR_PLOT_HEIGHT (GR_ATLAS_TEXTURE_HEIGHT / GR_ATLAS_HEIGHT) michael@0: michael@0: #endif michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: // for testing michael@0: #define FONT_CACHE_STATS 0 michael@0: #if FONT_CACHE_STATS michael@0: static int g_UploadCount = 0; michael@0: #endif michael@0: michael@0: GrPlot::GrPlot() : fDrawToken(NULL, 0) michael@0: , fTexture(NULL) michael@0: , fAtlasMgr(NULL) michael@0: , fBytesPerPixel(1) michael@0: { michael@0: fRects = GrRectanizer::Factory(GR_ATLAS_WIDTH, michael@0: GR_ATLAS_HEIGHT); michael@0: fOffset.set(0, 0); michael@0: } michael@0: michael@0: GrPlot::~GrPlot() { michael@0: delete fRects; michael@0: } michael@0: michael@0: static inline void adjust_for_offset(GrIPoint16* loc, const GrIPoint16& offset) { michael@0: loc->fX += offset.fX * GR_ATLAS_WIDTH; michael@0: loc->fY += offset.fY * GR_ATLAS_HEIGHT; michael@0: } michael@0: michael@0: bool GrPlot::addSubImage(int width, int height, const void* image, michael@0: GrIPoint16* loc) { michael@0: if (!fRects->addRect(width, height, loc)) { michael@0: return false; michael@0: } michael@0: michael@0: SkAutoSMalloc<1024> storage; michael@0: adjust_for_offset(loc, fOffset); michael@0: GrContext* context = fTexture->getContext(); michael@0: // We pass the flag that does not force a flush. We assume our caller is michael@0: // smart and hasn't referenced the part of the texture we're about to update michael@0: // since the last flush. michael@0: context->writeTexturePixels(fTexture, michael@0: loc->fX, loc->fY, width, height, michael@0: fTexture->config(), image, 0, michael@0: GrContext::kDontFlush_PixelOpsFlag); michael@0: michael@0: #if FONT_CACHE_STATS michael@0: ++g_UploadCount; michael@0: #endif michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void GrPlot::resetRects() { michael@0: SkASSERT(NULL != fRects); michael@0: fRects->reset(); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GrAtlasMgr::GrAtlasMgr(GrGpu* gpu, GrPixelConfig config) { michael@0: fGpu = gpu; michael@0: fPixelConfig = config; michael@0: gpu->ref(); michael@0: fTexture = NULL; michael@0: michael@0: // set up allocated plots michael@0: size_t bpp = GrBytesPerPixel(fPixelConfig); michael@0: fPlotArray = SkNEW_ARRAY(GrPlot, (GR_PLOT_WIDTH*GR_PLOT_HEIGHT)); michael@0: michael@0: GrPlot* currPlot = fPlotArray; michael@0: for (int y = GR_PLOT_HEIGHT-1; y >= 0; --y) { michael@0: for (int x = GR_PLOT_WIDTH-1; x >= 0; --x) { michael@0: currPlot->fAtlasMgr = this; michael@0: currPlot->fOffset.set(x, y); michael@0: currPlot->fBytesPerPixel = bpp; michael@0: michael@0: // build LRU list michael@0: fPlotList.addToHead(currPlot); michael@0: ++currPlot; michael@0: } michael@0: } michael@0: } michael@0: michael@0: GrAtlasMgr::~GrAtlasMgr() { michael@0: SkSafeUnref(fTexture); michael@0: SkDELETE_ARRAY(fPlotArray); michael@0: michael@0: fGpu->unref(); michael@0: #if FONT_CACHE_STATS michael@0: GrPrintf("Num uploads: %d\n", g_UploadCount); michael@0: #endif michael@0: } michael@0: michael@0: void GrAtlasMgr::moveToHead(GrPlot* plot) { michael@0: if (fPlotList.head() == plot) { michael@0: return; michael@0: } michael@0: michael@0: fPlotList.remove(plot); michael@0: fPlotList.addToHead(plot); michael@0: }; michael@0: michael@0: GrPlot* GrAtlasMgr::addToAtlas(GrAtlas* atlas, michael@0: int width, int height, const void* image, michael@0: GrIPoint16* loc) { michael@0: // iterate through entire plot list for this atlas, see if we can find a hole michael@0: // last one was most recently added and probably most empty michael@0: for (int i = atlas->fPlots.count()-1; i >= 0; --i) { michael@0: GrPlot* plot = atlas->fPlots[i]; michael@0: if (plot->addSubImage(width, height, image, loc)) { michael@0: this->moveToHead(plot); michael@0: return plot; michael@0: } michael@0: } michael@0: michael@0: // before we get a new plot, make sure we have a backing texture michael@0: if (NULL == fTexture) { michael@0: // TODO: Update this to use the cache rather than directly creating a texture. michael@0: GrTextureDesc desc; michael@0: desc.fFlags = kDynamicUpdate_GrTextureFlagBit; michael@0: desc.fWidth = GR_ATLAS_TEXTURE_WIDTH; michael@0: desc.fHeight = GR_ATLAS_TEXTURE_HEIGHT; michael@0: desc.fConfig = fPixelConfig; michael@0: michael@0: fTexture = fGpu->createTexture(desc, NULL, 0); michael@0: if (NULL == fTexture) { michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: // now look through all allocated plots for one we can share, in MRU order michael@0: GrPlotList::Iter plotIter; michael@0: plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart); michael@0: GrPlot* plot; michael@0: while (NULL != (plot = plotIter.get())) { michael@0: // make sure texture is set for quick lookup michael@0: plot->fTexture = fTexture; michael@0: if (plot->addSubImage(width, height, image, loc)) { michael@0: this->moveToHead(plot); michael@0: // new plot for atlas, put at end of array michael@0: *(atlas->fPlots.append()) = plot; michael@0: return plot; michael@0: } michael@0: plotIter.next(); michael@0: } michael@0: michael@0: // If the above fails, then the current plot list has no room michael@0: return NULL; michael@0: } michael@0: michael@0: bool GrAtlasMgr::removePlot(GrAtlas* atlas, const GrPlot* plot) { michael@0: // iterate through plot list for this atlas michael@0: int count = atlas->fPlots.count(); michael@0: for (int i = 0; i < count; ++i) { michael@0: if (plot == atlas->fPlots[i]) { michael@0: atlas->fPlots.remove(i); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: // get a plot that's not being used by the current draw michael@0: GrPlot* GrAtlasMgr::getUnusedPlot() { michael@0: GrPlotList::Iter plotIter; michael@0: plotIter.init(fPlotList, GrPlotList::Iter::kTail_IterStart); michael@0: GrPlot* plot; michael@0: while (NULL != (plot = plotIter.get())) { michael@0: if (plot->drawToken().isIssued()) { michael@0: return plot; michael@0: } michael@0: plotIter.prev(); michael@0: } michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: SkISize GrAtlas::getSize() const { michael@0: return SkISize::Make(GR_ATLAS_TEXTURE_WIDTH, GR_ATLAS_TEXTURE_HEIGHT); michael@0: }