Update GrConfigConversionEffect to use an input FP.
In a strange twist, this effect already exposed a required input FP to
external callers, but implemented its behavior using RunInSeries. This
doesn't appear to have imparted any benefit, compared to sampling from
a child FP in the usual way.
Additionally, moved a 100-line test function `GrConfigConversionEffect::
TestForPreservingPMConversions` out of the header and into the CPP.
Change-Id: Ice8c1f1e2cb76177a8a2bf6cfc3e3d163854df64
Bug: skia:10217
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/300908
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/effects/generated/GrConfigConversionEffect.cpp b/src/gpu/effects/generated/GrConfigConversionEffect.cpp
index 0ec66fd..9f3422a 100644
--- a/src/gpu/effects/generated/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/generated/GrConfigConversionEffect.cpp
@@ -27,6 +27,8 @@
(void)pmConversion;
fragBuilder->forceHighPrecision();
+ SkString _sample5730;
+ _sample5730 = this->invokeChild(_outer.inputFP_index, args);
fragBuilder->codeAppendf(
R"SkSL(%s = floor(%s * 255.0 + 0.5) / 255.0;
@switch (%d) {
@@ -38,7 +40,7 @@
break;
}
)SkSL",
- args.fOutputColor, args.fInputColor, (int)_outer.pmConversion, args.fOutputColor,
+ args.fOutputColor, _sample5730.c_str(), (int)_outer.pmConversion, args.fOutputColor,
args.fOutputColor, args.fOutputColor, args.fOutputColor, args.fOutputColor,
args.fOutputColor, args.fOutputColor);
}
@@ -62,7 +64,9 @@
}
GrConfigConversionEffect::GrConfigConversionEffect(const GrConfigConversionEffect& src)
: INHERITED(kGrConfigConversionEffect_ClassID, src.optimizationFlags())
- , pmConversion(src.pmConversion) {}
+ , pmConversion(src.pmConversion) {
+ { inputFP_index = this->cloneAndRegisterChildProcessor(src.childProcessor(src.inputFP_index)); }
+}
std::unique_ptr<GrFragmentProcessor> GrConfigConversionEffect::clone() const {
return std::unique_ptr<GrFragmentProcessor>(new GrConfigConversionEffect(*this));
}
@@ -72,6 +76,109 @@
GrProcessorTestData* data) {
PMConversion pmConv = static_cast<PMConversion>(
data->fRandom->nextULessThan((int)PMConversion::kPMConversionCnt));
- return std::unique_ptr<GrFragmentProcessor>(new GrConfigConversionEffect(pmConv));
+ return std::unique_ptr<GrFragmentProcessor>(
+ new GrConfigConversionEffect(GrProcessorUnitTest::MakeChildFP(data), pmConv));
}
#endif
+
+bool GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context) {
+ static constexpr int kSize = 256;
+ static constexpr GrColorType kColorType = GrColorType::kRGBA_8888;
+ SkAutoTMalloc<uint32_t> data(kSize * kSize * 3);
+ uint32_t* srcData = data.get();
+ uint32_t* firstRead = data.get() + kSize * kSize;
+ uint32_t* secondRead = data.get() + 2 * kSize * kSize;
+
+ // Fill with every possible premultiplied A, color channel value. There will be 256-y
+ // duplicate values in row y. We set r, g, and b to the same value since they are handled
+ // identically.
+ for (int y = 0; y < kSize; ++y) {
+ for (int x = 0; x < kSize; ++x) {
+ uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[kSize * y + x]);
+ color[3] = y;
+ color[2] = std::min(x, y);
+ color[1] = std::min(x, y);
+ color[0] = std::min(x, y);
+ }
+ }
+ memset(firstRead, 0, kSize * kSize * sizeof(uint32_t));
+ memset(secondRead, 0, kSize * kSize * sizeof(uint32_t));
+
+ const SkImageInfo ii =
+ SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
+ auto readRTC = GrRenderTargetContext::Make(context, kColorType, nullptr, SkBackingFit::kExact,
+ {kSize, kSize});
+ auto tempRTC = GrRenderTargetContext::Make(context, kColorType, nullptr, SkBackingFit::kExact,
+ {kSize, kSize});
+ if (!readRTC || !readRTC->asTextureProxy() || !tempRTC) {
+ return false;
+ }
+ // Adding discard to appease vulkan validation warning about loading uninitialized data on
+ // draw
+ readRTC->discard();
+
+ // This function is only ever called if we are in a GrContext that has a GrGpu since we are
+ // calling read pixels here. Thus the pixel data will be uploaded immediately and we don't
+ // need to keep the pixel data alive in the proxy. Therefore the ReleaseProc is nullptr.
+ SkBitmap bitmap;
+ bitmap.installPixels(ii, srcData, 4 * kSize);
+ bitmap.setImmutable();
+
+ GrBitmapTextureMaker maker(context, bitmap, GrImageTexGenPolicy::kNew_Uncached_Budgeted);
+ auto dataView = maker.view(GrMipMapped::kNo);
+ if (!dataView) {
+ return false;
+ }
+
+ static const SkRect kRect = SkRect::MakeIWH(kSize, kSize);
+
+ // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
+ // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
+ // We then verify that two reads produced the same values.
+
+ GrPaint paint1;
+ paint1.addColorFragmentProcessor(GrConfigConversionEffect::Make(
+ GrTextureEffect::Make(std::move(dataView), kPremul_SkAlphaType),
+ PMConversion::kToUnpremul));
+ paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+ readRTC->fillRectToRect(nullptr, std::move(paint1), GrAA::kNo, SkMatrix::I(), kRect, kRect);
+ if (!readRTC->readPixels(ii, firstRead, 0, {0, 0})) {
+ return false;
+ }
+
+ // Adding discard to appease vulkan validation warning about loading uninitialized data on
+ // draw
+ tempRTC->discard();
+
+ GrPaint paint2;
+ paint2.addColorFragmentProcessor(GrConfigConversionEffect::Make(
+ GrTextureEffect::Make(readRTC->readSurfaceView(), kUnpremul_SkAlphaType),
+ PMConversion::kToPremul));
+ paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+ tempRTC->fillRectToRect(nullptr, std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect, kRect);
+
+ GrPaint paint3;
+ paint3.addColorFragmentProcessor(GrConfigConversionEffect::Make(
+ GrTextureEffect::Make(tempRTC->readSurfaceView(), kPremul_SkAlphaType),
+ PMConversion::kToUnpremul));
+ paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
+
+ readRTC->fillRectToRect(nullptr, std::move(paint3), GrAA::kNo, SkMatrix::I(), kRect, kRect);
+
+ if (!readRTC->readPixels(ii, secondRead, 0, {0, 0})) {
+ return false;
+ }
+
+ for (int y = 0; y < kSize; ++y) {
+ for (int x = 0; x <= y; ++x) {
+ if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}