diff options
author | Jim Van Verth <jvanverth@google.com> | 2017-09-15 12:14:26 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-09-15 16:34:34 +0000 |
commit | 03168b8a62a0d3d14b7a0d14642df4d82203b87c (patch) | |
tree | 011df89efc1f6416ec08f6b1d6de173ff3000b1b | |
parent | a4083c97d48e8a4f88e2797d7363f141e3d42553 (diff) |
Allow GrDrawOpAtlas to grow as needed
Bug: skia:3550
Change-Id: Ib5312c8c06ba8549d90545658df6686c45058255
Reviewed-on: https://skia-review.googlesource.com/45841
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
-rw-r--r-- | src/gpu/GrDrawOpAtlas.cpp | 189 | ||||
-rw-r--r-- | src/gpu/GrDrawOpAtlas.h | 15 | ||||
-rw-r--r-- | src/gpu/GrGlyph.h | 1 | ||||
-rw-r--r-- | src/gpu/GrProcessor.cpp | 4 | ||||
-rw-r--r-- | src/gpu/GrProcessor.h | 1 | ||||
-rw-r--r-- | src/gpu/effects/GrAtlasedShaderHelpers.h | 14 | ||||
-rw-r--r-- | src/gpu/effects/GrBitmapTextGeoProc.cpp | 11 | ||||
-rw-r--r-- | src/gpu/effects/GrBitmapTextGeoProc.h | 2 | ||||
-rw-r--r-- | src/gpu/effects/GrDistanceFieldGeoProc.cpp | 33 | ||||
-rw-r--r-- | src/gpu/effects/GrDistanceFieldGeoProc.h | 6 | ||||
-rw-r--r-- | src/gpu/ops/GrAtlasTextOp.cpp | 35 | ||||
-rw-r--r-- | src/gpu/ops/GrSmallPathRenderer.cpp | 41 | ||||
-rw-r--r-- | src/gpu/text/GrAtlasGlyphCache.h | 9 | ||||
-rw-r--r-- | src/gpu/text/GrAtlasTextBlob_regenInOp.cpp | 10 |
14 files changed, 267 insertions, 104 deletions
diff --git a/src/gpu/GrDrawOpAtlas.cpp b/src/gpu/GrDrawOpAtlas.cpp index bdd07e7c82..22def06212 100644 --- a/src/gpu/GrDrawOpAtlas.cpp +++ b/src/gpu/GrDrawOpAtlas.cpp @@ -146,29 +146,8 @@ GrDrawOpAtlas::GrDrawOpAtlas(GrContext* context, GrPixelConfig config, int width , fPixelConfig(config) , fTextureWidth(width) , fTextureHeight(height) - , fAtlasGeneration(kInvalidAtlasGeneration + 1) { - - GrSurfaceDesc desc; - desc.fFlags = kNone_GrSurfaceFlags; - desc.fOrigin = kTopLeft_GrSurfaceOrigin; - desc.fWidth = fTextureWidth; - desc.fHeight = fTextureHeight; - desc.fConfig = fPixelConfig; - - // We don't want to flush the context so we claim we're in the middle of flushing so as to - // guarantee we do not recieve a texture with pending IO - // TODO: Determine how to avoid having to do this. (https://bug.skia.org/4156) - static const uint32_t kFlags = GrResourceProvider::kNoPendingIO_Flag; - sk_sp<GrTexture> texture(context->resourceProvider()->createApproxTexture(desc, kFlags)); - if (texture) { - // MDB TODO: for now, wrap an instantiated texture. Having the deferred instantiation - // possess the correct properties (e.g., no pendingIO) should fall out of the system but - // should receive special attention. - // Note: When switching over to the deferred proxy, use the kExact flag to create - // the atlas and assert that the width & height are powers of 2. - fProxies[0] = GrSurfaceProxy::MakeWrapped(std::move(texture), - kTopLeft_GrSurfaceOrigin); - } + , fAtlasGeneration(kInvalidAtlasGeneration + 1) + , fNumPages(0) { fPlotWidth = fTextureWidth / numPlotsX; fPlotHeight = fTextureHeight / numPlotsY; @@ -177,23 +156,8 @@ GrDrawOpAtlas::GrDrawOpAtlas(GrContext* context, GrPixelConfig config, int width SkASSERT(fPlotHeight * numPlotsY == fTextureHeight); SkDEBUGCODE(fNumPlots = numPlotsX * numPlotsY;) - SkDEBUGCODE(fNumPages = 1;) - - // set up allocated plots - fPages[0].fPlotArray.reset(new sk_sp<Plot>[ numPlotsX * numPlotsY ]); - sk_sp<Plot>* currPlot = fPages[0].fPlotArray.get(); - for (int y = numPlotsY - 1, r = 0; y >= 0; --y, ++r) { - for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) { - uint32_t index = r * numPlotsX + c; - currPlot->reset( - new Plot(0, index, 1, x, y, fPlotWidth, fPlotHeight, fPixelConfig)); - - // build LRU list - fPages[0].fPlotList.addToHead(currPlot->get()); - ++currPlot; - } - } + this->createNewPage(); } void GrDrawOpAtlas::processEviction(AtlasID id) { @@ -234,57 +198,83 @@ inline bool GrDrawOpAtlas::updatePlot(GrDrawOp::Target* target, AtlasID* id, Plo bool GrDrawOpAtlas::addToAtlas(AtlasID* id, GrDrawOp::Target* target, int width, int height, const void* image, SkIPoint16* loc) { - // Eventually we will iterate through these, for now just use the one. - int pageIdx = 0; - - // We should already have a texture, TODO clean this up - SkASSERT(fProxies[pageIdx]); if (width > fPlotWidth || height > fPlotHeight) { return false; } - // now look through all allocated plots for one we can share, in Most Recently Refed order - PlotList::Iter plotIter; - plotIter.init(fPages[pageIdx].fPlotList, PlotList::Iter::kHead_IterStart); - Plot* plot; - while ((plot = plotIter.get())) { - SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp()); - if (plot->addSubImage(width, height, image, loc)) { - return this->updatePlot(target, id, plot); + // Look through each page to see if we can upload without having to flush + // We prioritize this upload to the first pages, not the most recently used, to make it easier + // to remove unused pages in reverse page order. + for (unsigned int pageIdx = 0; pageIdx < fNumPages; ++pageIdx) { + SkASSERT(fProxies[pageIdx]); + // look through all allocated plots for one we can share, in Most Recently Refed order + PlotList::Iter plotIter; + plotIter.init(fPages[pageIdx].fPlotList, PlotList::Iter::kHead_IterStart); + Plot* plot; + while ((plot = plotIter.get())) { + SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp()); + if (plot->addSubImage(width, height, image, loc)) { + return this->updatePlot(target, id, plot); + } + plotIter.next(); + } + + // If the above fails, then see if the least recently refed plot has already been + // flushed to the gpu + plot = fPages[pageIdx].fPlotList.tail(); + SkASSERT(plot); + if (target->hasDrawBeenFlushed(plot->lastUseToken())) { + this->processEviction(plot->id()); + plot->resetRects(); + SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp()); + SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, loc); + SkASSERT(verify); + if (!this->updatePlot(target, id, plot)) { + return false; + } + + fAtlasGeneration++; + return true; } - plotIter.next(); } - // If the above fails, then see if the least recently refed plot has already been flushed to the - // gpu - plot = fPages[pageIdx].fPlotList.tail(); - SkASSERT(plot); - if (target->hasDrawBeenFlushed(plot->lastUseToken())) { - this->processEviction(plot->id()); - plot->resetRects(); + // If the simple cases fail, try to create a new page and add to it + if (this->createNewPage()) { + unsigned int pageIdx = fNumPages-1; + SkASSERT(fProxies[pageIdx]); + Plot* plot = fPages[pageIdx].fPlotList.head(); SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp()); - SkDEBUGCODE(bool verify = )plot->addSubImage(width, height, image, loc); - SkASSERT(verify); - if (!this->updatePlot(target, id, plot)) { - return false; + if (plot->addSubImage(width, height, image, loc)) { + return this->updatePlot(target, id, plot); } - fAtlasGeneration++; - return true; + // we shouldn't get here -- if so, something has gone terribly wrong + SkASSERT(false); + return false; } - // TODO: at this point try to create a new page and add to it before evicting + // Try to find a plot that we can perform an inline upload to. + // We prioritize this upload in reverse order of pages to counterbalance the order above. + Plot* plot = nullptr; + for (int pageIdx = (int)(fNumPages-1); pageIdx >= 0; --pageIdx) { + Plot* currentPlot = fPages[pageIdx].fPlotList.tail(); + if (currentPlot->lastUseToken() != target->nextDrawToken()) { + plot = currentPlot; + break; + } + } - // If this plot has been used in a draw that is currently being prepared by an op, then we have - // to fail. This gives the op a chance to enqueue the draw, and call back into this function. - // When that draw is enqueued, the draw token advances, and the subsequent call will continue - // past this branch and prepare an inline upload that will occur after the enqueued draw which - // references the plot's pre-upload content. - if (plot->lastUseToken() == target->nextDrawToken()) { + // If we can't find a plot that is not used in a draw currently being prepared by an op, then + // we have to fail. This gives the op a chance to enqueue the draw, and call back into this + // function. When that draw is enqueued, the draw token advances, and the subsequent call will + // continue past this branch and prepare an inline upload that will occur after the enqueued + //draw which references the plot's pre-upload content. + if (!plot) { return false; } this->processEviction(plot->id()); + int pageIdx = GetPageIndexFromID(plot->id()); fPages[pageIdx].fPlotList.remove(plot); sk_sp<Plot>& newPlot = fPages[pageIdx].fPlotArray[plot->index()]; newPlot.reset(plot->clone()); @@ -317,3 +307,56 @@ bool GrDrawOpAtlas::addToAtlas(AtlasID* id, GrDrawOp::Target* target, int width, fAtlasGeneration++; return true; } + +bool GrDrawOpAtlas::createNewPage() { + if (fNumPages == kMaxPages) { + return false; + } + + GrSurfaceDesc desc; + desc.fFlags = kNone_GrSurfaceFlags; + desc.fOrigin = kTopLeft_GrSurfaceOrigin; + desc.fWidth = fTextureWidth; + desc.fHeight = fTextureHeight; + desc.fConfig = fPixelConfig; + + // We don't want to flush the context so we claim we're in the middle of flushing so as to + // guarantee we do not recieve a texture with pending IO + // TODO: Determine how to avoid having to do this. (https://bug.skia.org/4156) + static const uint32_t kFlags = GrResourceProvider::kNoPendingIO_Flag; + sk_sp<GrTexture> texture(fContext->resourceProvider()->createApproxTexture(desc, kFlags)); + if (texture) { + // MDB TODO: for now, wrap an instantiated texture. Having the deferred instantiation + // possess the correct properties (e.g., no pendingIO) should fall out of the system but + // should receive special attention. + // Note: When switching over to the deferred proxy, use the kExact flag to create + // the atlas and assert that the width & height are powers of 2. + fProxies[fNumPages] = GrSurfaceProxy::MakeWrapped(std::move(texture), + kTopLeft_GrSurfaceOrigin); + } + if (!fProxies[fNumPages]) { + return false; + } + + int numPlotsX = fTextureWidth/fPlotWidth; + int numPlotsY = fTextureHeight/fPlotHeight; + + // set up allocated plots + fPages[fNumPages].fPlotArray.reset(new sk_sp<Plot>[ numPlotsX * numPlotsY ]); + + sk_sp<Plot>* currPlot = fPages[fNumPages].fPlotArray.get(); + for (int y = numPlotsY - 1, r = 0; y >= 0; --y, ++r) { + for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) { + uint32_t plotIndex = r * numPlotsX + c; + currPlot->reset(new Plot(fNumPages, plotIndex, 1, x, y, fPlotWidth, fPlotHeight, + fPixelConfig)); + + // build LRU list + fPages[fNumPages].fPlotList.addToHead(currPlot->get()); + ++currPlot; + } + } + + fNumPages++; + return true; +} diff --git a/src/gpu/GrDrawOpAtlas.h b/src/gpu/GrDrawOpAtlas.h index 6d12973f4a..87d9c5b619 100644 --- a/src/gpu/GrDrawOpAtlas.h +++ b/src/gpu/GrDrawOpAtlas.h @@ -120,6 +120,7 @@ public: } static constexpr auto kMaxPages = 4; + uint32_t pageCount() { return fNumPages; } /** * A class which can be handed back to GrDrawOpAtlas for updating last use tokens in bulk. The @@ -190,6 +191,10 @@ public: return width > kGlyphMaxDim || height > kGlyphMaxDim; } + static uint32_t GetPageIndexFromID(AtlasID id) { + return id & 0xff; + } + private: GrDrawOpAtlas(GrContext*, GrPixelConfig config, int width, int height, int numPlotsX, int numPlotsY); @@ -252,7 +257,7 @@ private: static GrDrawOpAtlas::AtlasID CreateId(uint32_t pageIdx, uint32_t plotIdx, uint64_t generation) { SkASSERT(pageIdx < (1 << 8)); - SkASSERT(pageIdx == 0); // for now, we only support one page + SkASSERT(pageIdx < kMaxPages); SkASSERT(plotIdx < (1 << 8)); SkASSERT(generation < ((uint64_t)1 << 48)); return generation << 16 | plotIdx << 8 | pageIdx; @@ -286,10 +291,6 @@ private: typedef SkTInternalLList<Plot> PlotList; - static uint32_t GetPageIndexFromID(AtlasID id) { - return id & 0xff; - } - static uint32_t GetPlotIndexFromID(AtlasID id) { return (id >> 8) & 0xff; } @@ -312,6 +313,8 @@ private: // TODO: make page MRU } + bool createNewPage(); + inline void processEviction(AtlasID); GrContext* fContext; @@ -340,7 +343,7 @@ private: // proxies kept separate to make it easier to pass them up to client sk_sp<GrTextureProxy> fProxies[kMaxPages]; Page fPages[kMaxPages]; - SkDEBUGCODE(uint32_t fNumPages;) + uint32_t fNumPages; }; #endif diff --git a/src/gpu/GrGlyph.h b/src/gpu/GrGlyph.h index 5005b74543..fbd389e4ab 100644 --- a/src/gpu/GrGlyph.h +++ b/src/gpu/GrGlyph.h @@ -61,6 +61,7 @@ struct GrGlyph { int height() const { return fBounds.height(); } bool isEmpty() const { return fBounds.isEmpty(); } uint16_t glyphID() const { return UnpackID(fPackedID); } + uint32_t pageIndex() const { return GrDrawOpAtlas::GetPageIndexFromID(fID); } /////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp index 7ea597221b..6650e208bc 100644 --- a/src/gpu/GrProcessor.cpp +++ b/src/gpu/GrProcessor.cpp @@ -137,6 +137,10 @@ void GrResourceIOProcessor::addImageStorageAccess(const ImageStorageAccess* acce fImageStorageAccesses.push_back(access); } +void GrResourceIOProcessor::resetTextureSamplers() { + fTextureSamplers.reset(); +} + void GrResourceIOProcessor::addPendingIOs() const { for (const auto& sampler : fTextureSamplers) { sampler->programProxy()->markPendingIO(); diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h index de0598fed9..54aae35401 100644 --- a/src/gpu/GrProcessor.h +++ b/src/gpu/GrProcessor.h @@ -191,6 +191,7 @@ protected: void addTextureSampler(const TextureSampler*); void addBufferAccess(const BufferAccess*); void addImageStorageAccess(const ImageStorageAccess*); + void resetTextureSamplers(); bool hasSameSamplersAndAccesses(const GrResourceIOProcessor&) const; diff --git a/src/gpu/effects/GrAtlasedShaderHelpers.h b/src/gpu/effects/GrAtlasedShaderHelpers.h index 67f2c242c2..50ac0efff4 100644 --- a/src/gpu/effects/GrAtlasedShaderHelpers.h +++ b/src/gpu/effects/GrAtlasedShaderHelpers.h @@ -47,17 +47,15 @@ static void append_multitexture_lookup(GrGLSLPrimitiveProcessor::EmitArgs& args, const char* coordName, const char* colorName) { // conditionally load from the indexed texture sampler - if (numTextureSamplers > 1) { - args.fFragBuilder->codeAppendf("if (%s == 0) ", texIdx.fsIn()); + for (int i = 0; i < numTextureSamplers-1; ++i) { + args.fFragBuilder->codeAppendf("if (%s == %d) { %s = ", texIdx.fsIn(), i, colorName); + args.fFragBuilder->appendTextureLookup(args.fTexSamplers[i], coordName, kVec2f_GrSLType); + args.fFragBuilder->codeAppend("; } else "); } args.fFragBuilder->codeAppendf("{ %s = ", colorName); - args.fFragBuilder->appendTextureLookup(args.fTexSamplers[0], coordName, kVec2f_GrSLType); + args.fFragBuilder->appendTextureLookup(args.fTexSamplers[numTextureSamplers-1], coordName, + kVec2f_GrSLType); args.fFragBuilder->codeAppend("; }"); - for (int i = 1; i < numTextureSamplers; ++i) { - args.fFragBuilder->codeAppendf("else if (%s == %d) { %s =", texIdx.fsIn(), i, colorName); - args.fFragBuilder->appendTextureLookup(args.fTexSamplers[i], coordName, kVec2f_GrSLType); - args.fFragBuilder->codeAppend("; }"); - } } #endif diff --git a/src/gpu/effects/GrBitmapTextGeoProc.cpp b/src/gpu/effects/GrBitmapTextGeoProc.cpp index 2d665c50cb..4df5cdf4a4 100644 --- a/src/gpu/effects/GrBitmapTextGeoProc.cpp +++ b/src/gpu/effects/GrBitmapTextGeoProc.cpp @@ -149,6 +149,17 @@ GrBitmapTextGeoProc::GrBitmapTextGeoProc(GrColor color, } } +void GrBitmapTextGeoProc::resetProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], + const GrSamplerState& params) { + this->resetTextureSamplers(); + for (int i = 0; i < kMaxTextures; ++i) { + if (proxies[i]) { + fTextureSamplers[i].reset(std::move(proxies[i]), params); + this->addTextureSampler(&fTextureSamplers[i]); + } + } +} + void GrBitmapTextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const { GrGLBitmapTextGeoProc::GenKey(*this, caps, b); diff --git a/src/gpu/effects/GrBitmapTextGeoProc.h b/src/gpu/effects/GrBitmapTextGeoProc.h index f8f4d8e5a8..75ad021445 100644 --- a/src/gpu/effects/GrBitmapTextGeoProc.h +++ b/src/gpu/effects/GrBitmapTextGeoProc.h @@ -45,6 +45,8 @@ public: const SkMatrix& localMatrix() const { return fLocalMatrix; } bool usesLocalCoords() const { return fUsesLocalCoords; } + void resetProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], const GrSamplerState& p); + void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override; GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override; diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.cpp b/src/gpu/effects/GrDistanceFieldGeoProc.cpp index 66e96199eb..1758f34b2f 100644 --- a/src/gpu/effects/GrDistanceFieldGeoProc.cpp +++ b/src/gpu/effects/GrDistanceFieldGeoProc.cpp @@ -261,6 +261,17 @@ GrDistanceFieldA8TextGeoProc::GrDistanceFieldA8TextGeoProc( } } +void GrDistanceFieldA8TextGeoProc::resetProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], + const GrSamplerState& params) { + this->resetTextureSamplers(); + for (int i = 0; i < kMaxTextures; ++i) { + if (proxies[i]) { + fTextureSamplers[i].reset(std::move(proxies[i]), params); + this->addTextureSampler(&fTextureSamplers[i]); + } + } +} + void GrDistanceFieldA8TextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const { GrGLDistanceFieldA8TextGeoProc::GenKey(*this, caps, b); @@ -511,6 +522,17 @@ GrDistanceFieldPathGeoProc::GrDistanceFieldPathGeoProc( } } +void GrDistanceFieldPathGeoProc::resetProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], + const GrSamplerState& params) { + this->resetTextureSamplers(); + for (int i = 0; i < kMaxTextures; ++i) { + if (proxies[i]) { + fTextureSamplers[i].reset(std::move(proxies[i]), params); + this->addTextureSampler(&fTextureSamplers[i]); + } + } +} + void GrDistanceFieldPathGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const { GrGLDistanceFieldPathGeoProc::GenKey(*this, caps, b); @@ -823,6 +845,17 @@ GrDistanceFieldLCDTextGeoProc::GrDistanceFieldLCDTextGeoProc( } } +void GrDistanceFieldLCDTextGeoProc::resetProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], + const GrSamplerState& params) { + this->resetTextureSamplers(); + for (int i = 0; i < kMaxTextures; ++i) { + if (proxies[i]) { + fTextureSamplers[i].reset(std::move(proxies[i]), params); + this->addTextureSampler(&fTextureSamplers[i]); + } + } +} + void GrDistanceFieldLCDTextGeoProc::getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const { GrGLDistanceFieldLCDTextGeoProc::GenKey(*this, caps, b); diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.h b/src/gpu/effects/GrDistanceFieldGeoProc.h index b5c6f9eea6..e7c02a5f59 100644 --- a/src/gpu/effects/GrDistanceFieldGeoProc.h +++ b/src/gpu/effects/GrDistanceFieldGeoProc.h @@ -87,6 +87,8 @@ public: #endif uint32_t getFlags() const { return fFlags; } + void resetProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], const GrSamplerState& p); + void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override; GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override; @@ -148,6 +150,8 @@ public: uint32_t getFlags() const { return fFlags; } bool usesLocalCoords() const { return fUsesLocalCoords; } + void resetProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], const GrSamplerState& p); + void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override; GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override; @@ -222,6 +226,8 @@ public: uint32_t getFlags() const { return fFlags; } bool usesLocalCoords() const { return fUsesLocalCoords; } + void resetProxies(const sk_sp<GrTextureProxy> proxies[kMaxTextures], const GrSamplerState& p); + void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override; GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override; diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp index c6ceaac005..44100cba0d 100644 --- a/src/gpu/ops/GrAtlasTextOp.cpp +++ b/src/gpu/ops/GrAtlasTextOp.cpp @@ -84,25 +84,26 @@ void GrAtlasTextOp::onPrepareDraws(Target* target) { return; } - const sk_sp<GrTextureProxy>* proxies = fFontCache->getProxies(this->maskFormat()); - if (!proxies[0]) { + GrMaskFormat maskFormat = this->maskFormat(); + + uint32_t atlasPageCount = fFontCache->getAtlasPageCount(maskFormat); + const sk_sp<GrTextureProxy>* proxies = fFontCache->getProxies(maskFormat); + if (!atlasPageCount || !proxies[0]) { SkDebugf("Could not allocate backing texture for atlas\n"); return; } - GrMaskFormat maskFormat = this->maskFormat(); - FlushInfo flushInfo; flushInfo.fPipeline = target->makePipeline(fSRGBFlags, std::move(fProcessors), target->detachAppliedClip()); if (this->usesDistanceFields()) { flushInfo.fGeometryProcessor = - this->setupDfProcessor(this->viewMatrix(), - fLuminanceColor, this->color(), proxies); + this->setupDfProcessor(this->viewMatrix(), + fLuminanceColor, this->color(), proxies); } else { flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make( - this->color(), proxies, GrSamplerState::ClampNearest(), maskFormat, - localMatrix, this->usesLocalCoords()); + this->color(), proxies, GrSamplerState::ClampNearest(), maskFormat, + localMatrix, this->usesLocalCoords()); } flushInfo.fGlyphsToFlush = 0; @@ -141,6 +142,24 @@ void GrAtlasTextOp::onPrepareDraws(Target* target) { currVertex += byteCount; } + // During preparation the number of atlas pages has increased. + // Update the proxies used in the GP to match. + if (atlasPageCount != fFontCache->getAtlasPageCount(maskFormat)) { + GrGeometryProcessor* gp = flushInfo.fGeometryProcessor.get(); + if (this->usesDistanceFields()) { + if (this->isLCD()) { + reinterpret_cast<GrDistanceFieldLCDTextGeoProc*>(gp)->resetProxies( + fFontCache->getProxies(maskFormat), GrSamplerState::ClampBilerp()); + } else { + reinterpret_cast<GrDistanceFieldA8TextGeoProc*>(gp)->resetProxies( + fFontCache->getProxies(maskFormat), GrSamplerState::ClampBilerp()); + } + } else { + reinterpret_cast<GrBitmapTextGeoProc*>(gp)->resetProxies( + fFontCache->getProxies(maskFormat), GrSamplerState::ClampNearest()); + } + } + this->flush(target, &flushInfo); } diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp index 6a037a6540..79d0061a82 100644 --- a/src/gpu/ops/GrSmallPathRenderer.cpp +++ b/src/gpu/ops/GrSmallPathRenderer.cpp @@ -238,6 +238,10 @@ private: flushInfo.fPipeline = fHelper.makePipeline(target); // Setup GrGeometryProcessor GrDrawOpAtlas* atlas = fAtlas; + uint32_t atlasPageCount = atlas->pageCount(); + if (!atlasPageCount) { + return; + } if (fUsesDistanceField) { uint32_t flags = 0; flags |= ctm.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0; @@ -385,6 +389,19 @@ private: flushInfo.fInstancesToFlush++; } + // During preparation the number of atlas pages has increased. + // Update the proxies used in the GP to match. + if (atlasPageCount != atlas->pageCount()) { + GrGeometryProcessor* gp = flushInfo.fGeometryProcessor.get(); + if (fUsesDistanceField) { + reinterpret_cast<GrDistanceFieldPathGeoProc*>(gp)->resetProxies( + atlas->getProxies(), GrSamplerState::ClampBilerp()); + } else { + reinterpret_cast<GrBitmapTextGeoProc*>(gp)->resetProxies( + atlas->getProxies(), GrSamplerState::ClampNearest()); + } + } + this->flush(target, &flushInfo); } @@ -496,12 +513,17 @@ private: shapeData->fBounds.fRight /= scale; shapeData->fBounds.fBottom /= scale; - shapeData->fTextureCoords.set((atlasLocation.fX+SK_DistanceFieldPad) << 1, - (atlasLocation.fY+SK_DistanceFieldPad) << 1, + // We pack the 2bit page index in the low bit of the u and v texture coords + uint16_t pageIndex = GrDrawOpAtlas::GetPageIndexFromID(id); + SkASSERT(pageIndex < 4); + uint16_t uBit = (pageIndex >> 1) & 0x1; + uint16_t vBit = pageIndex & 0x1; + shapeData->fTextureCoords.set((atlasLocation.fX+SK_DistanceFieldPad) << 1 | uBit, + (atlasLocation.fY+SK_DistanceFieldPad) << 1 | vBit, (atlasLocation.fX+SK_DistanceFieldPad+ - devPathBounds.width()) << 1, + devPathBounds.width()) << 1 | uBit, (atlasLocation.fY+SK_DistanceFieldPad+ - devPathBounds.height()) << 1); + devPathBounds.height()) << 1 | vBit); fShapeCache->add(shapeData); fShapeList->addToTail(shapeData); @@ -589,9 +611,14 @@ private: shapeData->fBounds = SkRect::Make(devPathBounds); shapeData->fBounds.offset(-translateX, -translateY); - shapeData->fTextureCoords.set(atlasLocation.fX << 1, atlasLocation.fY << 1, - (atlasLocation.fX+width) << 1, - (atlasLocation.fY+height) << 1); + // We pack the 2bit page index in the low bit of the u and v texture coords + uint16_t pageIndex = GrDrawOpAtlas::GetPageIndexFromID(id); + SkASSERT(pageIndex < 4); + uint16_t uBit = (pageIndex >> 1) & 0x1; + uint16_t vBit = pageIndex & 0x1; + shapeData->fTextureCoords.set(atlasLocation.fX << 1 | uBit, atlasLocation.fY << 1 | vBit, + (atlasLocation.fX+width) << 1 | uBit, + (atlasLocation.fY+height) << 1 | vBit); fShapeCache->add(shapeData); fShapeList->addToTail(shapeData); diff --git a/src/gpu/text/GrAtlasGlyphCache.h b/src/gpu/text/GrAtlasGlyphCache.h index 81876a01d9..78b743e2f4 100644 --- a/src/gpu/text/GrAtlasGlyphCache.h +++ b/src/gpu/text/GrAtlasGlyphCache.h @@ -126,7 +126,7 @@ public: void freeAll(); - // if getProxy returns nullptr, the client must not try to use other functions on the + // if getProxies returns nullptr, the client must not try to use other functions on the // GrAtlasGlyphCache which use the atlas. This function *must* be called first, before other // functions which use the atlas. const sk_sp<GrTextureProxy>* getProxies(GrMaskFormat format) { @@ -136,6 +136,13 @@ public: return nullptr; } + uint32_t getAtlasPageCount(GrMaskFormat format) { + if (this->initAtlas(format)) { + return this->getAtlas(format)->pageCount(); + } + return 0; + } + bool hasGlyph(GrGlyph* glyph) { SkASSERT(glyph); return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID); diff --git a/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp b/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp index b7911c3eae..778c9faabf 100644 --- a/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp +++ b/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp @@ -39,11 +39,19 @@ inline void regen_vertices(intptr_t vertex, const GrGlyph* glyph, size_t vertexS u1 = u0 + width; v1 = v0 + height; } - // shift to make space for index bits + // We pack the 2bit page index in the low bit of the u and v texture coords + uint32_t pageIndex = glyph->pageIndex(); + SkASSERT(pageIndex < 4); + uint16_t uBit = (pageIndex >> 1) & 0x1; + uint16_t vBit = pageIndex & 0x1; u0 <<= 1; + u0 |= uBit; v0 <<= 1; + v0 |= vBit; u1 <<= 1; + u1 |= uBit; v1 <<= 1; + v1 |= vBit; } // This is a bit wonky, but sometimes we have LCD text, in which case we won't have color |