Skip to content

Commit

Permalink
Update GrTextureStripAtlas for DDLs (take 2)
Browse files Browse the repository at this point in the history
Change-Id: I4a3f71ffe4a14785e7befddc378929cf4dcf7d8e
Reviewed-on: https://skia-review.googlesource.com/145150
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
  • Loading branch information
rphilli authored and Skia Commit-Bot committed Aug 3, 2018
1 parent aea785b commit 057ad70
Show file tree
Hide file tree
Showing 11 changed files with 481 additions and 113 deletions.
2 changes: 2 additions & 0 deletions gn/gpu.gni
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,8 @@ skia_gpu_sources = [
"$_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",
Expand Down
1 change: 1 addition & 0 deletions src/effects/SkTableColorFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ void GLColorTableEffect::emitCode(EmitArgs& args) {
///////////////////////////////////////////////////////////////////////////////
std::unique_ptr<GrFragmentProcessor> ColorTableEffect::Make(GrContext* context,
const SkBitmap& bitmap) {
SkASSERT(kPremul_SkAlphaType == bitmap.alphaType());
SkASSERT(bitmap.isImmutable());

if (kUnknown_GrPixelConfig == SkColorType2GrPixelConfig(bitmap.colorType())) {
Expand Down
4 changes: 4 additions & 0 deletions src/gpu/GrDrawingManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -394,6 +395,9 @@ void GrDrawingManager::addOnFlushCallbackObject(GrOnFlushCallbackObject* onFlush
}

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());
Expand Down
97 changes: 87 additions & 10 deletions src/gpu/GrTextureStripAtlas.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
#ifndef GrTextureStripAtlas_DEFINED
#define GrTextureStripAtlas_DEFINED

#include "effects/GrDynamicTextureStripAtlas.h"

#include "SkNoncopyable.h"
#include "SkOpts.h"
#include "SkRefCnt.h"
Expand All @@ -20,39 +18,118 @@ class GrProxyProvider;
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();

// 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;
};

Expand Down
197 changes: 197 additions & 0 deletions src/gpu/effects/GrDDLTextureStripAtlas.cpp
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 057ad70

Please sign in to comment.