Some changes to GrYUVToRGBEffect.

Don't implement domains, use child effects instead.

Don't do a 4x4 matrix mul. Do 3x3 + a translate.

Undo some of effects of this effect's stint as .fp generated code.

Specify optimization flags using resulting alpha type.

Multiply computed color with input color (so this is more interchangeable
with GrSimpleTextureEffect used for non-planar texture images)

Change-Id: I9d3d0ff7aed9177cd32ab16e9ceb67d458c3d88c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/257883
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Brian Salomon 2019-12-04 16:09:04 -05:00 committed by Skia Commit-Bot
parent fae46e4043
commit fa9232ccfd
2 changed files with 120 additions and 159 deletions

View File

@ -31,30 +31,68 @@ std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::Make(const sk_sp<GrTextur
GrSamplerState::Filter subsampledPlaneFilterMode = GrSamplerState::Filter::kMipMap == filterMode GrSamplerState::Filter subsampledPlaneFilterMode = GrSamplerState::Filter::kMipMap == filterMode
? GrSamplerState::Filter::kMipMap ? GrSamplerState::Filter::kMipMap
: GrSamplerState::Filter::kBilerp; : GrSamplerState::Filter::kBilerp;
std::unique_ptr<GrFragmentProcessor> planeFPs[4];
GrSamplerState::Filter filterModes[4];
SkSize scales[4];
for (int i = 0; i < numPlanes; ++i) { for (int i = 0; i < numPlanes; ++i) {
SkISize dimensions = proxies[i]->dimensions(); SkISize dimensions = proxies[i]->dimensions();
// JPEG chroma subsampling of odd dimensions produces U and V planes with the ceiling of SkTCopyOnFirstWrite<SkMatrix> planeMatrix(&localMatrix);
// the image size divided by the subsampling factor (2). Our API for creating YUVA doesn't GrSamplerState::Filter planeFilter = filterMode;
// capture the intended subsampling (and we should fix that). This fixes up 2x subsampling SkRect planeDomain;
// for images with odd widths/heights (e.g. JPEG 420 or 422). if (dimensions != YDimensions) {
scales[i] = {dimensions.width() / SkIntToScalar(YDimensions.width()), // JPEG chroma subsampling of odd dimensions produces U and V planes with the ceiling of
dimensions.height() / SkIntToScalar(YDimensions.height())}; // the image size divided by the subsampling factor (2). Our API for creating YUVA
if ((YDimensions.width() & 0b1) && dimensions.width() == YDimensions.width() / 2 + 1) { // doesn't capture the intended subsampling (and we should fix that). This fixes up 2x
scales[i].fWidth = 0.5f; // subsampling for images with odd widths/heights (e.g. JPEG 420 or 422).
float sx = (float)dimensions.width() / YDimensions.width();
float sy = (float)dimensions.height() / YDimensions.height();
if ((YDimensions.width() & 0b1) && dimensions.width() == YDimensions.width() / 2 + 1) {
sx = 0.5f;
}
if ((YDimensions.height() & 0b1) &&
dimensions.height() == YDimensions.height() / 2 + 1) {
sy = 0.5f;
}
*planeMatrix.writable() = SkMatrix::MakeScale(sx, sy);
planeMatrix.writable()->preConcat(localMatrix);
planeFilter = subsampledPlaneFilterMode;
if (domain) {
planeDomain = {domain->fLeft * sx,
domain->fTop * sy,
domain->fRight * sx,
domain->fBottom * sy};
}
} else if (domain) {
planeDomain = *domain;
} }
if ((YDimensions.height() & 0b1) && dimensions.height() == YDimensions.height() / 2 + 1) { planeFPs[i] = GrSimpleTextureEffect::Make(proxies[i], kUnknown_SkAlphaType, *planeMatrix,
scales[i].fHeight = 0.5f; planeFilter);
if (domain) {
SkASSERT(planeFilter != GrSamplerState::Filter::kMipMap);
if (planeFilter != GrSamplerState::Filter::kNearest) {
// Inset by half a pixel for bilerp, after scaling to the size of the plane
planeDomain.inset(0.5f, 0.5f);
}
planeFPs[i] = GrDomainEffect::Make(std::move(planeFPs[i]), planeDomain,
GrTextureDomain::kClamp_Mode, false);
} }
// It seems the assumption here is the plane dimensions are smaller than the Y plane.
filterModes[i] = (dimensions == YDimensions) ? filterMode : subsampledPlaneFilterMode;
} }
return std::unique_ptr<GrFragmentProcessor>(new GrYUVtoRGBEffect( return std::unique_ptr<GrFragmentProcessor>(
proxies, scales, filterModes, numPlanes, yuvaIndices, yuvColorSpace, localMatrix, new GrYUVtoRGBEffect(planeFPs, numPlanes, yuvaIndices, yuvColorSpace));
domain)); }
static SkAlphaType alpha_type(const SkYUVAIndex yuvaIndices[4]) {
return yuvaIndices[3].fIndex >= 0 ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
}
GrYUVtoRGBEffect::GrYUVtoRGBEffect(std::unique_ptr<GrFragmentProcessor> planeFPs[4], int numPlanes,
const SkYUVAIndex yuvaIndices[4], SkYUVColorSpace yuvColorSpace)
: GrFragmentProcessor(kGrYUVtoRGBEffect_ClassID,
ModulateForClampedSamplerOptFlags(alpha_type(yuvaIndices)))
, fYUVColorSpace(yuvColorSpace) {
for (int i = 0; i < numPlanes; ++i) {
this->registerChildProcessor(std::move(planeFPs[i]));
}
std::copy_n(yuvaIndices, 4, fYUVAIndices);
} }
#ifdef SK_DEBUG #ifdef SK_DEBUG
@ -78,85 +116,77 @@ GrGLSLFragmentProcessor* GrYUVtoRGBEffect::onCreateGLSLInstance() const {
void emitCode(EmitArgs& args) override { void emitCode(EmitArgs& args) override {
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
const GrYUVtoRGBEffect& _outer = args.fFp.cast<GrYUVtoRGBEffect>(); const GrYUVtoRGBEffect& yuvEffect = args.fFp.cast<GrYUVtoRGBEffect>();
(void)_outer;
if (kIdentity_SkYUVColorSpace != _outer.yuvColorSpace()) { int numPlanes = yuvEffect.numChildProcessors();
fColorSpaceMatrixVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
kHalf4x4_GrSLType,
"colorSpaceMatrix");
}
int numSamplers = args.fTexSamplers.count();
SkString coords[4]; SkString coords[4];
for (int i = 0; i < numSamplers; ++i) { fragBuilder->codeAppendf("half4 planes[%d];", numPlanes);
coords[i] = fragBuilder->ensureCoords2D(args.fTransformedCoords[i].fVaryingPoint); for (int i = 0; i < numPlanes; ++i) {
SkString tempVar;
tempVar.printf("tmp%d", i);
this->invokeChild(i, &tempVar, args);
fragBuilder->codeAppendf("planes[%d] = %s;", i, tempVar.c_str());
} }
for (int i = 0; i < numSamplers; ++i) { bool hasAlpha = yuvEffect.fYUVAIndices[3].fIndex >= 0;
SkString sampleVar; SkString rgba[4];
sampleVar.printf("tmp%d", i); rgba[3] = "1";
fragBuilder->codeAppendf("half4 %s;", sampleVar.c_str()); for (int i = 0; i < (hasAlpha ? 4 : 3); ++i) {
fGLDomains[i].sampleTexture(fragBuilder, args.fUniformHandler, args.fShaderCaps, auto info = yuvEffect.fYUVAIndices[i];
_outer.fDomains[i], sampleVar.c_str(), coords[i], args.fTexSamplers[i]); auto letter = "rgba"[static_cast<int>(info.fChannel)];
rgba[i].printf("planes[%d].%c", info.fIndex, letter);
} }
static const char kChannelToChar[4] = { 'x', 'y', 'z', 'w' }; fragBuilder->codeAppendf("half4 color = half4(%s, %s, %s, %s);",
rgba[0].c_str(), rgba[1].c_str(), rgba[2].c_str(), rgba[3].c_str());
fragBuilder->codeAppendf( if (kIdentity_SkYUVColorSpace != yuvEffect.fYUVColorSpace) {
"half4 yuvOne = half4(tmp%d.%c, tmp%d.%c, tmp%d.%c, 1.0);", fColorSpaceMatrixVar = args.fUniformHandler->addUniform(
_outer.yuvaIndex(0).fIndex, kChannelToChar[(int)_outer.yuvaIndex(0).fChannel], kFragment_GrShaderFlag, kHalf3x3_GrSLType, "colorSpaceMatrix");
_outer.yuvaIndex(1).fIndex, kChannelToChar[(int)_outer.yuvaIndex(1).fChannel], fColorSpaceTranslateVar = args.fUniformHandler->addUniform(
_outer.yuvaIndex(2).fIndex, kChannelToChar[(int)_outer.yuvaIndex(2).fChannel]); kFragment_GrShaderFlag, kHalf3_GrSLType, "colorSpaceTranslate");
if (kIdentity_SkYUVColorSpace != _outer.yuvColorSpace()) {
SkASSERT(fColorSpaceMatrixVar.isValid());
fragBuilder->codeAppendf( fragBuilder->codeAppendf(
"yuvOne *= %s;", args.fUniformHandler->getUniformCStr(fColorSpaceMatrixVar)); "color.rgb = saturate(color.rgb * %s + %s);",
fragBuilder->codeAppend("yuvOne.xyz = clamp(yuvOne.xyz, 0, 1);"); args.fUniformHandler->getUniformCStr(fColorSpaceMatrixVar),
args.fUniformHandler->getUniformCStr(fColorSpaceTranslateVar));
} }
if (_outer.yuvaIndex(3).fIndex >= 0) { if (hasAlpha) {
fragBuilder->codeAppendf(
"half a = tmp%d.%c;", _outer.yuvaIndex(3).fIndex,
kChannelToChar[(int)_outer.yuvaIndex(3).fChannel]);
// premultiply alpha // premultiply alpha
fragBuilder->codeAppend("yuvOne *= a;"); fragBuilder->codeAppendf("color.rgb *= color.a;");
} else {
fragBuilder->codeAppend("half a = 1.0;");
} }
fragBuilder->codeAppendf("%s = color;", args.fOutputColor);
fragBuilder->codeAppendf("%s = half4(yuvOne.xyz, a);", args.fOutputColor);
} }
private: private:
void onSetData(const GrGLSLProgramDataManager& pdman, void onSetData(const GrGLSLProgramDataManager& pdman,
const GrFragmentProcessor& _proc) override { const GrFragmentProcessor& proc) override {
const GrYUVtoRGBEffect& _outer = _proc.cast<GrYUVtoRGBEffect>(); const GrYUVtoRGBEffect& yuvEffect = proc.cast<GrYUVtoRGBEffect>();
if (_outer.yuvColorSpace() != kIdentity_SkYUVColorSpace) { if (yuvEffect.fYUVColorSpace != kIdentity_SkYUVColorSpace) {
SkASSERT(fColorSpaceMatrixVar.isValid()); SkASSERT(fColorSpaceMatrixVar.isValid());
float yuvM[20]; float yuvM[20];
SkColorMatrix_YUV2RGB(_outer.yuvColorSpace(), yuvM); SkColorMatrix_YUV2RGB(yuvEffect.fYUVColorSpace, yuvM);
// Need to drop the fourth column to go to 4x4 // We drop the fourth column entirely since the transformation
float mtx[16] = { // should not depend on alpha. The fifth column is sent as a separate
yuvM[ 0], yuvM[ 1], yuvM[ 2], yuvM[ 4], // vector. The fourth row is also dropped entirely because alpha should
yuvM[ 5], yuvM[ 6], yuvM[ 7], yuvM[ 9], // never be modified.
yuvM[10], yuvM[11], yuvM[12], yuvM[14], SkASSERT(yuvM[3] == 0 && yuvM[8] == 0 && yuvM[13] == 0 && yuvM[18] == 1);
yuvM[15], yuvM[16], yuvM[17], yuvM[19], SkASSERT(yuvM[15] == 0 && yuvM[16] == 0 && yuvM[17] == 0 && yuvM[19] == 0);
float mtx[9] = {
yuvM[ 0], yuvM[ 1], yuvM[ 2],
yuvM[ 5], yuvM[ 6], yuvM[ 7],
yuvM[10], yuvM[11], yuvM[12],
}; };
pdman.setMatrix4f(fColorSpaceMatrixVar, mtx); float v[3] = {yuvM[4], yuvM[9], yuvM[14]};
} pdman.setMatrix3f(fColorSpaceMatrixVar, mtx);
pdman.set3fv(fColorSpaceTranslateVar, 1, v);
int numSamplers = _outer.numTextureSamplers();
for (int i = 0; i < numSamplers; ++i) {
fGLDomains[i].setData(pdman, _outer.fDomains[i],
_outer.textureSampler(i).proxy(), _outer.textureSampler(i).samplerState());
} }
} }
UniformHandle fColorSpaceMatrixVar; UniformHandle fColorSpaceMatrixVar;
UniformHandle fColorSpaceTranslateVar;
GrTextureDomain::GLDomain fGLDomains[4]; GrTextureDomain::GLDomain fGLDomains[4];
}; };
@ -164,33 +194,25 @@ GrGLSLFragmentProcessor* GrYUVtoRGBEffect::onCreateGLSLInstance() const {
} }
void GrYUVtoRGBEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, void GrYUVtoRGBEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
GrProcessorKeyBuilder* b) const { GrProcessorKeyBuilder* b) const {
using Domain = GrTextureDomain::GLDomain;
b->add32(this->numTextureSamplers());
uint32_t packed = 0; uint32_t packed = 0;
uint32_t domain = 0;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
if (this->yuvaIndex(i).fIndex < 0) { if (fYUVAIndices[i].fIndex < 0) {
continue; continue;
} }
uint8_t index = this->yuvaIndex(i).fIndex; uint8_t index = fYUVAIndices[i].fIndex;
uint8_t chann = (uint8_t) this->yuvaIndex(i).fChannel; uint8_t chann = static_cast<int>(fYUVAIndices[i].fChannel);
SkASSERT(index < 4 && chann < 4); SkASSERT(index < 4 && chann < 4);
packed |= (index | (chann << 2)) << (i * 4); packed |= (index | (chann << 2)) << (i * 4);
domain |= Domain::DomainKey(fDomains[i]) << (i * Domain::kDomainKeyBits);
} }
if (kIdentity_SkYUVColorSpace == this->yuvColorSpace()) { if (fYUVColorSpace == kIdentity_SkYUVColorSpace) {
packed |= 0x1 << 16; packed |= 0x1 << 16;
} }
b->add32(packed); b->add32(packed);
b->add32(domain);
} }
bool GrYUVtoRGBEffect::onIsEqual(const GrFragmentProcessor& other) const { bool GrYUVtoRGBEffect::onIsEqual(const GrFragmentProcessor& other) const {
const GrYUVtoRGBEffect& that = other.cast<GrYUVtoRGBEffect>(); const GrYUVtoRGBEffect& that = other.cast<GrYUVtoRGBEffect>();
@ -200,44 +222,23 @@ bool GrYUVtoRGBEffect::onIsEqual(const GrFragmentProcessor& other) const {
} }
} }
for (int i = 0; i < this->numTextureSamplers(); ++i) {
// 'fSamplers' is checked by the base class
if (fSamplerTransforms[i] != that.fSamplerTransforms[i]) {
return false;
}
if (!(fDomains[i] == that.fDomains[i])) {
return false;
}
}
if (fYUVColorSpace != that.fYUVColorSpace) { if (fYUVColorSpace != that.fYUVColorSpace) {
return false; return false;
} }
return true; return true;
} }
GrYUVtoRGBEffect::GrYUVtoRGBEffect(const GrYUVtoRGBEffect& src) GrYUVtoRGBEffect::GrYUVtoRGBEffect(const GrYUVtoRGBEffect& src)
: INHERITED(kGrYUVtoRGBEffect_ClassID, src.optimizationFlags()) : GrFragmentProcessor(kGrYUVtoRGBEffect_ClassID, src.optimizationFlags())
, fDomains{src.fDomains[0], src.fDomains[1], src.fDomains[2], src.fDomains[3]}
, fYUVColorSpace(src.fYUVColorSpace) { , fYUVColorSpace(src.fYUVColorSpace) {
int numPlanes = src.numTextureSamplers(); int numPlanes = src.numChildProcessors();
for (int i = 0; i < numPlanes; ++i) { for (int i = 0; i < numPlanes; ++i) {
fSamplers[i].reset(sk_ref_sp(src.fSamplers[i].proxy()), src.fSamplers[i].samplerState()); this->registerChildProcessor(this->childProcessor(i).clone());
fSamplerTransforms[i] = src.fSamplerTransforms[i];
fSamplerCoordTransforms[i] = src.fSamplerCoordTransforms[i];
} }
std::copy_n(src.fYUVAIndices, this->numChildProcessors(), fYUVAIndices);
this->setTextureSamplerCnt(numPlanes);
for (int i = 0; i < numPlanes; ++i) {
this->addCoordTransform(&fSamplerCoordTransforms[i]);
}
memcpy(fYUVAIndices, src.fYUVAIndices, sizeof(fYUVAIndices));
} }
std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::clone() const { std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::clone() const {
return std::unique_ptr<GrFragmentProcessor>(new GrYUVtoRGBEffect(*this)); return std::unique_ptr<GrFragmentProcessor>(new GrYUVtoRGBEffect(*this));
} }
const GrFragmentProcessor::TextureSampler& GrYUVtoRGBEffect::onTextureSampler(int index) const {
SkASSERT(index < this->numTextureSamplers());
return fSamplers[index];
}

View File

@ -32,65 +32,25 @@ public:
SkString dumpInfo() const override; SkString dumpInfo() const override;
#endif #endif
SkYUVColorSpace yuvColorSpace() const { return fYUVColorSpace; }
const SkYUVAIndex& yuvaIndex(int i) const { return fYUVAIndices[i]; }
GrYUVtoRGBEffect(const GrYUVtoRGBEffect& src);
std::unique_ptr<GrFragmentProcessor> clone() const override; std::unique_ptr<GrFragmentProcessor> clone() const override;
const char* name() const override { return "YUVtoRGBEffect"; } const char* name() const override { return "YUVtoRGBEffect"; }
private: private:
GrYUVtoRGBEffect(const sk_sp<GrTextureProxy> proxies[], const SkSize scales[], GrYUVtoRGBEffect(std::unique_ptr<GrFragmentProcessor> planeFPs[4], int numPlanes,
const GrSamplerState::Filter filterModes[], int numPlanes, const SkYUVAIndex yuvaIndices[4], SkYUVColorSpace yuvColorSpace);
const SkYUVAIndex yuvaIndices[4], SkYUVColorSpace yuvColorSpace,
const SkMatrix& localMatrix, const SkRect* domain)
: INHERITED(kGrYUVtoRGBEffect_ClassID, kNone_OptimizationFlags)
, fDomains{GrTextureDomain::IgnoredDomain(), GrTextureDomain::IgnoredDomain(),
GrTextureDomain::IgnoredDomain(), GrTextureDomain::IgnoredDomain()}
, fYUVColorSpace(yuvColorSpace) {
for (int i = 0; i < numPlanes; ++i) {
SkMatrix planeMatrix = SkMatrix::MakeScale(scales[i].width(), scales[i].height());
if (domain) {
SkASSERT(filterModes[i] != GrSamplerState::Filter::kMipMap);
SkRect scaledDomain = planeMatrix.mapRect(*domain); GrYUVtoRGBEffect(const GrYUVtoRGBEffect& src);
if (filterModes[i] != GrSamplerState::Filter::kNearest) {
// Inset by half a pixel for bilerp, after scaling to the size of the plane
scaledDomain.inset(0.5f, 0.5f);
}
fDomains[i] = GrTextureDomain(proxies[i].get(), scaledDomain,
GrTextureDomain::kClamp_Mode, GrTextureDomain::kClamp_Mode, i);
}
planeMatrix.preConcat(localMatrix);
fSamplers[i].reset(std::move(proxies[i]),
GrSamplerState(GrSamplerState::WrapMode::kClamp, filterModes[i]));
fSamplerTransforms[i] = planeMatrix;
fSamplerCoordTransforms[i] =
GrCoordTransform(fSamplerTransforms[i], fSamplers[i].proxy());
}
this->setTextureSamplerCnt(numPlanes);
for (int i = 0; i < numPlanes; ++i) {
this->addCoordTransform(&fSamplerCoordTransforms[i]);
}
memcpy(fYUVAIndices, yuvaIndices, sizeof(fYUVAIndices));
}
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
bool onIsEqual(const GrFragmentProcessor&) const override; bool onIsEqual(const GrFragmentProcessor&) const override;
const TextureSampler& onTextureSampler(int) const override;
GR_DECLARE_FRAGMENT_PROCESSOR_TEST GR_DECLARE_FRAGMENT_PROCESSOR_TEST
TextureSampler fSamplers[4];
SkMatrix44 fSamplerTransforms[4];
GrCoordTransform fSamplerCoordTransforms[4];
GrTextureDomain fDomains[4];
SkYUVAIndex fYUVAIndices[4]; SkYUVAIndex fYUVAIndices[4];
SkYUVColorSpace fYUVColorSpace; SkYUVColorSpace fYUVColorSpace;
typedef GrFragmentProcessor INHERITED;
}; };
#endif #endif