Cache image behind picture-shader
PictureShader = picture + tiling + depth/colorspace + filtering [+ scale] Today we cache the imageshader that is used to rendering. However, the key for that cache is the pictureshader's ID itself... which means if we have several, all using the same picture (but maybe diff tiling) we would create dup cache entries. Idea: 1. only cache the image (rastered picture), not an imageShader 2. key the cache on the picture's ID, not the shader's Several implications of this: 1. Should get more cache reuse, since we don't care about the shader's ID (which is just wrapping a picture+tiling, etc.) 2. We also eliminate the indirection of creating a PictureImage. Instead we're creating real (pixel) images, and caching those. This removes one extra layer of "cache". Idea: when we cache something for pict Change-Id: I51cf4e9bff3c91ce1872876597d3d565039d8c7a Reviewed-on: https://skia-review.googlesource.com/c/skia/+/377844 Commit-Queue: Mike Reed <reed@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
3316796acb
commit
ff83dda8cd
@ -38,6 +38,7 @@ class SkWStream;
|
||||
*/
|
||||
class SK_API SkPicture : public SkRefCnt {
|
||||
public:
|
||||
~SkPicture() override;
|
||||
|
||||
/** Recreates SkPicture that was serialized into a stream. Returns constructed SkPicture
|
||||
if successful; otherwise, returns nullptr. Fails if data does not permit
|
||||
@ -275,6 +276,7 @@ private:
|
||||
class SkPictureData* backport() const;
|
||||
|
||||
uint32_t fUniqueID;
|
||||
mutable std::atomic<bool> fAddedToCache{false};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "src/core/SkPicturePlayback.h"
|
||||
#include "src/core/SkPicturePriv.h"
|
||||
#include "src/core/SkPictureRecord.h"
|
||||
#include "src/core/SkResourceCache.h"
|
||||
#include <atomic>
|
||||
|
||||
// When we read/write the SkPictInfo via a stream, we have a sentinel byte right after the info.
|
||||
@ -41,6 +42,12 @@ SkPicture::SkPicture() {
|
||||
} while (fUniqueID == 0);
|
||||
}
|
||||
|
||||
SkPicture::~SkPicture() {
|
||||
if (fAddedToCache.load()) {
|
||||
SkResourceCache::PostPurgeSharedID(SkPicturePriv::MakeSharedID(fUniqueID));
|
||||
}
|
||||
}
|
||||
|
||||
static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
|
||||
|
||||
SkPictInfo SkPicture::createHeader() const {
|
||||
|
@ -35,6 +35,15 @@ public:
|
||||
return picture->asSkBigPicture();
|
||||
}
|
||||
|
||||
static uint64_t MakeSharedID(uint32_t pictureID) {
|
||||
uint64_t sharedID = SkSetFourByteTag('p', 'i', 'c', 't');
|
||||
return (sharedID << 32) | pictureID;
|
||||
}
|
||||
|
||||
static void AddedToCache(const SkPicture* pic) {
|
||||
pic->fAddedToCache.store(true);
|
||||
}
|
||||
|
||||
// V35: Store SkRect (rather then width & height) in header
|
||||
// V36: Remove (obsolete) alphatype from SkColorTable
|
||||
// V37: Added shadow only option to SkDropShadowImageFilter (last version to record CLEAR)
|
||||
|
@ -31,8 +31,14 @@
|
||||
#include "src/gpu/GrFragmentProcessor.h"
|
||||
#include "src/gpu/GrRecordingContextPriv.h"
|
||||
#include "src/gpu/SkGr.h"
|
||||
#include "src/gpu/effects/GrTextureEffect.h"
|
||||
#include "src/image/SkImage_Base.h"
|
||||
#endif
|
||||
|
||||
#ifdef SK_SUPPORT_LEGACY_PICTURESHADER_MATH
|
||||
#include "src/shaders/SkPictureShader.cpp.legacy"
|
||||
#else
|
||||
|
||||
sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode filter,
|
||||
const SkMatrix* localMatrix, const SkRect* tile) const {
|
||||
if (localMatrix && !localMatrix->invert(nullptr)) {
|
||||
@ -57,80 +63,66 @@ sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy,
|
||||
}
|
||||
|
||||
namespace {
|
||||
static unsigned gBitmapShaderKeyNamespaceLabel;
|
||||
static unsigned gImageFromPictureKeyNamespaceLabel;
|
||||
|
||||
struct BitmapShaderKey : public SkResourceCache::Key {
|
||||
struct ImageFromPictureKey : public SkResourceCache::Key {
|
||||
public:
|
||||
BitmapShaderKey(SkColorSpace* colorSpace,
|
||||
SkImage::BitDepth bitDepth,
|
||||
uint32_t shaderID,
|
||||
const SkSize& scale)
|
||||
ImageFromPictureKey(SkColorSpace* colorSpace, SkColorType colorType,
|
||||
uint32_t pictureID, const SkRect& subset,
|
||||
SkSize scale)
|
||||
: fColorSpaceXYZHash(colorSpace->toXYZD50Hash())
|
||||
, fColorSpaceTransferFnHash(colorSpace->transferFnHash())
|
||||
, fBitDepth(bitDepth)
|
||||
, fScale(scale) {
|
||||
|
||||
, fColorType(static_cast<uint32_t>(colorType))
|
||||
, fSubset(subset)
|
||||
, fScale(scale)
|
||||
{
|
||||
static const size_t keySize = sizeof(fColorSpaceXYZHash) +
|
||||
sizeof(fColorSpaceTransferFnHash) +
|
||||
sizeof(fBitDepth) +
|
||||
sizeof(fColorType) +
|
||||
sizeof(fSubset) +
|
||||
sizeof(fScale);
|
||||
// This better be packed.
|
||||
SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fColorSpaceXYZHash) == keySize);
|
||||
this->init(&gBitmapShaderKeyNamespaceLabel, MakeSharedID(shaderID), keySize);
|
||||
}
|
||||
|
||||
static uint64_t MakeSharedID(uint32_t shaderID) {
|
||||
uint64_t sharedID = SkSetFourByteTag('p', 's', 'd', 'r');
|
||||
return (sharedID << 32) | shaderID;
|
||||
this->init(&gImageFromPictureKeyNamespaceLabel,
|
||||
SkPicturePriv::MakeSharedID(pictureID),
|
||||
keySize);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t fColorSpaceXYZHash;
|
||||
uint32_t fColorSpaceTransferFnHash;
|
||||
SkImage::BitDepth fBitDepth;
|
||||
SkSize fScale;
|
||||
uint32_t fColorSpaceXYZHash;
|
||||
uint32_t fColorSpaceTransferFnHash;
|
||||
uint32_t fColorType;
|
||||
SkRect fSubset;
|
||||
SkSize fScale;
|
||||
|
||||
SkDEBUGCODE(uint32_t fEndOfStruct;)
|
||||
};
|
||||
|
||||
struct BitmapShaderRec : public SkResourceCache::Rec {
|
||||
BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader)
|
||||
struct ImageFromPictureRec : public SkResourceCache::Rec {
|
||||
ImageFromPictureRec(const ImageFromPictureKey& key, sk_sp<SkImage> image)
|
||||
: fKey(key)
|
||||
, fShader(SkRef(tileShader)) {}
|
||||
, fImage(std::move(image)) {}
|
||||
|
||||
BitmapShaderKey fKey;
|
||||
sk_sp<SkShader> fShader;
|
||||
ImageFromPictureKey fKey;
|
||||
sk_sp<SkImage> fImage;
|
||||
|
||||
const Key& getKey() const override { return fKey; }
|
||||
size_t bytesUsed() const override {
|
||||
// Just the record overhead -- the actual pixels are accounted by SkImage_Lazy.
|
||||
return sizeof(fKey) + sizeof(SkImageShader);
|
||||
return sizeof(fKey) + (size_t)fImage->width() * fImage->height() * 4;
|
||||
}
|
||||
const char* getCategory() const override { return "bitmap-shader"; }
|
||||
SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
|
||||
|
||||
static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
|
||||
const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec);
|
||||
sk_sp<SkShader>* result = reinterpret_cast<sk_sp<SkShader>*>(contextShader);
|
||||
const ImageFromPictureRec& rec = static_cast<const ImageFromPictureRec&>(baseRec);
|
||||
sk_sp<SkImage>* result = reinterpret_cast<sk_sp<SkImage>*>(contextShader);
|
||||
|
||||
*result = rec.fShader;
|
||||
|
||||
// The bitmap shader is backed by an image generator, thus it can always re-generate its
|
||||
// pixels if discarded.
|
||||
*result = rec.fImage;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t next_id() {
|
||||
static std::atomic<uint32_t> nextID{1};
|
||||
|
||||
uint32_t id;
|
||||
do {
|
||||
id = nextID.fetch_add(1, std::memory_order_relaxed);
|
||||
} while (id == SK_InvalidGenID);
|
||||
return id;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy,
|
||||
@ -140,15 +132,7 @@ SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, SkTileMode tmx, SkTil
|
||||
, fTile(tile ? *tile : fPicture->cullRect())
|
||||
, fTmx(tmx)
|
||||
, fTmy(tmy)
|
||||
, fFilter(filter)
|
||||
, fUniqueID(next_id())
|
||||
, fAddedToCache(false) {}
|
||||
|
||||
SkPictureShader::~SkPictureShader() {
|
||||
if (fAddedToCache.load()) {
|
||||
SkResourceCache::PostPurgeSharedID(BitmapShaderKey::MakeSharedID(fUniqueID));
|
||||
}
|
||||
}
|
||||
, fFilter(filter) {}
|
||||
|
||||
sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy,
|
||||
FilterEnum filter, const SkMatrix* lm, const SkRect* tile) {
|
||||
@ -208,101 +192,139 @@ static SkFilterMode quality_to_filter(SkFilterQuality quality) {
|
||||
: SkFilterMode::kLinear;
|
||||
}
|
||||
|
||||
static sk_sp<SkColorSpace> ref_or_srgb(SkColorSpace* cs) {
|
||||
return cs ? sk_ref_sp(cs) : SkColorSpace::MakeSRGB();
|
||||
}
|
||||
|
||||
struct CachedImageInfo {
|
||||
bool success;
|
||||
SkSize tileScale;
|
||||
SkMatrix matrixForDraw;
|
||||
SkImageInfo imageInfo;
|
||||
|
||||
static CachedImageInfo Make(const SkRect& bounds,
|
||||
const SkMatrix& viewMatrix,
|
||||
SkTCopyOnFirstWrite<SkMatrix>* localMatrix, // in/out
|
||||
SkColorType dstColorType,
|
||||
SkColorSpace* dstColorSpace,
|
||||
const int maxTextureSize) {
|
||||
const SkMatrix m = SkMatrix::Concat(viewMatrix, **localMatrix);
|
||||
|
||||
const SkSize scaledSize = [&]() {
|
||||
SkSize size;
|
||||
// Use a rotation-invariant scale
|
||||
if (!m.decomposeScale(&size, nullptr)) {
|
||||
size = {1, 1};
|
||||
}
|
||||
size.fWidth *= bounds.width();
|
||||
size.fHeight *= bounds.height();
|
||||
|
||||
// Clamp the tile size to about 4M pixels
|
||||
static const SkScalar kMaxTileArea = 2048 * 2048;
|
||||
SkScalar tileArea = size.width() * size.height();
|
||||
if (tileArea > kMaxTileArea) {
|
||||
SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
|
||||
size.set(size.width() * clampScale, size.height() * clampScale);
|
||||
}
|
||||
|
||||
// Scale down the tile size if larger than maxTextureSize for GPU Path
|
||||
// or it should fail on create texture
|
||||
if (maxTextureSize) {
|
||||
if (size.width() > maxTextureSize || size.height() > maxTextureSize) {
|
||||
SkScalar downScale = maxTextureSize / std::max(size.width(),
|
||||
size.height());
|
||||
size.set(SkScalarFloorToScalar(size.width() * downScale),
|
||||
SkScalarFloorToScalar(size.height() * downScale));
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}();
|
||||
|
||||
const SkISize tileSize = scaledSize.toCeil();
|
||||
if (tileSize.isEmpty()) {
|
||||
return {false, {}, {}, {}};
|
||||
}
|
||||
|
||||
const SkSize tileScale = {
|
||||
tileSize.width() / bounds.width(), tileSize.height() / bounds.height()
|
||||
};
|
||||
auto imgCS = ref_or_srgb(dstColorSpace);
|
||||
const SkColorType imgCT = SkColorTypeMaxBitsPerChannel(dstColorType) <= 8
|
||||
? kRGBA_8888_SkColorType
|
||||
: kRGBA_F16Norm_SkColorType;
|
||||
|
||||
if (tileScale.width() != 1 || tileScale.height() != 1) {
|
||||
localMatrix->writable()->preScale(1 / tileScale.width(), 1 / tileScale.height());
|
||||
}
|
||||
|
||||
return {
|
||||
true,
|
||||
tileScale,
|
||||
SkMatrix::RectToRect(bounds, SkRect::MakeIWH(tileSize.width(), tileSize.height())),
|
||||
SkImageInfo::Make(tileSize.width(), tileSize.height(),
|
||||
imgCT, kPremul_SkAlphaType, imgCS),
|
||||
};
|
||||
}
|
||||
|
||||
sk_sp<SkImage> makeImage(sk_sp<SkSurface> surf, const SkPicture* pict) const {
|
||||
if (!surf) {
|
||||
return nullptr;
|
||||
}
|
||||
auto canvas = surf->getCanvas();
|
||||
canvas->concat(matrixForDraw);
|
||||
canvas->drawPicture(pict);
|
||||
return surf->makeImageSnapshot();
|
||||
}
|
||||
};
|
||||
|
||||
// Returns a cached image shader, which wraps a single picture tile at the given
|
||||
// CTM/local matrix. Also adjusts the local matrix for tile scaling.
|
||||
sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix,
|
||||
SkTCopyOnFirstWrite<SkMatrix>* localMatrix,
|
||||
SkColorType dstColorType,
|
||||
SkColorSpace* dstColorSpace,
|
||||
SkFilterMode paintFilter,
|
||||
const int maxTextureSize) const {
|
||||
SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
|
||||
|
||||
const SkMatrix m = SkMatrix::Concat(viewMatrix, **localMatrix);
|
||||
|
||||
// Use a rotation-invariant scale
|
||||
SkSize scaledSize;
|
||||
if (!m.decomposeScale(&scaledSize, nullptr)) {
|
||||
scaledSize = {1, 1};
|
||||
}
|
||||
scaledSize.fWidth *= fTile.width();
|
||||
scaledSize.fHeight *= fTile.height();
|
||||
|
||||
// Clamp the tile size to about 4M pixels
|
||||
static const SkScalar kMaxTileArea = 2048 * 2048;
|
||||
SkScalar tileArea = scaledSize.width() * scaledSize.height();
|
||||
if (tileArea > kMaxTileArea) {
|
||||
SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
|
||||
scaledSize.set(scaledSize.width() * clampScale,
|
||||
scaledSize.height() * clampScale);
|
||||
}
|
||||
#if SK_SUPPORT_GPU
|
||||
// Scale down the tile size if larger than maxTextureSize for GPU Path or it should fail on create texture
|
||||
if (maxTextureSize) {
|
||||
if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
|
||||
SkScalar downScale = maxTextureSize / std::max(scaledSize.width(), scaledSize.height());
|
||||
scaledSize.set(SkScalarFloorToScalar(scaledSize.width() * downScale),
|
||||
SkScalarFloorToScalar(scaledSize.height() * downScale));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const SkISize tileSize = scaledSize.toCeil();
|
||||
if (tileSize.isEmpty()) {
|
||||
sk_sp<SkShader> SkPictureShader::rasterShader(const SkMatrix& viewMatrix,
|
||||
SkTCopyOnFirstWrite<SkMatrix>* localMatrix,
|
||||
SkColorType dstColorType,
|
||||
SkColorSpace* dstColorSpace,
|
||||
SkFilterMode paintFilter) const {
|
||||
const int maxTextureSize_NotUsedForCPU = 0;
|
||||
CachedImageInfo info = CachedImageInfo::Make(fTile, viewMatrix, localMatrix,
|
||||
dstColorType, dstColorSpace,
|
||||
maxTextureSize_NotUsedForCPU);
|
||||
if (!info.success) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The actual scale, compensating for rounding & clamping.
|
||||
const SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
|
||||
SkIntToScalar(tileSize.height()) / fTile.height());
|
||||
ImageFromPictureKey key(info.imageInfo.colorSpace(), info.imageInfo.colorType(),
|
||||
fPicture->uniqueID(), fTile, info.tileScale);
|
||||
|
||||
|
||||
sk_sp<SkColorSpace> imgCS = dstColorSpace ? sk_ref_sp(dstColorSpace): SkColorSpace::MakeSRGB();
|
||||
SkImage::BitDepth bitDepth = SkColorTypeMaxBitsPerChannel(dstColorType) >= 16
|
||||
? SkImage::BitDepth::kF16
|
||||
: SkImage::BitDepth::kU8;
|
||||
|
||||
BitmapShaderKey key(imgCS.get(), bitDepth, fUniqueID, tileScale);
|
||||
|
||||
sk_sp<SkShader> tileShader;
|
||||
if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) {
|
||||
SkMatrix tileMatrix = SkMatrix::RectToRect(fTile, SkRect::MakeIWH(tileSize.width(),
|
||||
tileSize.height()));
|
||||
|
||||
sk_sp<SkImage> tileImage = SkImage::MakeFromPicture(fPicture, tileSize, &tileMatrix,
|
||||
nullptr, bitDepth, std::move(imgCS));
|
||||
if (!tileImage) {
|
||||
sk_sp<SkImage> image;
|
||||
if (!SkResourceCache::Find(key, ImageFromPictureRec::Visitor, &image)) {
|
||||
image = info.makeImage(SkSurface::MakeRaster(info.imageInfo), fPicture.get());
|
||||
if (!image) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkFilterMode filter;
|
||||
if (fFilter == kInheritFromPaint) {
|
||||
filter = paintFilter;
|
||||
} else {
|
||||
filter = (SkFilterMode)fFilter;
|
||||
}
|
||||
tileShader = tileImage->makeShader(fTmx, fTmy, SkSamplingOptions(filter), nullptr);
|
||||
|
||||
SkResourceCache::Add(new BitmapShaderRec(key, tileShader.get()));
|
||||
fAddedToCache.store(true);
|
||||
SkResourceCache::Add(new ImageFromPictureRec(key, image));
|
||||
SkPicturePriv::AddedToCache(fPicture.get());
|
||||
}
|
||||
return this->makeShader(image.get(), paintFilter);
|
||||
}
|
||||
|
||||
if (tileScale.width() != 1 || tileScale.height() != 1) {
|
||||
localMatrix->writable()->preScale(1 / tileScale.width(), 1 / tileScale.height());
|
||||
sk_sp<SkShader> SkPictureShader::makeShader(const SkImage* image, SkFilterMode paintFilter) const {
|
||||
SkFilterMode filter;
|
||||
if (fFilter == kInheritFromPaint) {
|
||||
filter = paintFilter;
|
||||
} else {
|
||||
filter = (SkFilterMode)fFilter;
|
||||
}
|
||||
|
||||
return tileShader;
|
||||
return image->makeShader(fTmx, fTmy, SkSamplingOptions(filter), nullptr);
|
||||
}
|
||||
|
||||
bool SkPictureShader::onAppendStages(const SkStageRec& rec) const {
|
||||
auto lm = this->totalLocalMatrix(rec.fLocalM);
|
||||
|
||||
// Keep bitmapShader alive by using alloc instead of stack memory
|
||||
auto& bitmapShader = *rec.fAlloc->make<sk_sp<SkShader>>();
|
||||
bitmapShader = this->refBitmapShader(rec.fMatrixProvider.localToDevice(), &lm,
|
||||
rec.fDstColorType, rec.fDstCS,
|
||||
quality_to_filter(rec.fPaint.getFilterQuality()));
|
||||
|
||||
bitmapShader = this->rasterShader(rec.fMatrixProvider.localToDevice(), &lm,
|
||||
rec.fDstColorType, rec.fDstCS,
|
||||
quality_to_filter(rec.fPaint.getFilterQuality()));
|
||||
if (!bitmapShader) {
|
||||
return false;
|
||||
}
|
||||
@ -322,9 +344,9 @@ skvm::Color SkPictureShader::onProgram(skvm::Builder* p,
|
||||
|
||||
// Keep bitmapShader alive by using alloc instead of stack memory
|
||||
auto& bitmapShader = *alloc->make<sk_sp<SkShader>>();
|
||||
bitmapShader = this->refBitmapShader(matrices.localToDevice(), &lm,
|
||||
dst.colorType(), dst.colorSpace(),
|
||||
quality_to_filter(quality));
|
||||
bitmapShader = this->rasterShader(matrices.localToDevice(), &lm,
|
||||
dst.colorType(), dst.colorSpace(),
|
||||
quality_to_filter(quality));
|
||||
if (!bitmapShader) {
|
||||
return {};
|
||||
}
|
||||
@ -341,9 +363,9 @@ skvm::Color SkPictureShader::onProgram(skvm::Builder* p,
|
||||
SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc)
|
||||
const {
|
||||
auto lm = this->totalLocalMatrix(rec.fLocalMatrix);
|
||||
sk_sp<SkShader> bitmapShader = this->refBitmapShader(*rec.fMatrix, &lm, rec.fDstColorType,
|
||||
rec.fDstColorSpace,
|
||||
sampling_to_filter(rec.fPaintSampling));
|
||||
sk_sp<SkShader> bitmapShader = this->rasterShader(*rec.fMatrix, &lm, rec.fDstColorType,
|
||||
rec.fDstColorSpace,
|
||||
sampling_to_filter(rec.fPaintSampling));
|
||||
if (!bitmapShader) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -384,31 +406,73 @@ void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor ds
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
|
||||
#include "src/gpu/GrProxyProvider.h"
|
||||
|
||||
std::unique_ptr<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(
|
||||
const GrFPArgs& args) const {
|
||||
int maxTextureSize = 0;
|
||||
if (args.fContext) {
|
||||
maxTextureSize = args.fContext->priv().caps()->maxTextureSize();
|
||||
}
|
||||
|
||||
auto ctx = args.fContext;
|
||||
auto lm = this->totalLocalMatrix(args.fPreLocalMatrix);
|
||||
SkColorType dstColorType = GrColorTypeToSkColorType(args.fDstColorInfo->colorType());
|
||||
if (dstColorType == kUnknown_SkColorType) {
|
||||
dstColorType = kRGBA_8888_SkColorType;
|
||||
}
|
||||
sk_sp<SkShader> bitmapShader(
|
||||
this->refBitmapShader(args.fMatrixProvider.localToDevice(), &lm, dstColorType,
|
||||
args.fDstColorInfo->colorSpace(),
|
||||
sampling_to_filter(args.fSampling),
|
||||
maxTextureSize));
|
||||
if (!bitmapShader) {
|
||||
|
||||
auto dstCS = ref_or_srgb(args.fDstColorInfo->colorSpace());
|
||||
auto info = CachedImageInfo::Make(fTile, args.fMatrixProvider.localToDevice(), &lm,
|
||||
dstColorType, dstCS.get(),
|
||||
ctx->priv().caps()->maxTextureSize());
|
||||
SkMatrix inv;
|
||||
if (!info.success || !(*lm).invert(&inv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We want to *reset* args.fPreLocalMatrix, not compose it.
|
||||
GrFPArgs newArgs(args.fContext, args.fMatrixProvider, args.fSampling, args.fDstColorInfo);
|
||||
newArgs.fPreLocalMatrix = lm.get();
|
||||
// Gotta be sure the GPU can support our requested colortype (might be FP16)
|
||||
if (!ctx->colorTypeSupportedAsSurface(info.imageInfo.colorType())) {
|
||||
info.imageInfo = info.imageInfo.makeColorType(kRGBA_8888_SkColorType);
|
||||
}
|
||||
|
||||
return as_SB(bitmapShader)->asFragmentProcessor(newArgs);
|
||||
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
|
||||
GrUniqueKey key;
|
||||
GrUniqueKey::Builder builder(&key, kDomain, 10, "Picture Shader Image");
|
||||
builder[0] = dstCS->toXYZD50Hash();
|
||||
builder[1] = dstCS->transferFnHash();
|
||||
builder[2] = static_cast<uint32_t>(dstColorType);
|
||||
builder[3] = fPicture->uniqueID();
|
||||
memcpy(&builder[4], &fTile, sizeof(fTile)); // 4,5,6,7
|
||||
memcpy(&builder[8], &info.tileScale, sizeof(info.tileScale)); // 8,9
|
||||
builder.finish();
|
||||
|
||||
GrProxyProvider* provider = ctx->priv().proxyProvider();
|
||||
GrSurfaceProxyView view;
|
||||
if (auto proxy = provider->findProxyByUniqueKey(key)) {
|
||||
view = GrSurfaceProxyView(proxy, kTopLeft_GrSurfaceOrigin, GrSwizzle());
|
||||
} else {
|
||||
const int msaaSampleCount = 0;
|
||||
const SkSurfaceProps* props = nullptr;
|
||||
const bool createWithMips = false;
|
||||
auto image = info.makeImage(SkSurface::MakeRenderTarget(ctx,
|
||||
SkBudgeted::kYes,
|
||||
info.imageInfo,
|
||||
msaaSampleCount,
|
||||
kTopLeft_GrSurfaceOrigin,
|
||||
props,
|
||||
createWithMips),
|
||||
fPicture.get());
|
||||
if (!image) {
|
||||
return nullptr;
|
||||
}
|
||||
auto [v, ct] = as_IB(image)->asView(ctx, GrMipmapped::kNo);
|
||||
view = std::move(v);
|
||||
provider->assignUniqueKeyToProxy(key, view.asTextureProxy());
|
||||
}
|
||||
|
||||
const GrSamplerState sampler(static_cast<GrSamplerState::WrapMode>(fTmx),
|
||||
static_cast<GrSamplerState::WrapMode>(fTmy),
|
||||
sampling_to_filter(args.fSampling));
|
||||
return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, inv, sampler,
|
||||
*ctx->priv().caps());
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // !legacy
|
||||
|
386
src/shaders/SkPictureShader.cpp.legacy
Normal file
386
src/shaders/SkPictureShader.cpp.legacy
Normal file
@ -0,0 +1,386 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode filter,
|
||||
const SkMatrix* localMatrix, const SkRect* tile) const {
|
||||
if (localMatrix && !localMatrix->invert(nullptr)) {
|
||||
return nullptr;
|
||||
}
|
||||
return SkPictureShader::Make(sk_ref_sp(this), tmx, tmy, (SkPictureShader::FilterEnum)filter,
|
||||
localMatrix, tile);
|
||||
}
|
||||
|
||||
sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy, const SkMatrix* localMatrix,
|
||||
const SkRect* tile) const {
|
||||
if (localMatrix && !localMatrix->invert(nullptr)) {
|
||||
return nullptr;
|
||||
}
|
||||
return SkPictureShader::Make(sk_ref_sp(this), tmx, tmy, SkPictureShader::kInheritFromPaint,
|
||||
localMatrix, tile);
|
||||
}
|
||||
|
||||
sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy,
|
||||
const SkMatrix* localMatrix) const {
|
||||
return this->makeShader(tmx, tmy, localMatrix, nullptr);
|
||||
}
|
||||
|
||||
namespace {
|
||||
static unsigned gBitmapShaderKeyNamespaceLabel;
|
||||
|
||||
struct BitmapShaderKey : public SkResourceCache::Key {
|
||||
public:
|
||||
BitmapShaderKey(SkColorSpace* colorSpace,
|
||||
SkImage::BitDepth bitDepth,
|
||||
uint32_t shaderID,
|
||||
const SkSize& scale)
|
||||
: fColorSpaceXYZHash(colorSpace->toXYZD50Hash())
|
||||
, fColorSpaceTransferFnHash(colorSpace->transferFnHash())
|
||||
, fBitDepth(bitDepth)
|
||||
, fScale(scale) {
|
||||
|
||||
static const size_t keySize = sizeof(fColorSpaceXYZHash) +
|
||||
sizeof(fColorSpaceTransferFnHash) +
|
||||
sizeof(fBitDepth) +
|
||||
sizeof(fScale);
|
||||
// This better be packed.
|
||||
SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fColorSpaceXYZHash) == keySize);
|
||||
this->init(&gBitmapShaderKeyNamespaceLabel, MakeSharedID(shaderID), keySize);
|
||||
}
|
||||
|
||||
static uint64_t MakeSharedID(uint32_t shaderID) {
|
||||
uint64_t sharedID = SkSetFourByteTag('p', 's', 'd', 'r');
|
||||
return (sharedID << 32) | shaderID;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t fColorSpaceXYZHash;
|
||||
uint32_t fColorSpaceTransferFnHash;
|
||||
SkImage::BitDepth fBitDepth;
|
||||
SkSize fScale;
|
||||
|
||||
SkDEBUGCODE(uint32_t fEndOfStruct;)
|
||||
};
|
||||
|
||||
struct BitmapShaderRec : public SkResourceCache::Rec {
|
||||
BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader)
|
||||
: fKey(key)
|
||||
, fShader(SkRef(tileShader)) {}
|
||||
|
||||
BitmapShaderKey fKey;
|
||||
sk_sp<SkShader> fShader;
|
||||
|
||||
const Key& getKey() const override { return fKey; }
|
||||
size_t bytesUsed() const override {
|
||||
// Just the record overhead -- the actual pixels are accounted by SkImage_Lazy.
|
||||
return sizeof(fKey) + sizeof(SkImageShader);
|
||||
}
|
||||
const char* getCategory() const override { return "bitmap-shader"; }
|
||||
SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
|
||||
|
||||
static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
|
||||
const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec);
|
||||
sk_sp<SkShader>* result = reinterpret_cast<sk_sp<SkShader>*>(contextShader);
|
||||
|
||||
*result = rec.fShader;
|
||||
|
||||
// The bitmap shader is backed by an image generator, thus it can always re-generate its
|
||||
// pixels if discarded.
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t next_id() {
|
||||
static std::atomic<uint32_t> nextID{1};
|
||||
|
||||
uint32_t id;
|
||||
do {
|
||||
id = nextID.fetch_add(1, std::memory_order_relaxed);
|
||||
} while (id == SK_InvalidGenID);
|
||||
return id;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy,
|
||||
FilterEnum filter, const SkMatrix* localMatrix, const SkRect* tile)
|
||||
: INHERITED(localMatrix)
|
||||
, fPicture(std::move(picture))
|
||||
, fTile(tile ? *tile : fPicture->cullRect())
|
||||
, fTmx(tmx)
|
||||
, fTmy(tmy)
|
||||
, fFilter(filter)
|
||||
, fUniqueID(next_id())
|
||||
, fAddedToCache(false) {}
|
||||
|
||||
SkPictureShader::~SkPictureShader() {
|
||||
if (fAddedToCache.load()) {
|
||||
SkResourceCache::PostPurgeSharedID(BitmapShaderKey::MakeSharedID(fUniqueID));
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy,
|
||||
FilterEnum filter, const SkMatrix* lm, const SkRect* tile) {
|
||||
if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
|
||||
return SkShaders::Empty();
|
||||
}
|
||||
return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, filter, lm, tile));
|
||||
}
|
||||
|
||||
sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) {
|
||||
SkMatrix lm;
|
||||
buffer.readMatrix(&lm);
|
||||
auto tmx = buffer.read32LE(SkTileMode::kLastTileMode);
|
||||
auto tmy = buffer.read32LE(SkTileMode::kLastTileMode);
|
||||
SkRect tile = buffer.readRect();
|
||||
|
||||
sk_sp<SkPicture> picture;
|
||||
FilterEnum filter;
|
||||
if (buffer.isVersionLT(SkPicturePriv::kPictureShaderFilterParam_Version)) {
|
||||
filter = kInheritFromPaint;
|
||||
bool didSerialize = buffer.readBool();
|
||||
if (didSerialize) {
|
||||
picture = SkPicturePriv::MakeFromBuffer(buffer);
|
||||
}
|
||||
} else {
|
||||
filter = buffer.read32LE(SkPictureShader::kLastFilterEnum);
|
||||
picture = SkPicturePriv::MakeFromBuffer(buffer);
|
||||
}
|
||||
return SkPictureShader::Make(picture, tmx, tmy, filter, &lm, &tile);
|
||||
}
|
||||
|
||||
void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
|
||||
buffer.writeMatrix(this->getLocalMatrix());
|
||||
buffer.write32((unsigned)fTmx);
|
||||
buffer.write32((unsigned)fTmy);
|
||||
buffer.writeRect(fTile);
|
||||
buffer.write32((unsigned)fFilter);
|
||||
SkPicturePriv::Flatten(fPicture, buffer);
|
||||
}
|
||||
|
||||
// These are tricky "downscales" -- need to deduce the caller's intention.
|
||||
// For now, map anything that is not "nearest/none" to kLinear
|
||||
//
|
||||
// The "modern" version of pictureshader explicitly takes SkFilterMode.
|
||||
// The legacy version inherits it from the paint, hence the extra conversions/plumbing
|
||||
// needed to downscale either filter-quality or sampling (from the paint) if we're in
|
||||
// legacy mode. When all clients only use the modern/explicit version, we can eliminate
|
||||
// all of this extra stuff.
|
||||
|
||||
static SkFilterMode sampling_to_filter(const SkSamplingOptions& sampling) {
|
||||
return sampling == SkSamplingOptions() ? SkFilterMode::kNearest
|
||||
: SkFilterMode::kLinear;
|
||||
}
|
||||
|
||||
static SkFilterMode quality_to_filter(SkFilterQuality quality) {
|
||||
return quality == kNone_SkFilterQuality ? SkFilterMode::kNearest
|
||||
: SkFilterMode::kLinear;
|
||||
}
|
||||
|
||||
// Returns a cached image shader, which wraps a single picture tile at the given
|
||||
// CTM/local matrix. Also adjusts the local matrix for tile scaling.
|
||||
sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix,
|
||||
SkTCopyOnFirstWrite<SkMatrix>* localMatrix,
|
||||
SkColorType dstColorType,
|
||||
SkColorSpace* dstColorSpace,
|
||||
SkFilterMode paintFilter,
|
||||
const int maxTextureSize) const {
|
||||
SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
|
||||
|
||||
const SkMatrix m = SkMatrix::Concat(viewMatrix, **localMatrix);
|
||||
|
||||
// Use a rotation-invariant scale
|
||||
SkSize scaledSize;
|
||||
if (!m.decomposeScale(&scaledSize, nullptr)) {
|
||||
scaledSize = {1, 1};
|
||||
}
|
||||
scaledSize.fWidth *= fTile.width();
|
||||
scaledSize.fHeight *= fTile.height();
|
||||
|
||||
// Clamp the tile size to about 4M pixels
|
||||
static const SkScalar kMaxTileArea = 2048 * 2048;
|
||||
SkScalar tileArea = scaledSize.width() * scaledSize.height();
|
||||
if (tileArea > kMaxTileArea) {
|
||||
SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
|
||||
scaledSize.set(scaledSize.width() * clampScale,
|
||||
scaledSize.height() * clampScale);
|
||||
}
|
||||
#if SK_SUPPORT_GPU
|
||||
// Scale down the tile size if larger than maxTextureSize for GPU Path or it should fail on create texture
|
||||
if (maxTextureSize) {
|
||||
if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
|
||||
SkScalar downScale = maxTextureSize / std::max(scaledSize.width(), scaledSize.height());
|
||||
scaledSize.set(SkScalarFloorToScalar(scaledSize.width() * downScale),
|
||||
SkScalarFloorToScalar(scaledSize.height() * downScale));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const SkISize tileSize = scaledSize.toCeil();
|
||||
if (tileSize.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The actual scale, compensating for rounding & clamping.
|
||||
const SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
|
||||
SkIntToScalar(tileSize.height()) / fTile.height());
|
||||
|
||||
|
||||
sk_sp<SkColorSpace> imgCS = dstColorSpace ? sk_ref_sp(dstColorSpace): SkColorSpace::MakeSRGB();
|
||||
SkImage::BitDepth bitDepth = SkColorTypeMaxBitsPerChannel(dstColorType) >= 16
|
||||
? SkImage::BitDepth::kF16
|
||||
: SkImage::BitDepth::kU8;
|
||||
|
||||
BitmapShaderKey key(imgCS.get(), bitDepth, fUniqueID, tileScale);
|
||||
|
||||
sk_sp<SkShader> tileShader;
|
||||
if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) {
|
||||
SkMatrix tileMatrix = SkMatrix::RectToRect(fTile, SkRect::MakeIWH(tileSize.width(),
|
||||
tileSize.height()));
|
||||
|
||||
sk_sp<SkImage> tileImage = SkImage::MakeFromPicture(fPicture, tileSize, &tileMatrix,
|
||||
nullptr, bitDepth, std::move(imgCS));
|
||||
if (!tileImage) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkFilterMode filter;
|
||||
if (fFilter == kInheritFromPaint) {
|
||||
filter = paintFilter;
|
||||
} else {
|
||||
filter = (SkFilterMode)fFilter;
|
||||
}
|
||||
tileShader = tileImage->makeShader(fTmx, fTmy, SkSamplingOptions(filter), nullptr);
|
||||
|
||||
SkResourceCache::Add(new BitmapShaderRec(key, tileShader.get()));
|
||||
fAddedToCache.store(true);
|
||||
}
|
||||
|
||||
if (tileScale.width() != 1 || tileScale.height() != 1) {
|
||||
localMatrix->writable()->preScale(1 / tileScale.width(), 1 / tileScale.height());
|
||||
}
|
||||
|
||||
return tileShader;
|
||||
}
|
||||
|
||||
bool SkPictureShader::onAppendStages(const SkStageRec& rec) const {
|
||||
auto lm = this->totalLocalMatrix(rec.fLocalM);
|
||||
|
||||
// Keep bitmapShader alive by using alloc instead of stack memory
|
||||
auto& bitmapShader = *rec.fAlloc->make<sk_sp<SkShader>>();
|
||||
bitmapShader = this->refBitmapShader(rec.fMatrixProvider.localToDevice(), &lm,
|
||||
rec.fDstColorType, rec.fDstCS,
|
||||
quality_to_filter(rec.fPaint.getFilterQuality()));
|
||||
|
||||
if (!bitmapShader) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkStageRec localRec = rec;
|
||||
localRec.fLocalM = lm->isIdentity() ? nullptr : lm.get();
|
||||
|
||||
return as_SB(bitmapShader)->appendStages(localRec);
|
||||
}
|
||||
|
||||
skvm::Color SkPictureShader::onProgram(skvm::Builder* p,
|
||||
skvm::Coord device, skvm::Coord local, skvm::Color paint,
|
||||
const SkMatrixProvider& matrices, const SkMatrix* localM,
|
||||
SkFilterQuality quality, const SkColorInfo& dst,
|
||||
skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
|
||||
auto lm = this->totalLocalMatrix(localM);
|
||||
|
||||
// Keep bitmapShader alive by using alloc instead of stack memory
|
||||
auto& bitmapShader = *alloc->make<sk_sp<SkShader>>();
|
||||
bitmapShader = this->refBitmapShader(matrices.localToDevice(), &lm,
|
||||
dst.colorType(), dst.colorSpace(),
|
||||
quality_to_filter(quality));
|
||||
if (!bitmapShader) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return as_SB(bitmapShader)->program(p, device,local, paint,
|
||||
matrices,lm,
|
||||
quality,dst,
|
||||
uniforms,alloc);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
|
||||
SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc)
|
||||
const {
|
||||
auto lm = this->totalLocalMatrix(rec.fLocalMatrix);
|
||||
sk_sp<SkShader> bitmapShader = this->refBitmapShader(*rec.fMatrix, &lm, rec.fDstColorType,
|
||||
rec.fDstColorSpace,
|
||||
sampling_to_filter(rec.fPaintSampling));
|
||||
if (!bitmapShader) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ContextRec localRec = rec;
|
||||
localRec.fLocalMatrix = lm->isIdentity() ? nullptr : lm.get();
|
||||
|
||||
PictureShaderContext* ctx =
|
||||
alloc->make<PictureShaderContext>(*this, localRec, std::move(bitmapShader), alloc);
|
||||
if (nullptr == ctx->fBitmapShaderContext) {
|
||||
ctx = nullptr;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkPictureShader::PictureShaderContext::PictureShaderContext(
|
||||
const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader,
|
||||
SkArenaAlloc* alloc)
|
||||
: INHERITED(shader, rec)
|
||||
, fBitmapShader(std::move(bitmapShader))
|
||||
{
|
||||
fBitmapShaderContext = as_SB(fBitmapShader)->makeContext(rec, alloc);
|
||||
//if fBitmapShaderContext is null, we are invalid
|
||||
}
|
||||
|
||||
uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
|
||||
SkASSERT(fBitmapShaderContext);
|
||||
return fBitmapShaderContext->getFlags();
|
||||
}
|
||||
|
||||
void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
|
||||
SkASSERT(fBitmapShaderContext);
|
||||
fBitmapShaderContext->shadeSpan(x, y, dstC, count);
|
||||
}
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
|
||||
std::unique_ptr<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(
|
||||
const GrFPArgs& args) const {
|
||||
int maxTextureSize = 0;
|
||||
if (args.fContext) {
|
||||
maxTextureSize = args.fContext->priv().caps()->maxTextureSize();
|
||||
}
|
||||
|
||||
auto lm = this->totalLocalMatrix(args.fPreLocalMatrix);
|
||||
SkColorType dstColorType = GrColorTypeToSkColorType(args.fDstColorInfo->colorType());
|
||||
if (dstColorType == kUnknown_SkColorType) {
|
||||
dstColorType = kRGBA_8888_SkColorType;
|
||||
}
|
||||
sk_sp<SkShader> bitmapShader(
|
||||
this->refBitmapShader(args.fMatrixProvider.localToDevice(), &lm, dstColorType,
|
||||
args.fDstColorInfo->colorSpace(),
|
||||
sampling_to_filter(args.fSampling),
|
||||
maxTextureSize));
|
||||
if (!bitmapShader) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We want to *reset* args.fPreLocalMatrix, not compose it.
|
||||
GrFPArgs newArgs(args.fContext, args.fMatrixProvider, args.fSampling, args.fDstColorInfo);
|
||||
newArgs.fPreLocalMatrix = lm.get();
|
||||
|
||||
return as_SB(bitmapShader)->asFragmentProcessor(newArgs);
|
||||
}
|
||||
#endif
|
@ -16,6 +16,10 @@ class SkArenaAlloc;
|
||||
class SkBitmap;
|
||||
class SkPicture;
|
||||
|
||||
#ifdef SK_SUPPORT_LEGACY_PICTURESHADER_MATH
|
||||
#include "src/shaders/SkPictureShader.h.legacy"
|
||||
#else
|
||||
|
||||
/*
|
||||
* An SkPictureShader can be used to draw SkPicture-based patterns.
|
||||
*
|
||||
@ -24,8 +28,6 @@ class SkPicture;
|
||||
*/
|
||||
class SkPictureShader : public SkShaderBase {
|
||||
public:
|
||||
~SkPictureShader() override;
|
||||
|
||||
enum FilterEnum {
|
||||
kNearest, // SkFilterMode::kNearest
|
||||
kLinear, // SkFilterMode::kLinear
|
||||
@ -60,10 +62,11 @@ private:
|
||||
SkPictureShader(sk_sp<SkPicture>, SkTileMode, SkTileMode, FilterEnum,
|
||||
const SkMatrix*, const SkRect*);
|
||||
|
||||
sk_sp<SkShader> refBitmapShader(const SkMatrix&, SkTCopyOnFirstWrite<SkMatrix>* localMatrix,
|
||||
SkColorType dstColorType, SkColorSpace* dstColorSpace,
|
||||
SkFilterMode paintFilter,
|
||||
const int maxTextureSize = 0) const;
|
||||
sk_sp<SkShader> rasterShader(const SkMatrix&, SkTCopyOnFirstWrite<SkMatrix>* localMatrix,
|
||||
SkColorType dstColorType, SkColorSpace* dstColorSpace,
|
||||
SkFilterMode paintFilter) const;
|
||||
|
||||
sk_sp<SkShader> makeShader(const SkImage*, SkFilterMode paintFilter) const;
|
||||
|
||||
class PictureShaderContext : public Context {
|
||||
public:
|
||||
@ -86,10 +89,8 @@ private:
|
||||
SkTileMode fTmx, fTmy;
|
||||
FilterEnum fFilter;
|
||||
|
||||
const uint32_t fUniqueID;
|
||||
mutable std::atomic<bool> fAddedToCache;
|
||||
|
||||
using INHERITED = SkShaderBase;
|
||||
};
|
||||
#endif // !legacy
|
||||
|
||||
#endif // SkPictureShader_DEFINED
|
||||
|
82
src/shaders/SkPictureShader.h.legacy
Normal file
82
src/shaders/SkPictureShader.h.legacy
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* An SkPictureShader can be used to draw SkPicture-based patterns.
|
||||
*
|
||||
* The SkPicture is first rendered into a tile, which is then used to shade the area according
|
||||
* to specified tiling rules.
|
||||
*/
|
||||
class SkPictureShader : public SkShaderBase {
|
||||
public:
|
||||
~SkPictureShader() override;
|
||||
|
||||
enum FilterEnum {
|
||||
kNearest, // SkFilterMode::kNearest
|
||||
kLinear, // SkFilterMode::kLinear
|
||||
kInheritFromPaint,
|
||||
|
||||
kLastFilterEnum = kInheritFromPaint,
|
||||
};
|
||||
|
||||
static sk_sp<SkShader> Make(sk_sp<SkPicture>, SkTileMode, SkTileMode, FilterEnum,
|
||||
const SkMatrix*, const SkRect*);
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
SkPictureShader(SkReadBuffer&);
|
||||
void flatten(SkWriteBuffer&) const override;
|
||||
bool onAppendStages(const SkStageRec&) const override;
|
||||
skvm::Color onProgram(skvm::Builder*, skvm::Coord device, skvm::Coord local, skvm::Color paint,
|
||||
const SkMatrixProvider&, const SkMatrix* localM,
|
||||
SkFilterQuality quality, const SkColorInfo& dst,
|
||||
skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override;
|
||||
|
||||
#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
|
||||
Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
SK_FLATTENABLE_HOOKS(SkPictureShader)
|
||||
|
||||
SkPictureShader(sk_sp<SkPicture>, SkTileMode, SkTileMode, FilterEnum,
|
||||
const SkMatrix*, const SkRect*);
|
||||
|
||||
sk_sp<SkShader> refBitmapShader(const SkMatrix&, SkTCopyOnFirstWrite<SkMatrix>* localMatrix,
|
||||
SkColorType dstColorType, SkColorSpace* dstColorSpace,
|
||||
SkFilterMode paintFilter,
|
||||
const int maxTextureSize = 0) const;
|
||||
|
||||
class PictureShaderContext : public Context {
|
||||
public:
|
||||
PictureShaderContext(
|
||||
const SkPictureShader&, const ContextRec&, sk_sp<SkShader> bitmapShader, SkArenaAlloc*);
|
||||
|
||||
uint32_t getFlags() const override;
|
||||
|
||||
void shadeSpan(int x, int y, SkPMColor dstC[], int count) override;
|
||||
|
||||
sk_sp<SkShader> fBitmapShader;
|
||||
SkShaderBase::Context* fBitmapShaderContext;
|
||||
void* fBitmapShaderContextStorage;
|
||||
|
||||
using INHERITED = Context;
|
||||
};
|
||||
|
||||
sk_sp<SkPicture> fPicture;
|
||||
SkRect fTile;
|
||||
SkTileMode fTmx, fTmy;
|
||||
FilterEnum fFilter;
|
||||
|
||||
const uint32_t fUniqueID;
|
||||
mutable std::atomic<bool> fAddedToCache;
|
||||
|
||||
using INHERITED = SkShaderBase;
|
||||
};
|
@ -46,3 +46,69 @@ DEF_TEST(PictureShader_caching, reporter) {
|
||||
// All but the local ref should be gone now.
|
||||
REPORTER_ASSERT(reporter, picture->unique());
|
||||
}
|
||||
|
||||
#ifndef SK_SUPPORT_LEGACY_PICTURESHADER_MATH
|
||||
|
||||
#include "src/core/SkPicturePriv.h"
|
||||
#include "src/core/SkResourceCache.h"
|
||||
|
||||
/*
|
||||
* Check caching of picture-shaders
|
||||
* - we do cache the underlying image (i.e. there is a cache entry)
|
||||
* - there is only 1 entry, even with differing tile modes
|
||||
* - after deleting the picture, the cache entry is purged
|
||||
*/
|
||||
DEF_TEST(PictureShader_caching2, reporter) {
|
||||
auto picture = []() {
|
||||
SkPictureRecorder recorder;
|
||||
recorder.beginRecording(100, 100)->drawColor(SK_ColorGREEN);
|
||||
return recorder.finishRecordingAsPicture();
|
||||
}();
|
||||
REPORTER_ASSERT(reporter, picture->unique());
|
||||
|
||||
struct Data {
|
||||
uint64_t sharedID;
|
||||
int counter;
|
||||
} data = {
|
||||
SkPicturePriv::MakeSharedID(picture->uniqueID()),
|
||||
0,
|
||||
};
|
||||
|
||||
auto counter = [](const SkResourceCache::Rec& rec, void* dataPtr) {
|
||||
if (rec.getKey().getSharedID() == ((Data*)dataPtr)->sharedID) {
|
||||
((Data*)dataPtr)->counter += 1;
|
||||
}
|
||||
};
|
||||
|
||||
SkResourceCache::VisitAll(counter, &data);
|
||||
REPORTER_ASSERT(reporter, data.counter == 0);
|
||||
|
||||
// Draw with a view variants of picture-shaders that all use the same picture.
|
||||
// Only expect 1 cache entry for all (since same CTM for all).
|
||||
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(100, 100);
|
||||
for (SkTileMode m : {
|
||||
SkTileMode::kClamp, SkTileMode::kRepeat, SkTileMode::kRepeat, SkTileMode::kDecal
|
||||
}) {
|
||||
SkPaint paint;
|
||||
paint.setShader(picture->makeShader(m, m));
|
||||
surface->getCanvas()->drawPaint(paint);
|
||||
}
|
||||
|
||||
// Don't expect any additional refs on the picture
|
||||
REPORTER_ASSERT(reporter, picture->unique());
|
||||
|
||||
// Check that we did cache something, but only 1 thing
|
||||
data.counter = 0;
|
||||
SkResourceCache::VisitAll(counter, &data);
|
||||
REPORTER_ASSERT(reporter, data.counter == 1);
|
||||
|
||||
// Now delete the picture, and check the we purge the cache entry
|
||||
|
||||
picture.reset();
|
||||
SkResourceCache::CheckMessages();
|
||||
|
||||
data.counter = 0;
|
||||
SkResourceCache::VisitAll(counter, &data);
|
||||
REPORTER_ASSERT(reporter, data.counter == 0);
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user