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::kMipMap
: GrSamplerState::Filter::kBilerp;
GrSamplerState::Filter filterModes[4];
SkSize scales[4];
std::unique_ptr<GrFragmentProcessor> planeFPs[4];
for (int i = 0; i < numPlanes; ++i) {
SkISize dimensions = proxies[i]->dimensions();
SkTCopyOnFirstWrite<SkMatrix> planeMatrix(&localMatrix);
GrSamplerState::Filter planeFilter = filterMode;
SkRect planeDomain;
if (dimensions != YDimensions) {
// JPEG chroma subsampling of odd dimensions produces U and V planes with the ceiling of
// the image size divided by the subsampling factor (2). Our API for creating YUVA doesn't
// capture the intended subsampling (and we should fix that). This fixes up 2x subsampling
// for images with odd widths/heights (e.g. JPEG 420 or 422).
scales[i] = {dimensions.width() / SkIntToScalar(YDimensions.width()),
dimensions.height() / SkIntToScalar(YDimensions.height())};
// the image size divided by the subsampling factor (2). Our API for creating YUVA
// doesn't capture the intended subsampling (and we should fix that). This fixes up 2x
// 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) {
scales[i].fWidth = 0.5f;
sx = 0.5f;
}
if ((YDimensions.height() & 0b1) && dimensions.height() == YDimensions.height() / 2 + 1) {
scales[i].fHeight = 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;
}
planeFPs[i] = GrSimpleTextureEffect::Make(proxies[i], kUnknown_SkAlphaType, *planeMatrix,
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(
proxies, scales, filterModes, numPlanes, yuvaIndices, yuvColorSpace, localMatrix,
domain));
return std::unique_ptr<GrFragmentProcessor>(
new GrYUVtoRGBEffect(planeFPs, numPlanes, yuvaIndices, yuvColorSpace));
}
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
@ -78,85 +116,77 @@ GrGLSLFragmentProcessor* GrYUVtoRGBEffect::onCreateGLSLInstance() const {
void emitCode(EmitArgs& args) override {
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
const GrYUVtoRGBEffect& _outer = args.fFp.cast<GrYUVtoRGBEffect>();
(void)_outer;
const GrYUVtoRGBEffect& yuvEffect = args.fFp.cast<GrYUVtoRGBEffect>();
if (kIdentity_SkYUVColorSpace != _outer.yuvColorSpace()) {
fColorSpaceMatrixVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
kHalf4x4_GrSLType,
"colorSpaceMatrix");
}
int numSamplers = args.fTexSamplers.count();
int numPlanes = yuvEffect.numChildProcessors();
SkString coords[4];
for (int i = 0; i < numSamplers; ++i) {
coords[i] = fragBuilder->ensureCoords2D(args.fTransformedCoords[i].fVaryingPoint);
fragBuilder->codeAppendf("half4 planes[%d];", numPlanes);
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) {
SkString sampleVar;
sampleVar.printf("tmp%d", i);
fragBuilder->codeAppendf("half4 %s;", sampleVar.c_str());
fGLDomains[i].sampleTexture(fragBuilder, args.fUniformHandler, args.fShaderCaps,
_outer.fDomains[i], sampleVar.c_str(), coords[i], args.fTexSamplers[i]);
bool hasAlpha = yuvEffect.fYUVAIndices[3].fIndex >= 0;
SkString rgba[4];
rgba[3] = "1";
for (int i = 0; i < (hasAlpha ? 4 : 3); ++i) {
auto info = yuvEffect.fYUVAIndices[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());
if (kIdentity_SkYUVColorSpace != yuvEffect.fYUVColorSpace) {
fColorSpaceMatrixVar = args.fUniformHandler->addUniform(
kFragment_GrShaderFlag, kHalf3x3_GrSLType, "colorSpaceMatrix");
fColorSpaceTranslateVar = args.fUniformHandler->addUniform(
kFragment_GrShaderFlag, kHalf3_GrSLType, "colorSpaceTranslate");
fragBuilder->codeAppendf(
"half4 yuvOne = half4(tmp%d.%c, tmp%d.%c, tmp%d.%c, 1.0);",
_outer.yuvaIndex(0).fIndex, kChannelToChar[(int)_outer.yuvaIndex(0).fChannel],
_outer.yuvaIndex(1).fIndex, kChannelToChar[(int)_outer.yuvaIndex(1).fChannel],
_outer.yuvaIndex(2).fIndex, kChannelToChar[(int)_outer.yuvaIndex(2).fChannel]);
if (kIdentity_SkYUVColorSpace != _outer.yuvColorSpace()) {
SkASSERT(fColorSpaceMatrixVar.isValid());
fragBuilder->codeAppendf(
"yuvOne *= %s;", args.fUniformHandler->getUniformCStr(fColorSpaceMatrixVar));
fragBuilder->codeAppend("yuvOne.xyz = clamp(yuvOne.xyz, 0, 1);");
"color.rgb = saturate(color.rgb * %s + %s);",
args.fUniformHandler->getUniformCStr(fColorSpaceMatrixVar),
args.fUniformHandler->getUniformCStr(fColorSpaceTranslateVar));
}
if (_outer.yuvaIndex(3).fIndex >= 0) {
fragBuilder->codeAppendf(
"half a = tmp%d.%c;", _outer.yuvaIndex(3).fIndex,
kChannelToChar[(int)_outer.yuvaIndex(3).fChannel]);
if (hasAlpha) {
// premultiply alpha
fragBuilder->codeAppend("yuvOne *= a;");
} else {
fragBuilder->codeAppend("half a = 1.0;");
fragBuilder->codeAppendf("color.rgb *= color.a;");
}
fragBuilder->codeAppendf("%s = half4(yuvOne.xyz, a);", args.fOutputColor);
fragBuilder->codeAppendf("%s = color;", args.fOutputColor);
}
private:
void onSetData(const GrGLSLProgramDataManager& pdman,
const GrFragmentProcessor& _proc) override {
const GrYUVtoRGBEffect& _outer = _proc.cast<GrYUVtoRGBEffect>();
const GrFragmentProcessor& proc) override {
const GrYUVtoRGBEffect& yuvEffect = proc.cast<GrYUVtoRGBEffect>();
if (_outer.yuvColorSpace() != kIdentity_SkYUVColorSpace) {
if (yuvEffect.fYUVColorSpace != kIdentity_SkYUVColorSpace) {
SkASSERT(fColorSpaceMatrixVar.isValid());
float yuvM[20];
SkColorMatrix_YUV2RGB(_outer.yuvColorSpace(), yuvM);
// Need to drop the fourth column to go to 4x4
float mtx[16] = {
yuvM[ 0], yuvM[ 1], yuvM[ 2], yuvM[ 4],
yuvM[ 5], yuvM[ 6], yuvM[ 7], yuvM[ 9],
yuvM[10], yuvM[11], yuvM[12], yuvM[14],
yuvM[15], yuvM[16], yuvM[17], yuvM[19],
SkColorMatrix_YUV2RGB(yuvEffect.fYUVColorSpace, yuvM);
// We drop the fourth column entirely since the transformation
// should not depend on alpha. The fifth column is sent as a separate
// vector. The fourth row is also dropped entirely because alpha should
// never be modified.
SkASSERT(yuvM[3] == 0 && yuvM[8] == 0 && yuvM[13] == 0 && yuvM[18] == 1);
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);
}
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());
float v[3] = {yuvM[4], yuvM[9], yuvM[14]};
pdman.setMatrix3f(fColorSpaceMatrixVar, mtx);
pdman.set3fv(fColorSpaceTranslateVar, 1, v);
}
}
UniformHandle fColorSpaceMatrixVar;
UniformHandle fColorSpaceTranslateVar;
GrTextureDomain::GLDomain fGLDomains[4];
};
@ -164,33 +194,25 @@ GrGLSLFragmentProcessor* GrYUVtoRGBEffect::onCreateGLSLInstance() const {
}
void GrYUVtoRGBEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
GrProcessorKeyBuilder* b) const {
using Domain = GrTextureDomain::GLDomain;
b->add32(this->numTextureSamplers());
uint32_t packed = 0;
uint32_t domain = 0;
for (int i = 0; i < 4; ++i) {
if (this->yuvaIndex(i).fIndex < 0) {
if (fYUVAIndices[i].fIndex < 0) {
continue;
}
uint8_t index = this->yuvaIndex(i).fIndex;
uint8_t chann = (uint8_t) this->yuvaIndex(i).fChannel;
uint8_t index = fYUVAIndices[i].fIndex;
uint8_t chann = static_cast<int>(fYUVAIndices[i].fChannel);
SkASSERT(index < 4 && chann < 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;
}
b->add32(packed);
b->add32(domain);
}
bool GrYUVtoRGBEffect::onIsEqual(const GrFragmentProcessor& other) const {
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) {
return false;
}
return true;
}
GrYUVtoRGBEffect::GrYUVtoRGBEffect(const GrYUVtoRGBEffect& src)
: INHERITED(kGrYUVtoRGBEffect_ClassID, src.optimizationFlags())
, fDomains{src.fDomains[0], src.fDomains[1], src.fDomains[2], src.fDomains[3]}
: GrFragmentProcessor(kGrYUVtoRGBEffect_ClassID, src.optimizationFlags())
, fYUVColorSpace(src.fYUVColorSpace) {
int numPlanes = src.numTextureSamplers();
int numPlanes = src.numChildProcessors();
for (int i = 0; i < numPlanes; ++i) {
fSamplers[i].reset(sk_ref_sp(src.fSamplers[i].proxy()), src.fSamplers[i].samplerState());
fSamplerTransforms[i] = src.fSamplerTransforms[i];
fSamplerCoordTransforms[i] = src.fSamplerCoordTransforms[i];
this->registerChildProcessor(this->childProcessor(i).clone());
}
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 {
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;
#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;
const char* name() const override { return "YUVtoRGBEffect"; }
private:
GrYUVtoRGBEffect(const sk_sp<GrTextureProxy> proxies[], const SkSize scales[],
const GrSamplerState::Filter filterModes[], int numPlanes,
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);
GrYUVtoRGBEffect(std::unique_ptr<GrFragmentProcessor> planeFPs[4], int numPlanes,
const SkYUVAIndex yuvaIndices[4], SkYUVColorSpace yuvColorSpace);
SkRect scaledDomain = planeMatrix.mapRect(*domain);
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);
}
GrYUVtoRGBEffect(const GrYUVtoRGBEffect& src);
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;
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
bool onIsEqual(const GrFragmentProcessor&) const override;
const TextureSampler& onTextureSampler(int) const override;
GR_DECLARE_FRAGMENT_PROCESSOR_TEST
TextureSampler fSamplers[4];
SkMatrix44 fSamplerTransforms[4];
GrCoordTransform fSamplerCoordTransforms[4];
GrTextureDomain fDomains[4];
SkYUVAIndex fYUVAIndices[4];
SkYUVColorSpace fYUVColorSpace;
typedef GrFragmentProcessor INHERITED;
};
#endif