Modify fontcache GM to actually spill atlas.
Adds an option to GrDrawOpAtlas to disable multitexturing.
Adds option to GrContextOptions to disable multitexturing for glyph atlases.
Change-Id: If413ab7061538fa0e75628d252be4fd14215b6ba
Reviewed-on: https://skia-review.googlesource.com/67802
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/gm/fontcache.cpp b/gm/fontcache.cpp
index a8c5b76..b93cc3f 100644
--- a/gm/fontcache.cpp
+++ b/gm/fontcache.cpp
@@ -5,11 +5,14 @@
* found in the LICENSE file.
*/
-#include "gm.h"
-#include "sk_tool_utils.h"
+#include "GrContext.h"
+#include "GrContextOptions.h"
#include "SkCanvas.h"
#include "SkGraphics.h"
+#include "SkImage.h"
#include "SkTypeface.h"
+#include "gm.h"
+#include "sk_tool_utils.h"
// GM to stress the GPU font cache
@@ -21,58 +24,94 @@
class FontCacheGM : public skiagm::GM {
public:
- FontCacheGM() {}
+ FontCacheGM() { this->setBGColor(SK_ColorLTGRAY); }
+
+ void modifyGrContextOptions(GrContextOptions* options) override {
+ options->fGlyphCacheTextureMaximumBytes = 0;
+ options->fAllowMultipleGlyphCacheTextures = GrContextOptions::Enable::kNo;
+ }
protected:
SkString onShortName() override {
return SkString("fontcache");
}
- SkISize onISize() override {
- return SkISize::Make(1280, 640);
- }
+ SkISize onISize() override { return SkISize::Make(kSize, kSize); }
void onOnceBeforeDraw() override {
fTypefaces[0] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Italic());
fTypefaces[1] = sk_tool_utils::create_portable_typeface("sans-serif",SkFontStyle::Italic());
+ fTypefaces[2] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Normal());
+ fTypefaces[3] =
+ sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle::Normal());
+ fTypefaces[4] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Bold());
+ fTypefaces[5] = sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle::Bold());
}
void onDraw(SkCanvas* canvas) override {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setLCDRenderText(true);
- paint.setSubpixelText(true);
- paint.setTypeface(fTypefaces[0]);
- paint.setTextSize(192);
-
- // Make sure the nul character does not cause problems.
- paint.measureText("\0", 1);
-
- SkScalar x = 20;
- SkScalar y = 128;
- SkString text("ABCDEFGHIJ");
- draw_string(canvas, text, x, y, paint);
- y += 100;
- SkString text2("KLMNOPQRS");
- draw_string(canvas, text2, x, y, paint);
- y += 100;
- SkString text3("TUVWXYZ012");
- draw_string(canvas, text3, x, y, paint);
- y += 100;
- paint.setTypeface(fTypefaces[1]);
- draw_string(canvas, text, x, y, paint);
- y += 100;
- draw_string(canvas, text2, x, y, paint);
- y += 100;
- draw_string(canvas, text3, x, y, paint);
- y += 100;
+ canvas->clear(SK_ColorLTGRAY);
+ this->drawText(canvas);
+ // Debugging tool for GPU.
+ static const bool kShowAtlas = false;
+ if (kShowAtlas) {
+ if (auto ctx = canvas->getGrContext()) {
+ auto img = ctx->getFontAtlasImage_ForTesting(kA8_GrMaskFormat);
+ canvas->drawImage(img, 0, 0);
+ }
+ }
}
private:
- sk_sp<SkTypeface> fTypefaces[2];
+ void drawText(SkCanvas* canvas) {
+ static const int kSizes[] = {8, 9, 10, 11, 12, 13, 18, 20, 25};
+
+ static const SkString kTexts[] = {SkString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
+ SkString("abcdefghijklmnopqrstuvwxyz"),
+ SkString("0123456789"),
+ SkString("!@#$%^&*()<>[]{}")};
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setLCDRenderText(false);
+ paint.setSubpixelText(true);
+
+ static const SkScalar kSubPixelInc = 1 / 2.f;
+ SkScalar x = 0;
+ SkScalar y = 10;
+ SkScalar subpixelX = 0;
+ SkScalar subpixelY = 0;
+ bool offsetX = true;
+
+ do {
+ for (auto s : kSizes) {
+ auto size = 2 * s;
+ paint.setTextSize(size);
+ for (const auto& typeface : fTypefaces) {
+ paint.setTypeface(typeface);
+ for (const auto& text : kTexts) {
+ x = size + draw_string(canvas, text, x + subpixelX, y + subpixelY, paint);
+ x = SkScalarCeilToScalar(x);
+ if (x + 100 > kSize) {
+ x = 0;
+ y += SkScalarCeilToScalar(size + 3);
+ if (y > kSize) {
+ return;
+ }
+ }
+ }
+ }
+ (offsetX ? subpixelX : subpixelY) += kSubPixelInc;
+ offsetX = !offsetX;
+ }
+ } while (true);
+ }
+
+ static constexpr SkScalar kSize = 1280;
+
+ sk_sp<SkTypeface> fTypefaces[6];
typedef GM INHERITED;
};
+constexpr SkScalar FontCacheGM::kSize;
//////////////////////////////////////////////////////////////////////////////
diff --git a/include/gpu/GrContextOptions.h b/include/gpu/GrContextOptions.h
index 7aa85e0..a3c914a 100644
--- a/include/gpu/GrContextOptions.h
+++ b/include/gpu/GrContextOptions.h
@@ -114,6 +114,12 @@
float fGlyphCacheTextureMaximumBytes = 2048 * 1024 * 4;
/**
+ * Can the glyph atlas use multiple textures. If allowed, the each texture's size is bound by
+ * fGlypheCacheTextureMaximumBytes.
+ */
+ Enable fAllowMultipleGlyphCacheTextures = Enable::kDefault;
+
+ /**
* Bugs on certain drivers cause stencil buffers to leak. This flag causes Skia to avoid
* allocating stencil buffers and use alternate rasterization paths, avoiding the leak.
*/
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 2146858..d3194be 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -204,7 +204,14 @@
}
fDrawingManager.reset(new GrDrawingManager(this, prcOptions, &fSingleOwner));
- fAtlasGlyphCache = new GrAtlasGlyphCache(this, options.fGlyphCacheTextureMaximumBytes);
+ GrDrawOpAtlas::AllowMultitexturing allowMultitexturing;
+ if (options.fAllowMultipleGlyphCacheTextures == GrContextOptions::Enable::kNo) {
+ allowMultitexturing = GrDrawOpAtlas::AllowMultitexturing::kNo;
+ } else {
+ allowMultitexturing = GrDrawOpAtlas::AllowMultitexturing::kYes;
+ }
+ fAtlasGlyphCache = new GrAtlasGlyphCache(this, options.fGlyphCacheTextureMaximumBytes,
+ allowMultitexturing);
this->contextPriv().addOnFlushCallbackObject(fAtlasGlyphCache);
fTextBlobCache.reset(new GrTextBlobCache(TextBlobCacheOverBudgetCB, this));
diff --git a/src/gpu/GrDrawOpAtlas.cpp b/src/gpu/GrDrawOpAtlas.cpp
index 8d81285..c0358b7 100644
--- a/src/gpu/GrDrawOpAtlas.cpp
+++ b/src/gpu/GrDrawOpAtlas.cpp
@@ -14,13 +14,12 @@
#include "GrTexture.h"
#include "GrTracing.h"
-std::unique_ptr<GrDrawOpAtlas> GrDrawOpAtlas::Make(GrContext* ctx, GrPixelConfig config,
- int width, int height,
- int numPlotsX, int numPlotsY,
- GrDrawOpAtlas::EvictionFunc func,
- void* data) {
- std::unique_ptr<GrDrawOpAtlas> atlas(
- new GrDrawOpAtlas(ctx, config, width, height, numPlotsX, numPlotsY));
+std::unique_ptr<GrDrawOpAtlas> GrDrawOpAtlas::Make(GrContext* ctx, GrPixelConfig config, int width,
+ int height, int numPlotsX, int numPlotsY,
+ AllowMultitexturing allowMultitexturing,
+ GrDrawOpAtlas::EvictionFunc func, void* data) {
+ std::unique_ptr<GrDrawOpAtlas> atlas(new GrDrawOpAtlas(ctx, config, width, height, numPlotsX,
+ numPlotsY, allowMultitexturing));
if (!atlas->getProxies()[0]) {
return nullptr;
}
@@ -147,13 +146,14 @@
///////////////////////////////////////////////////////////////////////////////
GrDrawOpAtlas::GrDrawOpAtlas(GrContext* context, GrPixelConfig config, int width, int height,
- int numPlotsX, int numPlotsY)
+ int numPlotsX, int numPlotsY, AllowMultitexturing allowMultitexturing)
: fContext(context)
, fPixelConfig(config)
, fTextureWidth(width)
, fTextureHeight(height)
, fAtlasGeneration(kInvalidAtlasGeneration + 1)
, fPrevFlushToken(GrDeferredUploadToken::AlreadyFlushedToken())
+ , fAllowMultitexturing(allowMultitexturing)
, fNumPages(0) {
fPlotWidth = fTextureWidth / numPlotsX;
fPlotHeight = fTextureHeight / numPlotsY;
@@ -242,7 +242,7 @@
for (unsigned int pageIdx = 0; pageIdx < fNumPages; ++pageIdx) {
Plot* plot = fPages[pageIdx].fPlotList.tail();
SkASSERT(plot);
- if ((fNumPages == kMaxPages && plot->lastUseToken() < target->nextTokenToFlush()) ||
+ if ((fNumPages == this->maxPages() && plot->lastUseToken() < target->nextTokenToFlush()) ||
plot->flushesSinceLastUsed() >= kRecentlyUsedCount) {
this->processEvictionAndResetRects(plot);
SkASSERT(GrBytesPerPixel(fProxies[pageIdx]->config()) == plot->bpp());
@@ -448,7 +448,7 @@
}
bool GrDrawOpAtlas::createNewPage() {
- if (fNumPages == kMaxPages) {
+ if (fNumPages == this->maxPages()) {
return false;
}
diff --git a/src/gpu/GrDrawOpAtlas.h b/src/gpu/GrDrawOpAtlas.h
index 91a88ec..edab2cb 100644
--- a/src/gpu/GrDrawOpAtlas.h
+++ b/src/gpu/GrDrawOpAtlas.h
@@ -51,7 +51,13 @@
* and passes in the given GrDrawUploadToken.
*/
class GrDrawOpAtlas {
+private:
+ static constexpr auto kMaxMultitexturePages = 4;
+
public:
+ /** Is the atlas allowed to use more than one texture? */
+ enum class AllowMultitexturing : bool { kNo, kYes };
+
/**
* An AtlasID is an opaque handle which callers can use to determine if the atlas contains
* a specific piece of data.
@@ -77,15 +83,16 @@
* direction
* @param numPlotsY The number of plots the atlas should be broken up into in the Y
* direction
+ * @param allowMultitexturing Can the atlas use more than one texture.
* @param func An eviction function which will be called whenever the atlas has to
* evict data
- * @param data User supplied data which will be passed into func whenver an
+ * @param data User supplied data which will be passed into func whenever an
* eviction occurs
* @return An initialized GrDrawOpAtlas, or nullptr if creation fails
*/
- static std::unique_ptr<GrDrawOpAtlas> Make(GrContext*, GrPixelConfig,
- int width, int height,
+ static std::unique_ptr<GrDrawOpAtlas> Make(GrContext*, GrPixelConfig, int width, int height,
int numPlotsX, int numPlotsY,
+ AllowMultitexturing allowMultitexturing,
GrDrawOpAtlas::EvictionFunc func, void* data);
/**
@@ -134,7 +141,6 @@
data->fData = userData;
}
- static constexpr auto kMaxPages = 4;
uint32_t pageCount() { return fNumPages; }
/**
@@ -186,7 +192,7 @@
static constexpr int kMinItems = 4;
static constexpr int kMaxPlots = 32;
SkSTArray<kMinItems, PlotData, true> fPlotsToUpdate;
- uint32_t fPlotAlreadyUpdated[kMaxPages];
+ uint32_t fPlotAlreadyUpdated[kMaxMultitexturePages];
friend class GrDrawOpAtlas;
};
@@ -217,8 +223,12 @@
}
private:
- GrDrawOpAtlas(GrContext*, GrPixelConfig config, int width, int height,
- int numPlotsX, int numPlotsY);
+ uint32_t maxPages() const {
+ return AllowMultitexturing::kYes == fAllowMultitexturing ? kMaxMultitexturePages : 1;
+ }
+
+ GrDrawOpAtlas(GrContext*, GrPixelConfig config, int width, int height, int numPlotsX,
+ int numPlotsY, AllowMultitexturing allowMultitexturing);
/**
* The backing GrTexture for a GrDrawOpAtlas is broken into a spatial grid of Plots. The Plots
@@ -282,7 +292,7 @@
static GrDrawOpAtlas::AtlasID CreateId(uint32_t pageIdx, uint32_t plotIdx,
uint64_t generation) {
SkASSERT(pageIdx < (1 << 8));
- SkASSERT(pageIdx < kMaxPages);
+ SkASSERT(pageIdx < kMaxMultitexturePages);
SkASSERT(plotIdx < (1 << 8));
SkASSERT(generation < ((uint64_t)1 << 48));
return generation << 16 | plotIdx << 8 | pageIdx;
@@ -376,8 +386,9 @@
PlotList fPlotList;
};
// proxies kept separate to make it easier to pass them up to client
- sk_sp<GrTextureProxy> fProxies[kMaxPages];
- Page fPages[kMaxPages];
+ sk_sp<GrTextureProxy> fProxies[kMaxMultitexturePages];
+ Page fPages[kMaxMultitexturePages];
+ AllowMultitexturing fAllowMultitexturing;
uint32_t fNumPages;
};
diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp
index d308498..4f4dcc1 100644
--- a/src/gpu/ops/GrSmallPathRenderer.cpp
+++ b/src/gpu/ops/GrSmallPathRenderer.cpp
@@ -181,10 +181,9 @@
fHelper.visitProxies(func);
const sk_sp<GrTextureProxy>* proxies = fAtlas->getProxies();
- for (int i = 0; i < GrDrawOpAtlas::kMaxPages; ++i) {
- if (proxies[i].get()) {
- func(proxies[i].get());
- }
+ for (uint32_t i = 0; i < fAtlas->pageCount(); ++i) {
+ SkASSERT(proxies[i]);
+ func(proxies[i].get());
}
}
@@ -793,6 +792,7 @@
kAlpha_8_GrPixelConfig,
ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
NUM_PLOTS_X, NUM_PLOTS_Y,
+ GrDrawOpAtlas::AllowMultitexturing::kYes,
&GrSmallPathRenderer::HandleEviction,
(void*)this);
if (!fAtlas) {
@@ -861,6 +861,7 @@
gTestStruct.fAtlas = GrDrawOpAtlas::Make(context, kAlpha_8_GrPixelConfig,
ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
NUM_PLOTS_X, NUM_PLOTS_Y,
+ GrDrawOpAtlas::AllowMultitexturing::kYes,
&PathTestStruct::HandleEviction,
(void*)&gTestStruct);
}
diff --git a/src/gpu/text/GrAtlasGlyphCache.cpp b/src/gpu/text/GrAtlasGlyphCache.cpp
index 25575be..80f4314 100644
--- a/src/gpu/text/GrAtlasGlyphCache.cpp
+++ b/src/gpu/text/GrAtlasGlyphCache.cpp
@@ -25,9 +25,9 @@
int numPlotsX = fAtlasConfigs[index].numPlotsX();
int numPlotsY = fAtlasConfigs[index].numPlotsY();
- fAtlases[index] = GrDrawOpAtlas::Make(
- fContext, config, width, height, numPlotsX, numPlotsY,
- &GrAtlasGlyphCache::HandleEviction, (void*)this);
+ fAtlases[index] = GrDrawOpAtlas::Make(fContext, config, width, height, numPlotsX, numPlotsY,
+ fAllowMultitexturing,
+ &GrAtlasGlyphCache::HandleEviction, (void*)this);
if (!fAtlases[index]) {
return false;
}
@@ -35,11 +35,12 @@
return true;
}
-GrAtlasGlyphCache::GrAtlasGlyphCache(GrContext* context, float maxTextureBytes)
- : fContext(context), fPreserveStrike(nullptr) {
- // Calculate RGBA size. Must be between 1024 x 512 and MaxTextureSize x MaxTextureSize / 2
+GrAtlasGlyphCache::GrAtlasGlyphCache(GrContext* context, float maxTextureBytes,
+ GrDrawOpAtlas::AllowMultitexturing allowMultitexturing)
+ : fContext(context), fAllowMultitexturing(allowMultitexturing), fPreserveStrike(nullptr) {
+ // Calculate RGBA size. Must be between 512 x 256 and MaxTextureSize x MaxTextureSize / 2
int log2MaxTextureSize = SkPrevLog2(context->caps()->maxTextureSize());
- int log2MaxDim = 10;
+ int log2MaxDim = 9;
for (; log2MaxDim <= log2MaxTextureSize; ++log2MaxDim) {
int maxDim = 1 << log2MaxDim;
int minDim = 1 << (log2MaxDim - 1);
@@ -177,17 +178,16 @@
for (int i = 0; i < kMaskFormatCount; ++i) {
if (fAtlases[i]) {
const sk_sp<GrTextureProxy>* proxies = fAtlases[i]->getProxies();
- for (int pageIdx = 0; pageIdx < GrDrawOpAtlas::kMaxPages; ++pageIdx) {
- if (proxies[pageIdx]) {
- SkString filename;
+ for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->pageCount(); ++pageIdx) {
+ SkASSERT(proxies[pageIdx]);
+ SkString filename;
#ifdef SK_BUILD_FOR_ANDROID
- filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
+ filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
#else
- filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
+ filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
#endif
- save_pixels(fContext, proxies[pageIdx].get(), filename.c_str());
- }
+ save_pixels(fContext, proxies[pageIdx].get(), filename.c_str());
}
}
}
diff --git a/src/gpu/text/GrAtlasGlyphCache.h b/src/gpu/text/GrAtlasGlyphCache.h
index 20bc32a..a75ef53 100644
--- a/src/gpu/text/GrAtlasGlyphCache.h
+++ b/src/gpu/text/GrAtlasGlyphCache.h
@@ -111,7 +111,7 @@
*/
class GrAtlasGlyphCache : public GrOnFlushCallbackObject {
public:
- GrAtlasGlyphCache(GrContext*, float maxTextureBytes);
+ GrAtlasGlyphCache(GrContext*, float maxTextureBytes, GrDrawOpAtlas::AllowMultitexturing);
~GrAtlasGlyphCache() override;
// The user of the cache may hold a long-lived ref to the returned strike. However, actions by
// another client of the cache may cause the strike to be purged while it is still reffed.
@@ -256,6 +256,7 @@
using StrikeHash = SkTDynamicHash<GrAtlasTextStrike, SkDescriptor>;
GrContext* fContext;
StrikeHash fCache;
+ GrDrawOpAtlas::AllowMultitexturing fAllowMultitexturing;
std::unique_ptr<GrDrawOpAtlas> fAtlases[kMaskFormatCount];
GrAtlasTextStrike* fPreserveStrike;
GrDrawOpAtlasConfig fAtlasConfigs[kMaskFormatCount];