Update GrTextureStripAtlas for DDLs
Change-Id: I9d1344103b338f7e9dcd12739a9f192390f4cb94
Reviewed-on: https://skia-review.googlesource.com/144305
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 1c25078..3fc89e0 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -358,6 +358,8 @@
"$_src/gpu/effects/GrBicubicEffect.h",
"$_src/gpu/effects/GrBitmapTextGeoProc.cpp",
"$_src/gpu/effects/GrBitmapTextGeoProc.h",
+ "$_src/gpu/effects/GrDDLTextureStripAtlas.cpp",
+ "$_src/gpu/effects/GrDDLTextureStripAtlas.h",
"$_src/gpu/effects/GrDisableColorXP.cpp",
"$_src/gpu/effects/GrDisableColorXP.h",
"$_src/gpu/effects/GrDistanceFieldGeoProc.cpp",
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index 25cfa6b..9eb626b 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -287,6 +287,7 @@
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrFragmentProcessor.h"
+#include "GrTexture.h"
#include "GrTextureStripAtlas.h"
#include "SkGr.h"
#include "glsl/GrGLSLFragmentProcessor.h"
@@ -307,6 +308,10 @@
std::unique_ptr<GrFragmentProcessor> clone() const override;
+ int peekBackingHeight() const {
+ return fTextureSampler.peekTexture()->height();
+ }
+
private:
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
@@ -347,8 +352,8 @@
float rgbaYValues[4];
const ColorTableEffect& cte = proc.cast<ColorTableEffect>();
if (cte.atlas()) {
- SkScalar yDelta = cte.atlas()->getNormalizedTexelHeight();
- rgbaYValues[3] = cte.atlas()->getYOffset(cte.atlasRow()) + SK_ScalarHalf * yDelta;
+ SkScalar yDelta = 1.0f / cte.peekBackingHeight();
+ rgbaYValues[3] = cte.atlas()->rowToTextureY(cte.atlasRow()) * yDelta;
rgbaYValues[0] = rgbaYValues[3] + yDelta;
rgbaYValues[1] = rgbaYValues[0] + yDelta;
rgbaYValues[2] = rgbaYValues[1] + yDelta;
@@ -412,26 +417,31 @@
///////////////////////////////////////////////////////////////////////////////
std::unique_ptr<GrFragmentProcessor> ColorTableEffect::Make(GrContext* context,
const SkBitmap& bitmap) {
- GrTextureStripAtlas::Desc desc;
- desc.fWidth = bitmap.width();
- desc.fHeight = 128;
- desc.fRowHeight = bitmap.height();
- desc.fConfig = SkColorType2GrPixelConfig(bitmap.colorType());
+ SkASSERT(kPremul_SkAlphaType == bitmap.alphaType());
+ SkASSERT(bitmap.isImmutable());
- if (kUnknown_GrPixelConfig == desc.fConfig) {
+ if (kUnknown_GrPixelConfig == SkColorType2GrPixelConfig(bitmap.colorType())) {
return nullptr;
}
+ GrTextureStripAtlas::Desc desc;
+ desc.fColorType = bitmap.colorType();
+ desc.fWidth = bitmap.width();
+ desc.fHeight = 128;
+ desc.fRowHeight = bitmap.height();
+
auto atlasManager = context->contextPriv().textureStripAtlasManager();
- sk_sp<GrTextureStripAtlas> atlas = atlasManager->refAtlas(desc);
- int row = atlas->lockRow(context, bitmap);
+ int row;
+ sk_sp<GrTextureStripAtlas> atlas = atlasManager->addStrip(context, desc, bitmap, &row);
+ if (!context->contextPriv().resourceProvider()) {
+ SkASSERT(atlas && row >= 0); // In DDL mode we should always be able to atlas
+ }
+
sk_sp<GrTextureProxy> proxy;
if (-1 == row) {
atlas = nullptr;
- SkASSERT(bitmap.isImmutable());
-
sk_sp<SkImage> srcImage = SkImage::MakeFromBitmap(bitmap);
if (!srcImage) {
return nullptr;
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index d7c5129..b849bea 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -25,6 +25,7 @@
#include "GrTexturePriv.h"
#include "GrTextureProxy.h"
#include "GrTextureProxyPriv.h"
+#include "GrTextureStripAtlas.h"
#include "GrTracing.h"
#include "SkDeferredDisplayList.h"
#include "SkSurface_Gpu.h"
@@ -394,6 +395,9 @@
}
void GrDrawingManager::moveOpListsToDDL(SkDeferredDisplayList* ddl) {
+ fContext->contextPriv().textureStripAtlasManager()->finish(
+ fContext->contextPriv().proxyProvider());
+
for (int i = 0; i < fOpLists.count(); ++i) {
// no opList should receive a new command after this
fOpLists[i]->makeClosed(*fContext->contextPriv().caps());
diff --git a/src/gpu/GrTextureStripAtlas.h b/src/gpu/GrTextureStripAtlas.h
index af972b5..3f4e897 100644
--- a/src/gpu/GrTextureStripAtlas.h
+++ b/src/gpu/GrTextureStripAtlas.h
@@ -8,8 +8,6 @@
#ifndef GrTextureStripAtlas_DEFINED
#define GrTextureStripAtlas_DEFINED
-#include "effects/GrDynamicTextureStripAtlas.h"
-
#include "SkNoncopyable.h"
#include "SkOpts.h"
#include "SkRefCnt.h"
@@ -20,17 +18,100 @@
class GrTextureProxy;
class SkBitmap;
+/**
+ * Base class for the texture strip atlases.
+ * It is ref counted because the GradientShader and TableColorFilter are given a pointer to it
+ * so that they can lock and unlock rows.
+ */
+class GrTextureStripAtlas : public SkRefCnt {
+public:
+ /**
+ * Descriptor struct which we'll use both to find and initialize an atlas and as a hash
+ * table key in the GrTextureStripAtlasManager.
+ */
+ struct Desc {
+ Desc() { sk_bzero(this, sizeof(*this)); }
+ SkColorType fColorType;
+ uint16_t fWidth;
+ uint16_t fHeight; // the max height for the DDL version, the size of the atlas for normal
+ uint16_t fRowHeight;
+ uint16_t fUnusedPadding;
+
+ bool operator==(const Desc& other) const {
+ return 0 == memcmp(this, &other, sizeof(Desc));
+ }
+ };
+
+ ~GrTextureStripAtlas() override {}
+
+ /**
+ * This is intended to be used when cloning a processor that already holds a lock. It is
+ * assumed that the row already has at least one lock.
+ */
+ virtual void lockRow(int row) = 0;
+
+ /**
+ * Some user of a given row is done. Release that row for reuse.
+ */
+ virtual void unlockRow(int row) = 0;
+
+ /**
+ * This returns the absolute Y location of the given row in the atlas. For atlases with
+ * 'fRowHeight' > 1, this is Y location of the topmost row of the atlas entry. It is always
+ * the middle of the row.
+ */
+ SkScalar rowToTextureY(int row) const {
+ return row * fDesc.fRowHeight + SK_ScalarHalf;
+ }
+
+ /**
+ * Get the texture proxy backing this atlas. Note that the texture proxy may be fully lazy
+ * (i.e., when recording DDLs) and, in particular, the final height may not be known.
+ */
+ virtual sk_sp<GrTextureProxy> asTextureProxyRef() const = 0;
+
+protected:
+ GrTextureStripAtlas(const Desc& desc) : fDesc(desc) {}
+
+ const Desc fDesc;
+
+private:
+ friend class GrTextureStripAtlasManager; // for addStrip, finish
+
+ /**
+ * Add a texture strip to the atlas
+ * @param context Everyone's favorite class
+ * @param bitmap Bitmap data to copy into the row
+ * @return The row index we inserted into, or -1 if we failed to find an open row. The caller
+ * is responsible for calling unlockRow() with this row index when it's done with it.
+ */
+ virtual int addStrip(GrContext*, const SkBitmap& bitmap) = 0;
+
+ /**
+ * This method is called when an atlas needs to finish its work on the current texture.
+ * Currently it is only called in DDL mode and when either:
+ * a given atlas has become full or,
+ * a DDL is being snapped from a DDL recorder
+ */
+ virtual void finish(GrProxyProvider*) = 0;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
class GrTextureStripAtlasManager {
public:
GrTextureStripAtlasManager() {}
~GrTextureStripAtlasManager();
void abandon();
+ void finish(GrProxyProvider*);
/**
- * Try to find an atlas with the required parameters, creates a new one if necessary
+ * Add a new texture strip to the atlas matching the descriptor. Upon failure, nullptr
+ * will be returned and 'row' will be set to -1.
*/
- sk_sp<GrTextureStripAtlas> refAtlas(const GrTextureStripAtlas::Desc&);
+ sk_sp<GrTextureStripAtlas> addStrip(GrContext*,
+ const GrTextureStripAtlas::Desc&,
+ const SkBitmap&, int* row);
private:
void deleteAllAtlases();
@@ -38,21 +119,17 @@
// Hash table entry for atlases
class AtlasEntry : public ::SkNoncopyable {
public:
- AtlasEntry(const GrTextureStripAtlas::Desc& desc, sk_sp<GrTextureStripAtlas> atlas)
- : fDesc(desc)
- , fAtlas(std::move(atlas)) {
- }
+ AtlasEntry(sk_sp<GrTextureStripAtlas> atlas) : fAtlas(std::move(atlas)) {}
~AtlasEntry() { }
// for SkTDynamicHash
static const GrTextureStripAtlas::Desc& GetKey(const AtlasEntry& entry) {
- return entry.fDesc;
+ return entry.fAtlas->fDesc;
}
static uint32_t Hash(const GrTextureStripAtlas::Desc& desc) {
return SkOpts::hash(&desc, sizeof(GrTextureStripAtlas::Desc));
}
- const GrTextureStripAtlas::Desc fDesc;
sk_sp<GrTextureStripAtlas> fAtlas;
};
diff --git a/src/gpu/effects/GrDDLTextureStripAtlas.cpp b/src/gpu/effects/GrDDLTextureStripAtlas.cpp
new file mode 100644
index 0000000..ac22df5
--- /dev/null
+++ b/src/gpu/effects/GrDDLTextureStripAtlas.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrDDLTextureStripAtlas.h"
+
+#include "GrContextPriv.h"
+#include "GrTexture.h"
+#include "SkGr.h"
+#include "SkTSearch.h"
+
+GrDDLTextureStripAtlas::GrDDLTextureStripAtlas(const Desc& desc)
+ : INHERITED(desc)
+ , fAtlasBitmap(nullptr)
+ , fMaxNumRows(desc.fHeight / desc.fRowHeight)
+ , fCurRow(0)
+ , fRows(new AtlasRow[fMaxNumRows]) {
+ SkASSERT(fMaxNumRows * fDesc.fRowHeight == fDesc.fHeight);
+ SkDEBUGCODE(this->validate();)
+}
+
+GrDDLTextureStripAtlas::~GrDDLTextureStripAtlas() { delete[] fRows; }
+
+// Flush the current state of the atlas.
+void GrDDLTextureStripAtlas::finish(GrProxyProvider* proxyProvider) {
+ SkDEBUGCODE(this->validate();)
+
+ if (!fCurRow) {
+ SkASSERT(!fCurProxy && !fAtlasBitmap);
+ return;
+ }
+
+ int height = fCurRow * fDesc.fRowHeight;
+ SkASSERT(height <= fDesc.fHeight);
+
+ SkImageInfo ii = SkImageInfo::Make(fDesc.fWidth, height,
+ fDesc.fColorType, kPremul_SkAlphaType);
+ fAtlasBitmap->allocPixels(ii);
+
+ for (int i = 0; i < fCurRow; ++i) {
+ SkASSERT(fRows[i].fBitmap.height() == fDesc.fRowHeight);
+
+ int yPos = i * fDesc.fRowHeight;
+ fAtlasBitmap->writePixels(fRows[i].fBitmap.pixmap(), 0, yPos);
+ }
+
+ GrUniqueKey key;
+ {
+ static const GrUniqueKey::Domain kTextureStripAtlasDomain = GrUniqueKey::GenerateDomain();
+ GrUniqueKey::Builder builder(&key, kTextureStripAtlasDomain, fCurRow,
+ "DDL Texture Strip Atlas");
+ for (int i = 0; i < fCurRow; ++i) {
+ builder[i] = fRows[i].fBitmap.getGenerationID();
+ }
+ builder.finish();
+ }
+
+ sk_sp<GrTextureProxy> interloper = proxyProvider->findProxyByUniqueKey(key,
+ fCurProxy->origin());
+ if (!interloper) {
+ // In the unlikely event that there is already a proxy with this key (i.e., it has exactly
+ // the same strips in exactly the same order) we'll just let it keep the key.
+ proxyProvider->assignUniqueKeyToProxy(key, fCurProxy.get());
+ }
+
+ // reset the state for the next aggregate texture
+ for (int i = 0; i < fCurRow; ++i) {
+ fRows[i].fBitmap.reset();
+ }
+ fCurRow = 0;
+ fCurProxy = nullptr;
+ fAtlasBitmap = nullptr;
+ fKeyTable.rewind();
+ SkDEBUGCODE(this->validate();)
+}
+
+int GrDDLTextureStripAtlas::addStrip(GrContext* context, const SkBitmap& bitmap) {
+ SkDEBUGCODE(this->validate();)
+
+ const int key = bitmap.getGenerationID();
+ int index = this->searchByKey(key);
+
+ if (fCurRow >= fMaxNumRows && index < 0) {
+ // The current atlas is full and adding another strip would make it overflow. Calve it off
+ // and allow the next block to start a new one.
+ this->finish(context->contextPriv().proxyProvider());
+ index = this->searchByKey(key); // 'finish' cleared the table
+ }
+
+ if (!fCurProxy) {
+ SkASSERT(!fAtlasBitmap);
+
+ const GrCaps* caps = context->contextPriv().caps();
+ GrPixelConfig pixelConfig = SkColorType2GrPixelConfig(fDesc.fColorType);
+ SkASSERT(kUnknown_GrPixelConfig != pixelConfig);
+
+ SkBitmap* atlasBitmap = new SkBitmap();
+
+ fCurProxy = GrProxyProvider::MakeFullyLazyProxy(
+ [atlasBitmap, pixelConfig](GrResourceProvider* provider) -> sk_sp<GrSurface> {
+ if (!provider) {
+ delete atlasBitmap;
+ return sk_sp<GrSurface>();
+ }
+ // When this is called 'atlasBitmap' should've been filled in and be
+ // non-empty
+ SkASSERT(atlasBitmap->width() && atlasBitmap->height());
+ GrSurfaceDesc desc;
+ desc.fFlags = kNone_GrSurfaceFlags;
+ desc.fWidth = atlasBitmap->width();
+ desc.fHeight = atlasBitmap->height();
+ desc.fConfig = pixelConfig;
+
+ GrMipLevel mipLevel = { atlasBitmap->getPixels(), atlasBitmap->rowBytes() };
+
+ return provider->createTexture(desc, SkBudgeted::kYes,
+ SkBackingFit::kExact, mipLevel);
+ },
+ GrProxyProvider::Renderable::kNo, kTopLeft_GrSurfaceOrigin, pixelConfig, *caps);
+
+ fAtlasBitmap = atlasBitmap;
+ }
+
+ SkASSERT(bitmap.width() == fDesc.fWidth);
+ SkASSERT(bitmap.height() == fDesc.fRowHeight);
+ SkASSERT(!context->contextPriv().resourceProvider()); // This atlas class is DDL specific
+ SkASSERT(fCurRow < fMaxNumRows);
+
+ int rowNumber = -1;
+
+ if (index >= 0) {
+ // We already have the data in a row, so we can just return that row
+ AtlasRow* row = fKeyTable[index];
+
+ // Since all the rows are always stored in a contiguous array, we can save the memory
+ // required for storing row numbers and just compute it with some pointer arithmetic
+ rowNumber = static_cast<int>(row - fRows);
+ } else {
+ // ~index is the index where we will insert the new key to keep things sorted
+ index = ~index;
+
+ rowNumber = fCurRow;
+ fRows[fCurRow].fBitmap = bitmap;
+
+ AtlasRow* row = &fRows[rowNumber];
+ fKeyTable.insert(index, 1, &row);
+
+ ++fCurRow;
+ SkASSERT(fCurRow <= fMaxNumRows);
+ }
+
+ SkASSERT(rowNumber >= 0);
+ SkDEBUGCODE(this->validate();)
+ return rowNumber;
+}
+
+int GrDDLTextureStripAtlas::searchByKey(uint32_t generationID) {
+ static struct AtlasRowLessFunctor {
+ bool operator()(const AtlasRow* row, const uint32_t& id) const {
+ return row->fBitmap.getGenerationID() < id;
+ }
+ bool operator()(const uint32_t& id, const AtlasRow* row) const {
+ return id < row->fBitmap.getGenerationID();
+ }
+ } functor;
+
+ return SkTSearch(fKeyTable.begin(), fKeyTable.count(), generationID, sizeof(AtlasRow*),
+ functor);
+}
+
+#ifdef SK_DEBUG
+void GrDDLTextureStripAtlas::validate() {
+ static const int kBitmapInvalidGenID = 0;
+
+ // Our key table should be sorted
+ uint32_t prev = fKeyTable.count() >= 1 ? fKeyTable[0]->fBitmap.getGenerationID() : 0;
+ for (int i = 1; i < fKeyTable.count(); ++i) {
+ AtlasRow* row = fKeyTable[i];
+ SkASSERT(prev < row->fBitmap.getGenerationID());
+ SkASSERT(row->fBitmap.getGenerationID() != kBitmapInvalidGenID);
+ prev = row->fBitmap.getGenerationID();
+ }
+
+ for (int i = 0; i < fCurRow; ++i) {
+ // These should all have a valid bitmap and be in the search table
+ SkASSERT(fRows[i].fBitmap.getGenerationID() != kBitmapInvalidGenID);
+ SkASSERT(this->searchByKey(fRows[i].fBitmap.getGenerationID()) >= 0);
+ }
+ for (int i = fCurRow; i < fMaxNumRows; ++i) {
+ // These should all be empty
+ SkASSERT(fRows[i].fBitmap.getGenerationID() == kBitmapInvalidGenID);
+ }
+}
+#endif
diff --git a/src/gpu/effects/GrDDLTextureStripAtlas.h b/src/gpu/effects/GrDDLTextureStripAtlas.h
new file mode 100644
index 0000000..5fb5804
--- /dev/null
+++ b/src/gpu/effects/GrDDLTextureStripAtlas.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrDDLTextureStripAtlas_DEFINED
+#define GrDDLTextureStripAtlas_DEFINED
+
+#include "GrTextureStripAtlas.h"
+
+#include "SkBitmap.h"
+#include "SkTDArray.h"
+
+/**
+ * The DDL version of the texture strip atlas consolidates individual strips into a larger texture
+ * until some limit is reached, at which point a new large texture is started.
+ * This can lead to the same strip being duplicated in VRAM. This can happen if a strip appears once
+ * early in a rendering (that has, say, a lot of gradients) and then again later in the rendering
+ * when one of the large textures has been filled. The second, probably more common, case is
+ * if the same strip is used in different DDL recordings. Since the texture strip atlases aren't
+ * dedupped across threads, if the same strip is used in two different DDL recordings it will
+ * be duplicated in both of the DDL recorders' atlases.
+ * Note, one additional feature of the DDL texture strip atlases is that, if DDL recording is ended
+ * before one of the large textures is full, the large texture will be "shrunk" to fit its
+ * contents.
+ */
+class GrDDLTextureStripAtlas final : public GrTextureStripAtlas {
+public:
+ ~GrDDLTextureStripAtlas() final;
+
+ // Overrides from GrTextureStripAtlas
+ void lockRow(int row) final { /* The DDL version doesn't lock & unlock individual rows */}
+ void unlockRow(int row) final { /* The DDL version doesn't lock & unlock individual rows */}
+
+ // Caution: this method will only return the appropriate proxy after a successful 'addStrip'
+ // call has been made. Additionally, the proxy return will be fully lazy (i.e., its final
+ // height will be unknown).
+ sk_sp<GrTextureProxy> asTextureProxyRef() const final {
+ SkASSERT(fCurProxy);
+ return fCurProxy;
+ }
+
+private:
+ friend class GrTextureStripAtlasManager; // for ctor
+
+ // Overrides from GrTextureStripAtlas
+ int addStrip(GrContext*, const SkBitmap&) final;
+ void finish(GrProxyProvider*) final;
+
+ /**
+ * The state of a single row in our cache. For the DDL texture strip atlas we hold onto all
+ * the individual strip bitmaps and, upon finish, combine them all into a single bitmap.
+ */
+ struct AtlasRow : ::SkNoncopyable {
+ AtlasRow() {}
+
+ SkBitmap fBitmap;
+ };
+
+ /**
+ * Only the GrTextureStripAtlasManager is allowed to create GrDDLTextureStripAtlas
+ */
+ GrDDLTextureStripAtlas(const Desc& desc);
+
+ /**
+ * Searches the key table for a key and returns the index if found; if not found, it returns
+ * the bitwise not of the index at which we could insert the key to maintain a sorted list.
+ **/
+ int searchByKey(uint32_t key);
+
+ SkDEBUGCODE(void validate();)
+
+ sk_sp<GrTextureProxy> fCurProxy; // the lazy proxy that will be split off in the finish call
+ SkBitmap* fAtlasBitmap; // the bitmap backing 'fCurProxy'
+
+ const uint16_t fMaxNumRows;
+ uint16_t fCurRow;
+
+ AtlasRow* fRows; // We just store the source bitmap for each row.
+
+ // A list of pointers to AtlasRows that currently contain cached images, sorted by key
+ SkTDArray<AtlasRow*> fKeyTable;
+
+ typedef GrTextureStripAtlas INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrDynamicTextureStripAtlas.cpp b/src/gpu/effects/GrDynamicTextureStripAtlas.cpp
index ede6809..04683e1 100644
--- a/src/gpu/effects/GrDynamicTextureStripAtlas.cpp
+++ b/src/gpu/effects/GrDynamicTextureStripAtlas.cpp
@@ -18,7 +18,7 @@
#define VALIDATE
#endif
-uint32_t GrTextureStripAtlas::CreateUniqueID() {
+uint32_t GrDynamicTextureStripAtlas::CreateUniqueID() {
static int32_t gUniqueID = SK_InvalidUniqueID;
uint32_t id;
// Loop in case our global wraps around, as we never want to return a 0.
@@ -28,30 +28,30 @@
return id;
}
-GrTextureStripAtlas::GrTextureStripAtlas(const Desc& desc)
- : fCacheKey(CreateUniqueID())
- , fLockedRows(0)
- , fDesc(desc)
- , fNumRows(desc.fHeight / desc.fRowHeight)
- , fRows(new AtlasRow[fNumRows])
- , fLRUFront(nullptr)
- , fLRUBack(nullptr) {
+GrDynamicTextureStripAtlas::GrDynamicTextureStripAtlas(const Desc& desc)
+ : INHERITED(desc)
+ , fCacheKey(CreateUniqueID())
+ , fLockedRows(0)
+ , fNumRows(desc.fHeight / desc.fRowHeight)
+ , fRows(new AtlasRow[fNumRows])
+ , fLRUFront(nullptr)
+ , fLRUBack(nullptr) {
SkASSERT(fNumRows * fDesc.fRowHeight == fDesc.fHeight);
this->initLRU();
fNormalizedYHeight = SK_Scalar1 / fDesc.fHeight;
VALIDATE;
}
-GrTextureStripAtlas::~GrTextureStripAtlas() { delete[] fRows; }
+GrDynamicTextureStripAtlas::~GrDynamicTextureStripAtlas() { delete[] fRows; }
-void GrTextureStripAtlas::lockRow(int row) {
+void GrDynamicTextureStripAtlas::lockRow(int row) {
// This should only be called on a row that is already locked.
SkASSERT(fRows[row].fLocks);
fRows[row].fLocks++;
++fLockedRows;
}
-int GrTextureStripAtlas::lockRow(GrContext* context, const SkBitmap& bitmap) {
+int GrDynamicTextureStripAtlas::addStrip(GrContext* context, const SkBitmap& bitmap) {
VALIDATE;
if (!context->contextPriv().resourceProvider()) {
@@ -142,11 +142,11 @@
return rowNumber;
}
-sk_sp<GrTextureProxy> GrTextureStripAtlas::asTextureProxyRef() const {
+sk_sp<GrTextureProxy> GrDynamicTextureStripAtlas::asTextureProxyRef() const {
return fTexContext->asTextureProxyRef();
}
-void GrTextureStripAtlas::unlockRow(int row) {
+void GrDynamicTextureStripAtlas::unlockRow(int row) {
VALIDATE;
--fRows[row].fLocks;
--fLockedRows;
@@ -160,13 +160,13 @@
VALIDATE;
}
-GrTextureStripAtlas::AtlasRow* GrTextureStripAtlas::getLRU() {
+GrDynamicTextureStripAtlas::AtlasRow* GrDynamicTextureStripAtlas::getLRU() {
// Front is least-recently-used
AtlasRow* row = fLRUFront;
return row;
}
-void GrTextureStripAtlas::lockTexture(GrContext* context) {
+void GrDynamicTextureStripAtlas::lockTexture(GrContext* context) {
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey key;
@@ -179,10 +179,12 @@
sk_sp<GrTextureProxy> proxy = proxyProvider->findOrCreateProxyByUniqueKey(
key, kTopLeft_GrSurfaceOrigin);
if (!proxy) {
+ GrPixelConfig pixelConfig = SkColorType2GrPixelConfig(fDesc.fColorType);
+
GrSurfaceDesc texDesc;
texDesc.fWidth = fDesc.fWidth;
texDesc.fHeight = fDesc.fHeight;
- texDesc.fConfig = fDesc.fConfig;
+ texDesc.fConfig = pixelConfig;
proxy = proxyProvider->createProxy(texDesc, kTopLeft_GrSurfaceOrigin, SkBackingFit::kExact,
SkBudgeted::kYes, GrInternalSurfaceFlags::kNoPendingIO);
@@ -200,12 +202,12 @@
fTexContext = context->contextPriv().makeWrappedSurfaceContext(std::move(proxy));
}
-void GrTextureStripAtlas::unlockTexture() {
+void GrDynamicTextureStripAtlas::unlockTexture() {
SkASSERT(fTexContext && 0 == fLockedRows);
fTexContext.reset();
}
-void GrTextureStripAtlas::initLRU() {
+void GrDynamicTextureStripAtlas::initLRU() {
fLRUFront = nullptr;
fLRUBack = nullptr;
// Initially all the rows are in the LRU list
@@ -219,7 +221,7 @@
SkASSERT(nullptr == fLRUBack || nullptr == fLRUBack->fNext);
}
-void GrTextureStripAtlas::appendLRU(AtlasRow* row) {
+void GrDynamicTextureStripAtlas::appendLRU(AtlasRow* row) {
SkASSERT(nullptr == row->fPrev && nullptr == row->fNext);
if (nullptr == fLRUFront && nullptr == fLRUBack) {
fLRUFront = row;
@@ -231,7 +233,7 @@
}
}
-void GrTextureStripAtlas::removeFromLRU(AtlasRow* row) {
+void GrDynamicTextureStripAtlas::removeFromLRU(AtlasRow* row) {
SkASSERT(row);
if (row->fNext && row->fPrev) {
row->fPrev->fNext = row->fNext;
@@ -256,19 +258,17 @@
row->fPrev = nullptr;
}
-int GrTextureStripAtlas::searchByKey(uint32_t key) {
+int GrDynamicTextureStripAtlas::searchByKey(uint32_t key) {
AtlasRow target;
target.fKey = key;
- return SkTSearch<const AtlasRow,
- GrTextureStripAtlas::KeyLess>((const AtlasRow**)fKeyTable.begin(),
- fKeyTable.count(),
- &target,
- sizeof(AtlasRow*));
+ return SkTSearch<const AtlasRow, KeyLess>((const AtlasRow**)fKeyTable.begin(),
+ fKeyTable.count(),
+ &target,
+ sizeof(AtlasRow*));
}
#ifdef SK_DEBUG
-void GrTextureStripAtlas::validate() {
-
+void GrDynamicTextureStripAtlas::validate() {
// Our key table should be sorted
uint32_t prev = 1 > fKeyTable.count() ? 0 : fKeyTable[0]->fKey;
for (int i = 1; i < fKeyTable.count(); ++i) {
diff --git a/src/gpu/effects/GrDynamicTextureStripAtlas.h b/src/gpu/effects/GrDynamicTextureStripAtlas.h
index b404f57..301617b 100644
--- a/src/gpu/effects/GrDynamicTextureStripAtlas.h
+++ b/src/gpu/effects/GrDynamicTextureStripAtlas.h
@@ -8,39 +8,35 @@
#ifndef GrDynamicTextureStripAtlas_DEFINED
#define GrDynamicTextureStripAtlas_DEFINED
-#include "GrTypesPriv.h"
-
-#include "SkNoncopyable.h"
-#include "SkRefCnt.h"
-#include "SkScalar.h"
+#include "GrTextureStripAtlas.h"
#include "SkTDArray.h"
-class GrContext;
class GrSurfaceContext;
-class GrTextureProxy;
-
-class SkBitmap;
/**
* Maintains a single large texture whose rows store many textures of a small fixed height,
* stored in rows across the x-axis such that we can safely wrap/repeat them horizontally.
*/
-class GrTextureStripAtlas : public SkRefCnt {
+class GrDynamicTextureStripAtlas final : public GrTextureStripAtlas {
public:
- /**
- * Descriptor struct which we'll use as a hash table key
- */
- struct Desc {
- Desc() { sk_bzero(this, sizeof(*this)); }
- GrPixelConfig fConfig;
- uint16_t fWidth, fHeight, fRowHeight;
- uint16_t fUnusedPadding;
- bool operator==(const Desc& other) const {
- return 0 == memcmp(this, &other, sizeof(Desc));
- }
- };
+ ~GrDynamicTextureStripAtlas() final;
- ~GrTextureStripAtlas();
+ /**
+ * This is intended to be used when cloning a processor that already holds a lock. It is
+ * assumed that the row already has at least one lock.
+ */
+ void lockRow(int row) final;
+ void unlockRow(int row) final;
+
+ sk_sp<GrTextureProxy> asTextureProxyRef() const final;
+
+private:
+ friend class GrTextureStripAtlasManager; // for ctor
+
+ /**
+ * Only the GrTextureStripAtlasManager is allowed to create GrTextureStripAtlases
+ */
+ GrDynamicTextureStripAtlas(const Desc& desc);
/**
* Add a texture to the atlas
@@ -48,38 +44,9 @@
* @return The row index we inserted into, or -1 if we failed to find an open row. The caller
* is responsible for calling unlockRow() with this row index when it's done with it.
*/
- int lockRow(GrContext*, const SkBitmap&);
+ int addStrip(GrContext*, const SkBitmap&) final;
- /**
- * This is intended to be used when cloning a processor that already holds a lock. It is
- * assumed that the row already has at least one lock.
- */
- void lockRow(int row);
- void unlockRow(int row);
-
- /**
- * These functions help turn an integer row index in [0, 1, 2, ... numRows] into a scalar y
- * texture coordinate in [0, 1] that we can use in a shader.
- *
- * If a regular texture access without using the atlas looks like:
- *
- * texture2D(sampler, float2(x, y))
- *
- * Then when using the atlas we'd replace it with:
- *
- * texture2D(sampler, float2(x, yOffset + y * scaleFactor))
- *
- * Where yOffset, returned by getYOffset(), is the offset to the start of the row within the
- * atlas and scaleFactor, returned by getNormalizedTexelHeight, is the normalized height of
- * one texel row.
- */
- SkScalar getYOffset(int row) const { return SkIntToScalar(row) / fNumRows; }
- SkScalar getNormalizedTexelHeight() const { return fNormalizedYHeight; }
-
- sk_sp<GrTextureProxy> asTextureProxyRef() const;
-
-private:
- friend class GrTextureStripAtlasManager; // for ctor
+ void finish(GrProxyProvider*) final { SkASSERT(0); } // this is only called in DDL mode
static uint32_t CreateUniqueID();
@@ -101,11 +68,6 @@
AtlasRow* fPrev;
};
- /**
- * Only the GrTextureStripAtlasManager is allowed to create GrTextureStripAtlases
- */
- GrTextureStripAtlas(const Desc& desc);
-
void lockTexture(GrContext*);
void unlockTexture();
@@ -147,7 +109,6 @@
// Total locks on all rows (when this reaches zero, we can unlock our texture)
int32_t fLockedRows;
- const Desc fDesc;
const uint16_t fNumRows;
sk_sp<GrSurfaceContext> fTexContext;
@@ -165,6 +126,8 @@
// A list of pointers to AtlasRows that currently contain cached images, sorted by key
SkTDArray<AtlasRow*> fKeyTable;
+
+ typedef GrTextureStripAtlas INHERITED;
};
#endif
diff --git a/src/gpu/effects/GrTextureStripAtlas.cpp b/src/gpu/effects/GrTextureStripAtlas.cpp
index eecaadf..9144184 100644
--- a/src/gpu/effects/GrTextureStripAtlas.cpp
+++ b/src/gpu/effects/GrTextureStripAtlas.cpp
@@ -8,10 +8,10 @@
#include "GrTextureStripAtlas.h"
#include "GrContext.h"
#include "GrContextPriv.h"
+#include "GrDDLTextureStripAtlas.h"
#include "GrDynamicTextureStripAtlas.h"
#include "SkBitmap.h"
-////////////////////////////////////////////////////////////////////////////////
GrTextureStripAtlasManager::~GrTextureStripAtlasManager() {
this->deleteAllAtlases();
}
@@ -30,15 +30,41 @@
this->deleteAllAtlases();
}
-sk_sp<GrTextureStripAtlas> GrTextureStripAtlasManager::refAtlas(
- const GrTextureStripAtlas::Desc& desc) {
+void GrTextureStripAtlasManager::finish(GrProxyProvider* proxyProvider) {
+ for (AtlasHash::Iter iter(&fAtlasCache); !iter.done(); ++iter) {
+ AtlasEntry* tmp = &(*iter);
+ tmp->fAtlas->finish(proxyProvider);
+ }
+ fAtlasCache.reset();
+}
+
+
+sk_sp<GrTextureStripAtlas> GrTextureStripAtlasManager::addStrip(
+ GrContext* context,
+ const GrTextureStripAtlas::Desc& desc,
+ const SkBitmap& bitmap,
+ int* row) {
+ SkASSERT(kPremul_SkAlphaType == bitmap.alphaType());
+
AtlasEntry* entry = fAtlasCache.find(desc);
if (!entry) {
- // TODO: Does the AtlasEntry need a copy of the Desc if the GrTextureStripAtlas has one?
- entry = new AtlasEntry(desc, sk_sp<GrTextureStripAtlas>(new GrTextureStripAtlas(desc)));
+ sk_sp<GrTextureStripAtlas> atlas;
+ if (!context->contextPriv().resourceProvider()) {
+ atlas.reset(new GrDDLTextureStripAtlas(desc));
+ } else {
+ atlas.reset(new GrDynamicTextureStripAtlas(desc));
+ }
+
+ entry = new AtlasEntry(sk_sp<GrTextureStripAtlas>(std::move(atlas)));
fAtlasCache.add(entry);
}
+ *row = entry->fAtlas->addStrip(context, bitmap);
+ if (*row < 0) {
+ return nullptr;
+ }
+
return entry->fAtlas;
}
+
diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp
index aa44ae6..536a7a8 100644
--- a/src/shaders/gradients/SkGradientShader.cpp
+++ b/src/shaders/gradients/SkGradientShader.cpp
@@ -919,6 +919,7 @@
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrShaderCaps.h"
+#include "GrTexture.h"
#include "GrTextureStripAtlas.h"
#include "gl/GrGLContext.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
@@ -966,7 +967,8 @@
break;
case GrGradientEffect::InterpolationStrategy::kTexture:
if (e.fYCoord != fCachedYCoord) {
- pdman.set1f(fFSYUni, e.fYCoord);
+ SkScalar yDelta = 1.0f / e.fTextureSampler.peekTexture()->height();
+ pdman.set1f(fFSYUni, e.fYCoord * yDelta);
fCachedYCoord = e.fYCoord;
}
break;
@@ -1246,6 +1248,8 @@
SkBitmap bitmap;
shader.getGradientTableBitmap(xformedColors.fColors, &bitmap, colorType);
SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width()));
+ SkASSERT(kPremul_SkAlphaType == bitmap.alphaType());
+ SkASSERT(bitmap.isImmutable());
auto atlasManager = args.fContext->contextPriv().textureStripAtlasManager();
@@ -1253,18 +1257,24 @@
desc.fWidth = bitmap.width();
desc.fHeight = 32;
desc.fRowHeight = bitmap.height(); // always 1 here
- desc.fConfig = SkColorType2GrPixelConfig(bitmap.colorType());
- fAtlas = atlasManager->refAtlas(desc);
- SkASSERT(fAtlas);
+ desc.fColorType = bitmap.colorType();
+
+ int row;
+ fAtlas = atlasManager->addStrip(args.fContext, desc, bitmap, &row);
+ if (!args.fContext->contextPriv().resourceProvider()) {
+ // In DDL mode we should always be able to atlas
+ SkASSERT(fAtlas && row >= 0);
+ }
// We always filter the gradient table. Each table is one row of a texture, always
// y-clamp.
GrSamplerState samplerState(args.fWrapMode, GrSamplerState::Filter::kBilerp);
- fRow = fAtlas->lockRow(args.fContext, bitmap);
if (-1 != fRow) {
- fYCoord = fAtlas->getYOffset(fRow)+SK_ScalarHalf*fAtlas->getNormalizedTexelHeight();
- // This is 1/2 places where auto-normalization is disabled
+ SkASSERT(fAtlas);
+
+ fYCoord = fAtlas->rowToTextureY(fRow);
+ // This is 1/2 places where auto-normalization is disabled bc the gradient T is 0..1
fCoordTransform.reset(*args.fMatrix, fAtlas->asTextureProxyRef().get(), false);
fTextureSampler.reset(fAtlas->asTextureProxyRef(), samplerState);
} else {
@@ -1276,7 +1286,6 @@
// that GrMakeCachedImageProxy is sufficient (i.e., it won't need to be
// extracted to a subset or mipmapped).
- SkASSERT(bitmap.isImmutable());
sk_sp<SkImage> srcImage = SkImage::MakeFromBitmap(bitmap);
if (!srcImage) {
return;
@@ -1289,11 +1298,13 @@
SkDebugf("Gradient won't draw. Could not create texture.");
return;
}
- // This is 2/2 places where auto-normalization is disabled
+ // This is 2/2 places where auto-normalization is disabled because the graient T is 0..1
fCoordTransform.reset(*args.fMatrix, proxy.get(), false);
fTextureSampler.reset(std::move(proxy), samplerState);
+ SkASSERT(1 == bitmap.height());
fYCoord = SK_ScalarHalf;
}
+
this->setTextureSamplerCnt(1);
}
diff --git a/src/shaders/gradients/SkGradientShaderPriv.h b/src/shaders/gradients/SkGradientShaderPriv.h
index 6aa5da2..f3e10d0 100644
--- a/src/shaders/gradients/SkGradientShaderPriv.h
+++ b/src/shaders/gradients/SkGradientShaderPriv.h
@@ -156,6 +156,7 @@
#include "glsl/GrGLSLProgramDataManager.h"
class GrInvariantOutput;
+class GrTextureStripAtlas;
/*
* The interpretation of the texture matrix depends on the sample mode. The
@@ -180,8 +181,6 @@
* determines the gradient value.
*/
- class GrTextureStripAtlas;
-
// Base class for Gr gradient effects
class GrGradientEffect : public GrFragmentProcessor {
public: