Revert "Update GrTextureStripAtlas for DDLs"

This reverts commit c042d41f2a.

Reason for revert: Leaking

Original change's description:
> 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>

TBR=egdaniel@google.com,robertphillips@google.com

Change-Id: I1c4b10ef5a66585c9d85ddfcab8bffcabdee2c19
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/145180
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Robert Phillips 2018-08-02 17:09:24 +00:00 committed by Skia Commit-Bot
parent e1b36fe3e5
commit 20ee171b98
11 changed files with 124 additions and 502 deletions

View File

@ -358,8 +358,6 @@ skia_gpu_sources = [
"$_src/gpu/effects/GrBicubicEffect.h", "$_src/gpu/effects/GrBicubicEffect.h",
"$_src/gpu/effects/GrBitmapTextGeoProc.cpp", "$_src/gpu/effects/GrBitmapTextGeoProc.cpp",
"$_src/gpu/effects/GrBitmapTextGeoProc.h", "$_src/gpu/effects/GrBitmapTextGeoProc.h",
"$_src/gpu/effects/GrDDLTextureStripAtlas.cpp",
"$_src/gpu/effects/GrDDLTextureStripAtlas.h",
"$_src/gpu/effects/GrDisableColorXP.cpp", "$_src/gpu/effects/GrDisableColorXP.cpp",
"$_src/gpu/effects/GrDisableColorXP.h", "$_src/gpu/effects/GrDisableColorXP.h",
"$_src/gpu/effects/GrDistanceFieldGeoProc.cpp", "$_src/gpu/effects/GrDistanceFieldGeoProc.cpp",

View File

@ -287,7 +287,6 @@ sk_sp<SkColorFilter> SkTable_ColorFilter::onMakeComposed(sk_sp<SkColorFilter> in
#include "GrContext.h" #include "GrContext.h"
#include "GrContextPriv.h" #include "GrContextPriv.h"
#include "GrFragmentProcessor.h" #include "GrFragmentProcessor.h"
#include "GrTexture.h"
#include "GrTextureStripAtlas.h" #include "GrTextureStripAtlas.h"
#include "SkGr.h" #include "SkGr.h"
#include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentProcessor.h"
@ -308,10 +307,6 @@ public:
std::unique_ptr<GrFragmentProcessor> clone() const override; std::unique_ptr<GrFragmentProcessor> clone() const override;
int peekBackingHeight() const {
return fTextureSampler.peekTexture()->height();
}
private: private:
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
@ -352,8 +347,8 @@ void GLColorTableEffect::onSetData(const GrGLSLProgramDataManager& pdm,
float rgbaYValues[4]; float rgbaYValues[4];
const ColorTableEffect& cte = proc.cast<ColorTableEffect>(); const ColorTableEffect& cte = proc.cast<ColorTableEffect>();
if (cte.atlas()) { if (cte.atlas()) {
SkScalar yDelta = 1.0f / cte.peekBackingHeight(); SkScalar yDelta = cte.atlas()->getNormalizedTexelHeight();
rgbaYValues[3] = cte.atlas()->rowToTextureY(cte.atlasRow()) * yDelta; rgbaYValues[3] = cte.atlas()->getYOffset(cte.atlasRow()) + SK_ScalarHalf * yDelta;
rgbaYValues[0] = rgbaYValues[3] + yDelta; rgbaYValues[0] = rgbaYValues[3] + yDelta;
rgbaYValues[1] = rgbaYValues[0] + yDelta; rgbaYValues[1] = rgbaYValues[0] + yDelta;
rgbaYValues[2] = rgbaYValues[1] + yDelta; rgbaYValues[2] = rgbaYValues[1] + yDelta;
@ -417,31 +412,26 @@ void GLColorTableEffect::emitCode(EmitArgs& args) {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
std::unique_ptr<GrFragmentProcessor> ColorTableEffect::Make(GrContext* context, std::unique_ptr<GrFragmentProcessor> ColorTableEffect::Make(GrContext* context,
const SkBitmap& bitmap) { const SkBitmap& bitmap) {
SkASSERT(kPremul_SkAlphaType == bitmap.alphaType());
SkASSERT(bitmap.isImmutable());
if (kUnknown_GrPixelConfig == SkColorType2GrPixelConfig(bitmap.colorType())) {
return nullptr;
}
GrTextureStripAtlas::Desc desc; GrTextureStripAtlas::Desc desc;
desc.fColorType = bitmap.colorType();
desc.fWidth = bitmap.width(); desc.fWidth = bitmap.width();
desc.fHeight = 128; desc.fHeight = 128;
desc.fRowHeight = bitmap.height(); desc.fRowHeight = bitmap.height();
desc.fConfig = SkColorType2GrPixelConfig(bitmap.colorType());
if (kUnknown_GrPixelConfig == desc.fConfig) {
return nullptr;
}
auto atlasManager = context->contextPriv().textureStripAtlasManager(); auto atlasManager = context->contextPriv().textureStripAtlasManager();
int row; sk_sp<GrTextureStripAtlas> atlas = atlasManager->refAtlas(desc);
sk_sp<GrTextureStripAtlas> atlas = atlasManager->addStrip(context, desc, bitmap, &row); int row = atlas->lockRow(context, bitmap);
if (!context->contextPriv().resourceProvider()) {
SkASSERT(atlas && row >= 0); // In DDL mode we should always be able to atlas
}
sk_sp<GrTextureProxy> proxy; sk_sp<GrTextureProxy> proxy;
if (-1 == row) { if (-1 == row) {
atlas = nullptr; atlas = nullptr;
SkASSERT(bitmap.isImmutable());
sk_sp<SkImage> srcImage = SkImage::MakeFromBitmap(bitmap); sk_sp<SkImage> srcImage = SkImage::MakeFromBitmap(bitmap);
if (!srcImage) { if (!srcImage) {
return nullptr; return nullptr;

View File

@ -25,7 +25,6 @@
#include "GrTexturePriv.h" #include "GrTexturePriv.h"
#include "GrTextureProxy.h" #include "GrTextureProxy.h"
#include "GrTextureProxyPriv.h" #include "GrTextureProxyPriv.h"
#include "GrTextureStripAtlas.h"
#include "GrTracing.h" #include "GrTracing.h"
#include "SkDeferredDisplayList.h" #include "SkDeferredDisplayList.h"
#include "SkSurface_Gpu.h" #include "SkSurface_Gpu.h"
@ -395,9 +394,6 @@ void GrDrawingManager::addOnFlushCallbackObject(GrOnFlushCallbackObject* onFlush
} }
void GrDrawingManager::moveOpListsToDDL(SkDeferredDisplayList* ddl) { void GrDrawingManager::moveOpListsToDDL(SkDeferredDisplayList* ddl) {
fContext->contextPriv().textureStripAtlasManager()->finish(
fContext->contextPriv().proxyProvider());
for (int i = 0; i < fOpLists.count(); ++i) { for (int i = 0; i < fOpLists.count(); ++i) {
// no opList should receive a new command after this // no opList should receive a new command after this
fOpLists[i]->makeClosed(*fContext->contextPriv().caps()); fOpLists[i]->makeClosed(*fContext->contextPriv().caps());

View File

@ -8,6 +8,8 @@
#ifndef GrTextureStripAtlas_DEFINED #ifndef GrTextureStripAtlas_DEFINED
#define GrTextureStripAtlas_DEFINED #define GrTextureStripAtlas_DEFINED
#include "effects/GrDynamicTextureStripAtlas.h"
#include "SkNoncopyable.h" #include "SkNoncopyable.h"
#include "SkOpts.h" #include "SkOpts.h"
#include "SkRefCnt.h" #include "SkRefCnt.h"
@ -18,100 +20,17 @@ class GrProxyProvider;
class GrTextureProxy; class GrTextureProxy;
class SkBitmap; 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 { class GrTextureStripAtlasManager {
public: public:
GrTextureStripAtlasManager() {} GrTextureStripAtlasManager() {}
~GrTextureStripAtlasManager(); ~GrTextureStripAtlasManager();
void abandon(); void abandon();
void finish(GrProxyProvider*);
/** /**
* Add a new texture strip to the atlas matching the descriptor. Upon failure, nullptr * Try to find an atlas with the required parameters, creates a new one if necessary
* will be returned and 'row' will be set to -1.
*/ */
sk_sp<GrTextureStripAtlas> addStrip(GrContext*, sk_sp<GrTextureStripAtlas> refAtlas(const GrTextureStripAtlas::Desc&);
const GrTextureStripAtlas::Desc&,
const SkBitmap&, int* row);
private: private:
void deleteAllAtlases(); void deleteAllAtlases();
@ -119,17 +38,21 @@ private:
// Hash table entry for atlases // Hash table entry for atlases
class AtlasEntry : public ::SkNoncopyable { class AtlasEntry : public ::SkNoncopyable {
public: public:
AtlasEntry(sk_sp<GrTextureStripAtlas> atlas) : fAtlas(std::move(atlas)) {} AtlasEntry(const GrTextureStripAtlas::Desc& desc, sk_sp<GrTextureStripAtlas> atlas)
: fDesc(desc)
, fAtlas(std::move(atlas)) {
}
~AtlasEntry() { } ~AtlasEntry() { }
// for SkTDynamicHash // for SkTDynamicHash
static const GrTextureStripAtlas::Desc& GetKey(const AtlasEntry& entry) { static const GrTextureStripAtlas::Desc& GetKey(const AtlasEntry& entry) {
return entry.fAtlas->fDesc; return entry.fDesc;
} }
static uint32_t Hash(const GrTextureStripAtlas::Desc& desc) { static uint32_t Hash(const GrTextureStripAtlas::Desc& desc) {
return SkOpts::hash(&desc, sizeof(GrTextureStripAtlas::Desc)); return SkOpts::hash(&desc, sizeof(GrTextureStripAtlas::Desc));
} }
const GrTextureStripAtlas::Desc fDesc;
sk_sp<GrTextureStripAtlas> fAtlas; sk_sp<GrTextureStripAtlas> fAtlas;
}; };

View File

@ -1,197 +0,0 @@
/*
* 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

View File

@ -1,89 +0,0 @@
/*
* 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

View File

@ -18,7 +18,7 @@
#define VALIDATE #define VALIDATE
#endif #endif
uint32_t GrDynamicTextureStripAtlas::CreateUniqueID() { uint32_t GrTextureStripAtlas::CreateUniqueID() {
static int32_t gUniqueID = SK_InvalidUniqueID; static int32_t gUniqueID = SK_InvalidUniqueID;
uint32_t id; uint32_t id;
// Loop in case our global wraps around, as we never want to return a 0. // Loop in case our global wraps around, as we never want to return a 0.
@ -28,30 +28,30 @@ uint32_t GrDynamicTextureStripAtlas::CreateUniqueID() {
return id; return id;
} }
GrDynamicTextureStripAtlas::GrDynamicTextureStripAtlas(const Desc& desc) GrTextureStripAtlas::GrTextureStripAtlas(const Desc& desc)
: INHERITED(desc) : fCacheKey(CreateUniqueID())
, fCacheKey(CreateUniqueID()) , fLockedRows(0)
, fLockedRows(0) , fDesc(desc)
, fNumRows(desc.fHeight / desc.fRowHeight) , fNumRows(desc.fHeight / desc.fRowHeight)
, fRows(new AtlasRow[fNumRows]) , fRows(new AtlasRow[fNumRows])
, fLRUFront(nullptr) , fLRUFront(nullptr)
, fLRUBack(nullptr) { , fLRUBack(nullptr) {
SkASSERT(fNumRows * fDesc.fRowHeight == fDesc.fHeight); SkASSERT(fNumRows * fDesc.fRowHeight == fDesc.fHeight);
this->initLRU(); this->initLRU();
fNormalizedYHeight = SK_Scalar1 / fDesc.fHeight; fNormalizedYHeight = SK_Scalar1 / fDesc.fHeight;
VALIDATE; VALIDATE;
} }
GrDynamicTextureStripAtlas::~GrDynamicTextureStripAtlas() { delete[] fRows; } GrTextureStripAtlas::~GrTextureStripAtlas() { delete[] fRows; }
void GrDynamicTextureStripAtlas::lockRow(int row) { void GrTextureStripAtlas::lockRow(int row) {
// This should only be called on a row that is already locked. // This should only be called on a row that is already locked.
SkASSERT(fRows[row].fLocks); SkASSERT(fRows[row].fLocks);
fRows[row].fLocks++; fRows[row].fLocks++;
++fLockedRows; ++fLockedRows;
} }
int GrDynamicTextureStripAtlas::addStrip(GrContext* context, const SkBitmap& bitmap) { int GrTextureStripAtlas::lockRow(GrContext* context, const SkBitmap& bitmap) {
VALIDATE; VALIDATE;
if (!context->contextPriv().resourceProvider()) { if (!context->contextPriv().resourceProvider()) {
@ -142,11 +142,11 @@ int GrDynamicTextureStripAtlas::addStrip(GrContext* context, const SkBitmap& bit
return rowNumber; return rowNumber;
} }
sk_sp<GrTextureProxy> GrDynamicTextureStripAtlas::asTextureProxyRef() const { sk_sp<GrTextureProxy> GrTextureStripAtlas::asTextureProxyRef() const {
return fTexContext->asTextureProxyRef(); return fTexContext->asTextureProxyRef();
} }
void GrDynamicTextureStripAtlas::unlockRow(int row) { void GrTextureStripAtlas::unlockRow(int row) {
VALIDATE; VALIDATE;
--fRows[row].fLocks; --fRows[row].fLocks;
--fLockedRows; --fLockedRows;
@ -160,13 +160,13 @@ void GrDynamicTextureStripAtlas::unlockRow(int row) {
VALIDATE; VALIDATE;
} }
GrDynamicTextureStripAtlas::AtlasRow* GrDynamicTextureStripAtlas::getLRU() { GrTextureStripAtlas::AtlasRow* GrTextureStripAtlas::getLRU() {
// Front is least-recently-used // Front is least-recently-used
AtlasRow* row = fLRUFront; AtlasRow* row = fLRUFront;
return row; return row;
} }
void GrDynamicTextureStripAtlas::lockTexture(GrContext* context) { void GrTextureStripAtlas::lockTexture(GrContext* context) {
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey key; GrUniqueKey key;
@ -179,12 +179,10 @@ void GrDynamicTextureStripAtlas::lockTexture(GrContext* context) {
sk_sp<GrTextureProxy> proxy = proxyProvider->findOrCreateProxyByUniqueKey( sk_sp<GrTextureProxy> proxy = proxyProvider->findOrCreateProxyByUniqueKey(
key, kTopLeft_GrSurfaceOrigin); key, kTopLeft_GrSurfaceOrigin);
if (!proxy) { if (!proxy) {
GrPixelConfig pixelConfig = SkColorType2GrPixelConfig(fDesc.fColorType);
GrSurfaceDesc texDesc; GrSurfaceDesc texDesc;
texDesc.fWidth = fDesc.fWidth; texDesc.fWidth = fDesc.fWidth;
texDesc.fHeight = fDesc.fHeight; texDesc.fHeight = fDesc.fHeight;
texDesc.fConfig = pixelConfig; texDesc.fConfig = fDesc.fConfig;
proxy = proxyProvider->createProxy(texDesc, kTopLeft_GrSurfaceOrigin, SkBackingFit::kExact, proxy = proxyProvider->createProxy(texDesc, kTopLeft_GrSurfaceOrigin, SkBackingFit::kExact,
SkBudgeted::kYes, GrInternalSurfaceFlags::kNoPendingIO); SkBudgeted::kYes, GrInternalSurfaceFlags::kNoPendingIO);
@ -202,12 +200,12 @@ void GrDynamicTextureStripAtlas::lockTexture(GrContext* context) {
fTexContext = context->contextPriv().makeWrappedSurfaceContext(std::move(proxy)); fTexContext = context->contextPriv().makeWrappedSurfaceContext(std::move(proxy));
} }
void GrDynamicTextureStripAtlas::unlockTexture() { void GrTextureStripAtlas::unlockTexture() {
SkASSERT(fTexContext && 0 == fLockedRows); SkASSERT(fTexContext && 0 == fLockedRows);
fTexContext.reset(); fTexContext.reset();
} }
void GrDynamicTextureStripAtlas::initLRU() { void GrTextureStripAtlas::initLRU() {
fLRUFront = nullptr; fLRUFront = nullptr;
fLRUBack = nullptr; fLRUBack = nullptr;
// Initially all the rows are in the LRU list // Initially all the rows are in the LRU list
@ -221,7 +219,7 @@ void GrDynamicTextureStripAtlas::initLRU() {
SkASSERT(nullptr == fLRUBack || nullptr == fLRUBack->fNext); SkASSERT(nullptr == fLRUBack || nullptr == fLRUBack->fNext);
} }
void GrDynamicTextureStripAtlas::appendLRU(AtlasRow* row) { void GrTextureStripAtlas::appendLRU(AtlasRow* row) {
SkASSERT(nullptr == row->fPrev && nullptr == row->fNext); SkASSERT(nullptr == row->fPrev && nullptr == row->fNext);
if (nullptr == fLRUFront && nullptr == fLRUBack) { if (nullptr == fLRUFront && nullptr == fLRUBack) {
fLRUFront = row; fLRUFront = row;
@ -233,7 +231,7 @@ void GrDynamicTextureStripAtlas::appendLRU(AtlasRow* row) {
} }
} }
void GrDynamicTextureStripAtlas::removeFromLRU(AtlasRow* row) { void GrTextureStripAtlas::removeFromLRU(AtlasRow* row) {
SkASSERT(row); SkASSERT(row);
if (row->fNext && row->fPrev) { if (row->fNext && row->fPrev) {
row->fPrev->fNext = row->fNext; row->fPrev->fNext = row->fNext;
@ -258,17 +256,19 @@ void GrDynamicTextureStripAtlas::removeFromLRU(AtlasRow* row) {
row->fPrev = nullptr; row->fPrev = nullptr;
} }
int GrDynamicTextureStripAtlas::searchByKey(uint32_t key) { int GrTextureStripAtlas::searchByKey(uint32_t key) {
AtlasRow target; AtlasRow target;
target.fKey = key; target.fKey = key;
return SkTSearch<const AtlasRow, KeyLess>((const AtlasRow**)fKeyTable.begin(), return SkTSearch<const AtlasRow,
fKeyTable.count(), GrTextureStripAtlas::KeyLess>((const AtlasRow**)fKeyTable.begin(),
&target, fKeyTable.count(),
sizeof(AtlasRow*)); &target,
sizeof(AtlasRow*));
} }
#ifdef SK_DEBUG #ifdef SK_DEBUG
void GrDynamicTextureStripAtlas::validate() { void GrTextureStripAtlas::validate() {
// Our key table should be sorted // Our key table should be sorted
uint32_t prev = 1 > fKeyTable.count() ? 0 : fKeyTable[0]->fKey; uint32_t prev = 1 > fKeyTable.count() ? 0 : fKeyTable[0]->fKey;
for (int i = 1; i < fKeyTable.count(); ++i) { for (int i = 1; i < fKeyTable.count(); ++i) {

View File

@ -8,35 +8,39 @@
#ifndef GrDynamicTextureStripAtlas_DEFINED #ifndef GrDynamicTextureStripAtlas_DEFINED
#define GrDynamicTextureStripAtlas_DEFINED #define GrDynamicTextureStripAtlas_DEFINED
#include "GrTextureStripAtlas.h" #include "GrTypesPriv.h"
#include "SkNoncopyable.h"
#include "SkRefCnt.h"
#include "SkScalar.h"
#include "SkTDArray.h" #include "SkTDArray.h"
class GrContext;
class GrSurfaceContext; class GrSurfaceContext;
class GrTextureProxy;
class SkBitmap;
/** /**
* Maintains a single large texture whose rows store many textures of a small fixed height, * 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. * stored in rows across the x-axis such that we can safely wrap/repeat them horizontally.
*/ */
class GrDynamicTextureStripAtlas final : public GrTextureStripAtlas { class GrTextureStripAtlas : public SkRefCnt {
public: public:
~GrDynamicTextureStripAtlas() final;
/** /**
* This is intended to be used when cloning a processor that already holds a lock. It is * Descriptor struct which we'll use as a hash table key
* assumed that the row already has at least one lock.
*/ */
void lockRow(int row) final; struct Desc {
void unlockRow(int row) final; 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));
}
};
sk_sp<GrTextureProxy> asTextureProxyRef() const final; ~GrTextureStripAtlas();
private:
friend class GrTextureStripAtlasManager; // for ctor
/**
* Only the GrTextureStripAtlasManager is allowed to create GrTextureStripAtlases
*/
GrDynamicTextureStripAtlas(const Desc& desc);
/** /**
* Add a texture to the atlas * Add a texture to the atlas
@ -44,9 +48,38 @@ private:
* @return The row index we inserted into, or -1 if we failed to find an open row. The caller * @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. * is responsible for calling unlockRow() with this row index when it's done with it.
*/ */
int addStrip(GrContext*, const SkBitmap&) final; int lockRow(GrContext*, const SkBitmap&);
void finish(GrProxyProvider*) final { SkASSERT(0); } // this is only called in DDL mode /**
* 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
static uint32_t CreateUniqueID(); static uint32_t CreateUniqueID();
@ -68,6 +101,11 @@ private:
AtlasRow* fPrev; AtlasRow* fPrev;
}; };
/**
* Only the GrTextureStripAtlasManager is allowed to create GrTextureStripAtlases
*/
GrTextureStripAtlas(const Desc& desc);
void lockTexture(GrContext*); void lockTexture(GrContext*);
void unlockTexture(); void unlockTexture();
@ -109,6 +147,7 @@ private:
// Total locks on all rows (when this reaches zero, we can unlock our texture) // Total locks on all rows (when this reaches zero, we can unlock our texture)
int32_t fLockedRows; int32_t fLockedRows;
const Desc fDesc;
const uint16_t fNumRows; const uint16_t fNumRows;
sk_sp<GrSurfaceContext> fTexContext; sk_sp<GrSurfaceContext> fTexContext;
@ -126,8 +165,6 @@ private:
// A list of pointers to AtlasRows that currently contain cached images, sorted by key // A list of pointers to AtlasRows that currently contain cached images, sorted by key
SkTDArray<AtlasRow*> fKeyTable; SkTDArray<AtlasRow*> fKeyTable;
typedef GrTextureStripAtlas INHERITED;
}; };
#endif #endif

View File

@ -8,10 +8,10 @@
#include "GrTextureStripAtlas.h" #include "GrTextureStripAtlas.h"
#include "GrContext.h" #include "GrContext.h"
#include "GrContextPriv.h" #include "GrContextPriv.h"
#include "GrDDLTextureStripAtlas.h"
#include "GrDynamicTextureStripAtlas.h" #include "GrDynamicTextureStripAtlas.h"
#include "SkBitmap.h" #include "SkBitmap.h"
////////////////////////////////////////////////////////////////////////////////
GrTextureStripAtlasManager::~GrTextureStripAtlasManager() { GrTextureStripAtlasManager::~GrTextureStripAtlasManager() {
this->deleteAllAtlases(); this->deleteAllAtlases();
} }
@ -30,41 +30,15 @@ void GrTextureStripAtlasManager::abandon() {
this->deleteAllAtlases(); this->deleteAllAtlases();
} }
void GrTextureStripAtlasManager::finish(GrProxyProvider* proxyProvider) { sk_sp<GrTextureStripAtlas> GrTextureStripAtlasManager::refAtlas(
for (AtlasHash::Iter iter(&fAtlasCache); !iter.done(); ++iter) { const GrTextureStripAtlas::Desc& desc) {
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); AtlasEntry* entry = fAtlasCache.find(desc);
if (!entry) { if (!entry) {
sk_sp<GrTextureStripAtlas> atlas; // 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)));
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); fAtlasCache.add(entry);
} }
*row = entry->fAtlas->addStrip(context, bitmap);
if (*row < 0) {
return nullptr;
}
return entry->fAtlas; return entry->fAtlas;
} }

View File

@ -919,7 +919,6 @@ SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
#include "GrContext.h" #include "GrContext.h"
#include "GrContextPriv.h" #include "GrContextPriv.h"
#include "GrShaderCaps.h" #include "GrShaderCaps.h"
#include "GrTexture.h"
#include "GrTextureStripAtlas.h" #include "GrTextureStripAtlas.h"
#include "gl/GrGLContext.h" #include "gl/GrGLContext.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h" #include "glsl/GrGLSLFragmentShaderBuilder.h"
@ -967,8 +966,7 @@ void GrGradientEffect::GLSLProcessor::onSetData(const GrGLSLProgramDataManager&
break; break;
case GrGradientEffect::InterpolationStrategy::kTexture: case GrGradientEffect::InterpolationStrategy::kTexture:
if (e.fYCoord != fCachedYCoord) { if (e.fYCoord != fCachedYCoord) {
SkScalar yDelta = 1.0f / e.fTextureSampler.peekTexture()->height(); pdman.set1f(fFSYUni, e.fYCoord);
pdman.set1f(fFSYUni, e.fYCoord * yDelta);
fCachedYCoord = e.fYCoord; fCachedYCoord = e.fYCoord;
} }
break; break;
@ -1248,8 +1246,6 @@ GrGradientEffect::GrGradientEffect(ClassID classID, const CreateArgs& args, bool
SkBitmap bitmap; SkBitmap bitmap;
shader.getGradientTableBitmap(xformedColors.fColors, &bitmap, colorType); shader.getGradientTableBitmap(xformedColors.fColors, &bitmap, colorType);
SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width())); SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width()));
SkASSERT(kPremul_SkAlphaType == bitmap.alphaType());
SkASSERT(bitmap.isImmutable());
auto atlasManager = args.fContext->contextPriv().textureStripAtlasManager(); auto atlasManager = args.fContext->contextPriv().textureStripAtlasManager();
@ -1257,24 +1253,18 @@ GrGradientEffect::GrGradientEffect(ClassID classID, const CreateArgs& args, bool
desc.fWidth = bitmap.width(); desc.fWidth = bitmap.width();
desc.fHeight = 32; desc.fHeight = 32;
desc.fRowHeight = bitmap.height(); // always 1 here desc.fRowHeight = bitmap.height(); // always 1 here
desc.fColorType = bitmap.colorType(); desc.fConfig = SkColorType2GrPixelConfig(bitmap.colorType());
fAtlas = atlasManager->refAtlas(desc);
int row; SkASSERT(fAtlas);
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 // We always filter the gradient table. Each table is one row of a texture, always
// y-clamp. // y-clamp.
GrSamplerState samplerState(args.fWrapMode, GrSamplerState::Filter::kBilerp); GrSamplerState samplerState(args.fWrapMode, GrSamplerState::Filter::kBilerp);
fRow = fAtlas->lockRow(args.fContext, bitmap);
if (-1 != fRow) { if (-1 != fRow) {
SkASSERT(fAtlas); fYCoord = fAtlas->getYOffset(fRow)+SK_ScalarHalf*fAtlas->getNormalizedTexelHeight();
// This is 1/2 places where auto-normalization is disabled
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); fCoordTransform.reset(*args.fMatrix, fAtlas->asTextureProxyRef().get(), false);
fTextureSampler.reset(fAtlas->asTextureProxyRef(), samplerState); fTextureSampler.reset(fAtlas->asTextureProxyRef(), samplerState);
} else { } else {
@ -1286,6 +1276,7 @@ GrGradientEffect::GrGradientEffect(ClassID classID, const CreateArgs& args, bool
// that GrMakeCachedImageProxy is sufficient (i.e., it won't need to be // that GrMakeCachedImageProxy is sufficient (i.e., it won't need to be
// extracted to a subset or mipmapped). // extracted to a subset or mipmapped).
SkASSERT(bitmap.isImmutable());
sk_sp<SkImage> srcImage = SkImage::MakeFromBitmap(bitmap); sk_sp<SkImage> srcImage = SkImage::MakeFromBitmap(bitmap);
if (!srcImage) { if (!srcImage) {
return; return;
@ -1298,13 +1289,11 @@ GrGradientEffect::GrGradientEffect(ClassID classID, const CreateArgs& args, bool
SkDebugf("Gradient won't draw. Could not create texture."); SkDebugf("Gradient won't draw. Could not create texture.");
return; return;
} }
// This is 2/2 places where auto-normalization is disabled because the graient T is 0..1 // This is 2/2 places where auto-normalization is disabled
fCoordTransform.reset(*args.fMatrix, proxy.get(), false); fCoordTransform.reset(*args.fMatrix, proxy.get(), false);
fTextureSampler.reset(std::move(proxy), samplerState); fTextureSampler.reset(std::move(proxy), samplerState);
SkASSERT(1 == bitmap.height());
fYCoord = SK_ScalarHalf; fYCoord = SK_ScalarHalf;
} }
this->setTextureSamplerCnt(1); this->setTextureSamplerCnt(1);
} }

View File

@ -156,7 +156,6 @@ struct SkColor4fXformer {
#include "glsl/GrGLSLProgramDataManager.h" #include "glsl/GrGLSLProgramDataManager.h"
class GrInvariantOutput; class GrInvariantOutput;
class GrTextureStripAtlas;
/* /*
* The interpretation of the texture matrix depends on the sample mode. The * The interpretation of the texture matrix depends on the sample mode. The
@ -181,6 +180,8 @@ class GrTextureStripAtlas;
* determines the gradient value. * determines the gradient value.
*/ */
class GrTextureStripAtlas;
// Base class for Gr gradient effects // Base class for Gr gradient effects
class GrGradientEffect : public GrFragmentProcessor { class GrGradientEffect : public GrFragmentProcessor {
public: public: