Draw YUVA images with multitexture

Bug: skia:7901
Change-Id: I99cde1acc27c1cfb730671463a2c17537926cd99
Reviewed-on: https://skia-review.googlesource.com/c/164696
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
Jim Van Verth 2018-11-02 13:36:42 -04:00 committed by Skia Commit-Bot
parent 8b35379ae1
commit 30e0d7fb4f
13 changed files with 199 additions and 55 deletions

View File

@ -121,7 +121,8 @@ protected:
std::unique_ptr<GrFragmentProcessor> fp(
GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
static_cast<SkYUVColorSpace>(space)));
static_cast<SkYUVColorSpace>(space),
GrSamplerState::Filter::kNearest));
if (fp) {
GrPaint grPaint;
grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
@ -244,7 +245,8 @@ protected:
GrPaint grPaint;
grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
auto fp = GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
static_cast<SkYUVColorSpace>(space));
static_cast<SkYUVColorSpace>(space),
GrSamplerState::Filter::kNearest);
if (fp) {
SkMatrix viewMatrix;
viewMatrix.setTranslate(x, y);

View File

@ -7,7 +7,9 @@
#include "GrImageTextureMaker.h"
#include "SkGr.h"
#include "SkImage_GpuYUVA.h"
#include "SkImage_Lazy.h"
#include "effects/GrYUVtoRGBEffect.h"
GrImageTextureMaker::GrImageTextureMaker(GrContext* context, const SkImage* client,
SkImage::CachingHint chint)
@ -39,3 +41,65 @@ SkAlphaType GrImageTextureMaker::alphaType() const {
SkColorSpace* GrImageTextureMaker::colorSpace() const {
return fImage->colorSpace();
}
/////////////////////////////////////////////////////////////////////////////////////////////////
GrYUVAImageTextureMaker::GrYUVAImageTextureMaker(GrContext* context, const SkImage* client )
: INHERITED(context, client->width(), client->height(), client->isAlphaOnly())
, fImage(static_cast<const SkImage_GpuYUVA*>(client)) {
SkASSERT(as_IB(client)->isYUVA());
GrMakeKeyFromImageID(&fOriginalKey, client->uniqueID(),
SkIRect::MakeWH(this->width(), this->height()));
}
sk_sp<GrTextureProxy> GrYUVAImageTextureMaker::refOriginalTextureProxy(bool willBeMipped,
AllowedTexGenType onlyIfFast) {
return (AllowedTexGenType::kCheap == onlyIfFast) ? nullptr : fImage->asTextureProxyRef();
}
void GrYUVAImageTextureMaker::makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) {
// TODO: Do we ever want to disable caching?
if (fOriginalKey.isValid()) {
GrUniqueKey cacheKey;
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
GrUniqueKey::Builder builder(&cacheKey, fOriginalKey, kDomain, 0, "Image");
MakeCopyKeyFromOrigKey(cacheKey, stretch, paramsCopyKey);
}
}
SkAlphaType GrYUVAImageTextureMaker::alphaType() const {
return fImage->alphaType();
}
SkColorSpace* GrYUVAImageTextureMaker::colorSpace() const {
return fImage->colorSpace();
}
std::unique_ptr<GrFragmentProcessor> GrYUVAImageTextureMaker::createFragmentProcessor(
const SkMatrix& textureMatrix,
const SkRect& constraintRect,
FilterConstraint filterConstraint,
bool coordsLimitedToConstraintRect,
const GrSamplerState::Filter* filterOrNullForBicubic) {
// Check simple cases to see if we need to fall back to flattening the image
if (!textureMatrix.isIdentity() || kNo_FilterConstraint != filterConstraint ||
!coordsLimitedToConstraintRect || !filterOrNullForBicubic) {
return this->INHERITED::createFragmentProcessor(textureMatrix, constraintRect,
filterConstraint,
coordsLimitedToConstraintRect,
filterOrNullForBicubic);
}
// Check to see if the client has given us pre-mipped textures
// If not, fall back to bilerp
// TODO: investigate flattening the image and generating miplevels
GrSamplerState::Filter filter = *filterOrNullForBicubic;
if (GrSamplerState::Filter::kMipMap == filter && !fImage->canBeMipmapped(fContext)) {
filter = GrSamplerState::Filter::kBilerp;
}
return GrYUVtoRGBEffect::Make(fImage->fProxies, fImage->fYUVAIndices,
fImage->fYUVColorSpace, filter);
}

View File

@ -12,6 +12,7 @@
#include "SkImage.h"
class SkImage_Lazy;
class SkImage_GpuYUVA;
/** This class manages the conversion of generator-backed images to GrTextures. If the caching hint
is kAllow the image's ID is used for the cache key. */
@ -40,4 +41,37 @@ private:
typedef GrTextureMaker INHERITED;
};
/** This class manages the conversion of generator-backed YUVA images to GrTextures. */
class GrYUVAImageTextureMaker : public GrTextureMaker {
public:
GrYUVAImageTextureMaker(GrContext* context, const SkImage* client);
protected:
// TODO: consider overriding this, for the case where the underlying generator might be
// able to efficiently produce a "stretched" texture natively (e.g. picture-backed)
// GrTexture* generateTextureForParams(const CopyParams&) override;
sk_sp<GrTextureProxy> refOriginalTextureProxy(bool willBeMipped,
AllowedTexGenType onlyIfFast) override;
void makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) override;
void didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) override {}
std::unique_ptr<GrFragmentProcessor> createFragmentProcessor(
const SkMatrix& textureMatrix,
const SkRect& constraintRect,
FilterConstraint filterConstraint,
bool coordsLimitedToConstraintRect,
const GrSamplerState::Filter* filterOrNullForBicubic) override;
SkAlphaType alphaType() const override;
SkColorSpace* colorSpace() const override;
private:
const SkImage_GpuYUVA* fImage;
GrUniqueKey fOriginalKey;
typedef GrTextureMaker INHERITED;
};
#endif

View File

@ -163,7 +163,8 @@ sk_sp<GrTextureProxy> GrYUVProvider::refAsTextureProxy(GrContext* ctx, const GrS
}
GrPaint paint;
auto yuvToRgbProcessor = GrYUVtoRGBEffect::Make(yuvTextureProxies, yuvaIndices, yuvColorSpace);
auto yuvToRgbProcessor = GrYUVtoRGBEffect::Make(yuvTextureProxies, yuvaIndices, yuvColorSpace,
GrSamplerState::Filter::kNearest);
paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor));
// If the caller expects the pixels in a different color space than the one from the image,

View File

@ -50,6 +50,7 @@
#include "SkVertState.h"
#include "SkVertices.h"
#include "SkWritePixelsRec.h"
#include "SkYUVAIndex.h"
#include "effects/GrBicubicEffect.h"
#include "effects/GrSimpleTextureEffect.h"
#include "effects/GrTextureDomain.h"
@ -822,8 +823,8 @@ void SkGpuDevice::drawBitmap(const SkBitmap& bitmap,
}
}
GrBitmapTextureMaker maker(fContext.get(), bitmap);
this->drawTextureMaker(&maker, bitmap.width(), bitmap.height(), nullptr, nullptr,
SkCanvas::kStrict_SrcRectConstraint, viewMatrix, paint);
this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kStrict_SrcRectConstraint,
viewMatrix, paint, true);
}
// This method outsets 'iRect' by 'outset' all around and then clamps its extents to
@ -1180,8 +1181,7 @@ void SkGpuDevice::drawBitmapRect(const SkBitmap& bitmap,
}
}
GrBitmapTextureMaker maker(fContext.get(), bitmap);
this->drawTextureMaker(&maker, bitmap.width(), bitmap.height(), src, dst, constraint,
this->ctm(), paint);
this->drawTextureProducer(&maker, src, dst, constraint, this->ctm(), paint, true);
}
sk_sp<SkSpecialImage> SkGpuDevice::makeSpecial(const SkBitmap& bitmap) {
@ -1274,6 +1274,12 @@ void SkGpuDevice::drawImage(const SkImage* image, SkScalar x, SkScalar y, const
ASSERT_SINGLE_OWNER
SkMatrix viewMatrix = this->ctm();
viewMatrix.preTranslate(x, y);
if (as_IB(image)->isYUVA()) {
GrYUVAImageTextureMaker maker(fContext.get(), image);
this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint,
viewMatrix, paint, false);
return;
}
uint32_t pinnedUniqueID;
if (sk_sp<GrTextureProxy> proxy = as_IB(image)->refPinnedTextureProxy(&pinnedUniqueID)) {
this->drawPinnedTextureProxy(std::move(proxy), pinnedUniqueID, as_IB(image)->colorSpace(),
@ -1293,24 +1299,29 @@ void SkGpuDevice::drawImage(const SkImage* image, SkScalar x, SkScalar y, const
}
if (image->isLazyGenerated()) {
GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
this->drawTextureMaker(&maker, image->width(), image->height(), nullptr, nullptr,
SkCanvas::kFast_SrcRectConstraint, viewMatrix, paint);
this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint,
viewMatrix, paint, true);
return;
}
if (as_IB(image)->getROPixels(&bm)) {
GrBitmapTextureMaker maker(fContext.get(), bm);
this->drawTextureMaker(&maker, image->width(), image->height(), nullptr, nullptr,
SkCanvas::kFast_SrcRectConstraint, viewMatrix, paint);
this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint,
viewMatrix, paint, true);
}
}
void SkGpuDevice::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
ASSERT_SINGLE_OWNER
uint32_t pinnedUniqueID;
if (!src || src->contains(image->bounds())) {
constraint = SkCanvas::kFast_SrcRectConstraint;
}
if (as_IB(image)->isYUVA()) {
GrYUVAImageTextureMaker maker(fContext.get(), image);
this->drawTextureProducer(&maker, src, &dst, constraint, this->ctm(), paint, false);
return;
}
uint32_t pinnedUniqueID;
if (sk_sp<GrTextureProxy> proxy = as_IB(image)->refPinnedTextureProxy(&pinnedUniqueID)) {
this->drawPinnedTextureProxy(std::move(proxy), pinnedUniqueID, as_IB(image)->colorSpace(),
image->alphaType(), src, &dst, constraint, this->ctm(), paint);
@ -1331,14 +1342,12 @@ void SkGpuDevice::drawImageRect(const SkImage* image, const SkRect* src, const S
}
if (image->isLazyGenerated()) {
GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
this->drawTextureMaker(&maker, image->width(), image->height(), src, &dst, constraint,
this->ctm(), paint);
this->drawTextureProducer(&maker, src, &dst, constraint, this->ctm(), paint, true);
return;
}
if (as_IB(image)->getROPixels(&bm)) {
GrBitmapTextureMaker maker(fContext.get(), bm);
this->drawTextureMaker(&maker, image->width(), image->height(), src, &dst, constraint,
this->ctm(), paint);
this->drawTextureProducer(&maker, src, &dst, constraint, this->ctm(), paint, true);
}
}

View File

@ -215,21 +215,13 @@ private:
const SkMatrix& viewMatrix,
const SkPaint&);
void drawTextureMaker(GrTextureMaker* maker,
int imageW,
int imageH,
const SkRect* srcRect,
const SkRect* dstRect,
SkCanvas::SrcRectConstraint,
const SkMatrix& viewMatrix,
const SkPaint&);
void drawTextureProducer(GrTextureProducer*,
const SkRect* srcRect,
const SkRect* dstRect,
SkCanvas::SrcRectConstraint,
const SkMatrix& viewMatrix,
const SkPaint&);
const SkPaint&,
bool attemptDrawTexture);
void drawTextureProducerImpl(GrTextureProducer*,
const SkRect& clippedSrcRect,

View File

@ -156,27 +156,7 @@ void SkGpuDevice::drawPinnedTextureProxy(sk_sp<GrTextureProxy> proxy, uint32_t p
}
GrTextureAdjuster adjuster(this->context(), std::move(proxy), alphaType, pinnedUniqueID,
colorSpace);
this->drawTextureProducer(&adjuster, srcRect, dstRect, constraint, viewMatrix, paint);
}
void SkGpuDevice::drawTextureMaker(GrTextureMaker* maker, int imageW, int imageH,
const SkRect* srcRect, const SkRect* dstRect,
SkCanvas::SrcRectConstraint constraint,
const SkMatrix& viewMatrix, const SkPaint& paint) {
GrAA aa = GrAA(paint.isAntiAlias());
if (can_use_draw_texture(paint)) {
// We've done enough checks above to allow us to pass ClampNearest() and not check for
// scaling adjustments.
auto proxy = maker->refTextureProxyForParams(GrSamplerState::ClampNearest(), nullptr);
if (!proxy) {
return;
}
draw_texture(paint, viewMatrix, srcRect, dstRect, aa, constraint, std::move(proxy),
maker->alphaType(), maker->colorSpace(), this->clip(),
fRenderTargetContext.get());
return;
}
this->drawTextureProducer(maker, srcRect, dstRect, constraint, viewMatrix, paint);
this->drawTextureProducer(&adjuster, srcRect, dstRect, constraint, viewMatrix, paint, false);
}
void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer,
@ -184,7 +164,22 @@ void SkGpuDevice::drawTextureProducer(GrTextureProducer* producer,
const SkRect* dstRect,
SkCanvas::SrcRectConstraint constraint,
const SkMatrix& viewMatrix,
const SkPaint& paint) {
const SkPaint& paint,
bool attemptDrawTexture) {
if (attemptDrawTexture && can_use_draw_texture(paint)) {
GrAA aa = GrAA(paint.isAntiAlias());
// We've done enough checks above to allow us to pass ClampNearest() and not check for
// scaling adjustments.
auto proxy = producer->refTextureProxyForParams(GrSamplerState::ClampNearest(), nullptr);
if (!proxy) {
return;
}
draw_texture(paint, viewMatrix, srcRect, dstRect, aa, constraint, std::move(proxy),
producer->alphaType(), producer->colorSpace(), this->clip(),
fRenderTargetContext.get());
return;
}
// This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry.
SK_HISTOGRAM_BOOLEAN("DrawTiled", false);

View File

@ -30,20 +30,24 @@ static const float kRec709ConversionMatrix[16] = {
std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::Make(const sk_sp<GrTextureProxy> proxies[],
const SkYUVAIndex yuvaIndices[4],
SkYUVColorSpace yuvColorSpace) {
SkYUVColorSpace yuvColorSpace,
GrSamplerState::Filter filterMode) {
int numPlanes;
SkAssertResult(SkYUVAIndex::AreValidIndices(yuvaIndices, &numPlanes));
const SkISize YSize = proxies[yuvaIndices[SkYUVAIndex::kY_Index].fIndex]->isize();
GrSamplerState::Filter minimizeFilterMode = GrSamplerState::Filter::kMipMap == filterMode ?
GrSamplerState::Filter::kMipMap :
GrSamplerState::Filter::kBilerp;
GrSamplerState::Filter filterModes[4];
SkSize scales[4];
for (int i = 0; i < numPlanes; ++i) {
SkISize size = proxies[i]->isize();
scales[i] = SkSize::Make(SkIntToScalar(size.width()) / SkIntToScalar(YSize.width()),
SkIntToScalar(size.height()) / SkIntToScalar(YSize.height()));
filterModes[i] = (size == YSize) ? GrSamplerState::Filter::kNearest
: GrSamplerState::Filter::kBilerp;
filterModes[i] = (size == YSize) ? filterMode : minimizeFilterMode;
}
SkMatrix44 mat;

View File

@ -19,7 +19,8 @@ class GrYUVtoRGBEffect : public GrFragmentProcessor {
public:
static std::unique_ptr<GrFragmentProcessor> Make(const sk_sp<GrTextureProxy> proxies[],
const SkYUVAIndex indices[4],
SkYUVColorSpace yuvColorSpace);
SkYUVColorSpace yuvColorSpace,
GrSamplerState::Filter filterMode);
SkString dumpInfo() const override;
const SkMatrix44& colorSpaceMatrix() const { return fColorSpaceMatrix; }

View File

@ -58,7 +58,9 @@ public:
virtual sk_sp<GrTextureProxy> refPinnedTextureProxy(uint32_t* uniqueID) const {
return nullptr;
}
virtual bool isYUVA() const { return false; }
virtual bool asYUVATextureProxiesRef(sk_sp<GrTextureProxy>[4], SkYUVAIndex[4],
SkYUVColorSpace*) const { return false; }
virtual GrTexture* onGetTexture() const { return nullptr; }
#endif
virtual GrBackendTexture onGetBackendTexture(bool flushPendingGrContextIO,

View File

@ -183,7 +183,8 @@ sk_sp<SkImage> SkImage_Gpu::ConvertYUVATexturesToRGB(
// TODO: Modify the fragment processor to sample from different channel instead of taking nv12
// bool.
paint.addColorFragmentProcessor(GrYUVtoRGBEffect::Make(tempTextureProxies, yuvaIndices,
yuvColorSpace));
yuvColorSpace,
GrSamplerState::Filter::kNearest));
const SkRect rect = SkRect::MakeIWH(width, height);

View File

@ -12,10 +12,13 @@
#include "GrClip.h"
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrGpu.h"
#include "GrRenderTargetContext.h"
#include "GrTexture.h"
#include "GrTextureProducer.h"
#include "SkImage_Gpu.h"
#include "SkImage_GpuYUVA.h"
#include "SkMipMap.h"
#include "SkYUVASizeInfo.h"
#include "effects/GrYUVtoRGBEffect.h"
@ -52,6 +55,25 @@ SkImageInfo SkImage_GpuYUVA::onImageInfo() const {
fAlphaType, fColorSpace);
}
bool SkImage_GpuYUVA::canBeMipmapped(GrContext* context) const {
int numTextures;
if (!SkYUVAIndex::AreValidIndices(fYUVAIndices, &numTextures)) {
return false;
}
for (int i = 0; i < numTextures; ++i) {
GrTextureProducer::CopyParams copyParams;
int mipCount = SkMipMap::ComputeLevelCount(fProxies[i]->width(), fProxies[i]->height());
if (mipCount && GrGpu::IsACopyNeededForMips(fContext->contextPriv().caps(),
fProxies[i].get(),
GrSamplerState::Filter::kMipMap,
&copyParams)) {
return false;
}
}
return true;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
sk_sp<GrTextureProxy> SkImage_GpuYUVA::asTextureProxyRef() const {
@ -69,7 +91,8 @@ sk_sp<GrTextureProxy> SkImage_GpuYUVA::asTextureProxyRef() const {
// TODO: modify the YUVtoRGBEffect to do premul if fImageAlphaType is kPremul_AlphaType
paint.addColorFragmentProcessor(GrYUVtoRGBEffect::Make(fProxies, fYUVAIndices,
fYUVColorSpace));
fYUVColorSpace,
GrSamplerState::Filter::kNearest));
const SkRect rect = SkRect::MakeIWH(this->width(), this->height());

View File

@ -23,6 +23,8 @@ struct SkYUVASizeInfo;
// proxy will be stored and used for any future rendering.
class SkImage_GpuYUVA : public SkImage_GpuBase {
public:
friend class GrYUVAImageTextureMaker;
SkImage_GpuYUVA(sk_sp<GrContext>, int width, int height, uint32_t uniqueID, SkYUVColorSpace,
sk_sp<GrTextureProxy> proxies[], const SkYUVAIndex yuvaIndices[4],
GrSurfaceOrigin, sk_sp<SkColorSpace>, SkBudgeted);
@ -35,6 +37,20 @@ public:
virtual bool onIsTextureBacked() const override { return SkToBool(fProxies[0].get()); }
virtual bool isYUVA() const override { return true; }
virtual bool asYUVATextureProxiesRef(sk_sp<GrTextureProxy> proxies[4],
SkYUVAIndex yuvaIndices[4],
SkYUVColorSpace* yuvColorSpace) const override {
for (int i = 0; i < 4; ++i) {
proxies[i] = fProxies[i];
yuvaIndices[i] = fYUVAIndices[i];
}
*yuvColorSpace = fYUVColorSpace;
return true;
}
bool canBeMipmapped(GrContext* context) const;
/**
Create a new SkImage_GpuYUVA that's very similar to SkImage created by MakeFromYUVATextures.
The main difference is that the client doesn't have the backend textures on the gpu yet but