diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index 08198beb59..158699b837 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -313,8 +313,8 @@ private: bool fDisableGpuYUVConversion; bool fDidTestPMConversions; - int fPMToUPMConversion; - int fUPMToPMConversion; + // true if the PM/UPM conversion succeeded; false otherwise + bool fPMUPMConversionsRoundTrip; // In debug builds we guard against improper thread handling // This guard is passed to the GrDrawingManager and, from there to all the diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 0edfb298e0..02ffe3c8a4 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -878,16 +878,6 @@ bool GrContext::abandoned() const { return fDrawingManager->wasAbandoned(); } -namespace { -void test_pm_conversions(GrContext* ctx, int* pmToUPMValue, int* upmToPMValue) { - GrConfigConversionEffect::PMConversion pmToUPM; - GrConfigConversionEffect::PMConversion upmToPM; - GrConfigConversionEffect::TestForPreservingPMConversions(ctx, &pmToUPM, &upmToPM); - *pmToUPMValue = pmToUPM; - *upmToPMValue = upmToPM; -} -} - sk_sp GrContext::createPMToUPMEffect(sk_sp fp, bool useConfigConversionEffect) { ASSERT_SINGLE_OWNER @@ -898,9 +888,8 @@ sk_sp GrContext::createPMToUPMEffect(sk_spvalidPMUPMConversionExists()); - GrConfigConversionEffect::PMConversion pmToUPM = - static_cast(fPMToUPMConversion); - return GrConfigConversionEffect::Make(std::move(fp), pmToUPM); + return GrConfigConversionEffect::Make(std::move(fp), + GrConfigConversionEffect::kToUnpremul_PMConversion); } else { // For everything else (sRGB, half-float, etc...), it doesn't make sense to try and // explicitly round the results. Just do the obvious, naive thing in the shader. @@ -918,9 +907,8 @@ sk_sp GrContext::createUPMToPMEffect(sk_spvalidPMUPMConversionExists()); - GrConfigConversionEffect::PMConversion upmToPM = - static_cast(fUPMToPMConversion); - return GrConfigConversionEffect::Make(std::move(fp), upmToPM); + return GrConfigConversionEffect::Make(std::move(fp), + GrConfigConversionEffect::kToPremul_PMConversion); } else { // For everything else (sRGB, half-float, etc...), it doesn't make sense to try and // explicitly round the results. Just do the obvious, naive thing in the shader. @@ -931,12 +919,12 @@ sk_sp GrContext::createUPMToPMEffect(sk_spUPM tests fail or succeed together so we only need to check one. - return GrConfigConversionEffect::kPMConversionCnt != fPMToUPMConversion; + return fPMUPMConversionsRoundTrip; } ////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp index 566a1afac8..2e0886f698 100644 --- a/src/gpu/effects/GrConfigConversionEffect.cpp +++ b/src/gpu/effects/GrConfigConversionEffect.cpp @@ -30,36 +30,15 @@ public: // Aggressively round to the nearest exact (N / 255) floating point value. This lets us // find a round-trip preserving pair on some GPUs that do odd byte to float conversion. - fragBuilder->codeAppendf("vec4 color = floor(%s * 255.0 + 0.5) / 255.0;", - args.fInputColor); + fragBuilder->codeAppendf("vec4 color = floor(%s * 255.0 + 0.5) / 255.0;", args.fInputColor); switch (cce.pmConversion()) { - case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion: - fragBuilder->codeAppend( - "color.rgb = ceil(color.rgb * color.a * 255.0) / 255.0;"); - break; - case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion: - // Add a compensation(0.001) here to avoid the side effect of the floor operation. - // In Intel GPUs, the integer value converted from floor(%s.r * 255.0) / 255.0 - // is less than the integer value converted from %s.r by 1 when the %s.r is - // converted from the integer value 2^n, such as 1, 2, 4, 8, etc. - fragBuilder->codeAppend( - "color.rgb = floor(color.rgb * color.a * 255.0 + 0.001) / 255.0;"); - break; - case GrConfigConversionEffect::kMulByAlpha_RoundNearest_PMConversion: + case GrConfigConversionEffect::kToPremul_PMConversion: fragBuilder->codeAppend( "color.rgb = floor(color.rgb * color.a * 255.0 + 0.5) / 255.0;"); break; - case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion: - fragBuilder->codeAppend( - "color.rgb = color.a <= 0.0 ? vec3(0,0,0) : ceil(color.rgb / color.a * 255.0) / 255.0;"); - break; - case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion: - fragBuilder->codeAppend( - "color.rgb = color.a <= 0.0 ? vec3(0,0,0) : floor(color.rgb / color.a * 255.0) / 255.0;"); - break; - case GrConfigConversionEffect::kDivByAlpha_RoundNearest_PMConversion: + case GrConfigConversionEffect::kToUnpremul_PMConversion: fragBuilder->codeAppend( "color.rgb = color.a <= 0.0 ? vec3(0,0,0) : floor(color.rgb / color.a * 255.0 + 0.5) / 255.0;"); break; @@ -119,12 +98,7 @@ GrGLSLFragmentProcessor* GrConfigConversionEffect::onCreateGLSLInstance() const } - -void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context, - PMConversion* pmToUPMRule, - PMConversion* upmToPMRule) { - *pmToUPMRule = kPMConversionCnt; - *upmToPMRule = kPMConversionCnt; +bool GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context) { static constexpr int kSize = 256; static constexpr GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig; SkAutoTMalloc data(kSize * kSize * 3); @@ -153,8 +127,8 @@ void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context sk_sp tempRTC(context->makeRenderTargetContext(SkBackingFit::kExact, kSize, kSize, kConfig, nullptr)); - if (!readRTC || !tempRTC) { - return; + if (!readRTC || !readRTC->asTextureProxy() || !tempRTC) { + return false; } GrSurfaceDesc desc; desc.fWidth = kSize; @@ -165,90 +139,57 @@ void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context sk_sp dataProxy = GrSurfaceProxy::MakeDeferred(resourceProvider, desc, SkBudgeted::kYes, data, 0); if (!dataProxy) { - return; + return false; } - static const PMConversion kConversionRules[][2] = { - {kDivByAlpha_RoundNearest_PMConversion, kMulByAlpha_RoundNearest_PMConversion}, - {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion}, - {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion}, - }; + static const SkRect kRect = SkRect::MakeIWH(kSize, kSize); - uint32_t bestFailCount = 0xFFFFFFFF; - size_t bestRule = 0; + // 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. - for (size_t i = 0; i < SK_ARRAY_COUNT(kConversionRules) && bestFailCount; ++i) { - *pmToUPMRule = kConversionRules[i][0]; - *upmToPMRule = kConversionRules[i][1]; + GrPaint paint1; + GrPaint paint2; + GrPaint paint3; + sk_sp pmToUPM(new GrConfigConversionEffect(kToUnpremul_PMConversion)); + sk_sp upmToPM(new GrConfigConversionEffect(kToPremul_PMConversion)); - static const SkRect kDstRect = SkRect::MakeIWH(kSize, kSize); - static const SkRect kSrcRect = 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. + paint1.addColorTextureProcessor(resourceProvider, dataProxy, nullptr, SkMatrix::I()); + paint1.addColorFragmentProcessor(pmToUPM); + paint1.setPorterDuffXPFactory(SkBlendMode::kSrc); - if (!readRTC->asTextureProxy()) { - continue; - } - GrPaint paint1; - GrPaint paint2; - GrPaint paint3; - sk_sp pmToUPM(new GrConfigConversionEffect(*pmToUPMRule)); - sk_sp upmToPM(new GrConfigConversionEffect(*upmToPMRule)); + readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kRect, kRect); + if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) { + return false; + } - paint1.addColorTextureProcessor(resourceProvider, dataProxy, nullptr, SkMatrix::I()); - paint1.addColorFragmentProcessor(pmToUPM); - paint1.setPorterDuffXPFactory(SkBlendMode::kSrc); + paint2.addColorTextureProcessor(resourceProvider, readRTC->asTextureProxyRef(), nullptr, + SkMatrix::I()); + paint2.addColorFragmentProcessor(std::move(upmToPM)); + paint2.setPorterDuffXPFactory(SkBlendMode::kSrc); - readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kDstRect, - kSrcRect); + tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect, kRect); - if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) { - continue; - } + paint3.addColorTextureProcessor(resourceProvider, tempRTC->asTextureProxyRef(), nullptr, + SkMatrix::I()); + paint3.addColorFragmentProcessor(std::move(pmToUPM)); + paint3.setPorterDuffXPFactory(SkBlendMode::kSrc); - paint2.addColorTextureProcessor(resourceProvider, readRTC->asTextureProxyRef(), nullptr, - SkMatrix::I()); - paint2.addColorFragmentProcessor(std::move(upmToPM)); - paint2.setPorterDuffXPFactory(SkBlendMode::kSrc); + readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kRect, kRect); - tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kDstRect, - kSrcRect); + if (!readRTC->readPixels(ii, secondRead, 0, 0, 0)) { + return false; + } - paint3.addColorTextureProcessor(resourceProvider, tempRTC->asTextureProxyRef(), nullptr, - SkMatrix::I()); - paint3.addColorFragmentProcessor(std::move(pmToUPM)); - paint3.setPorterDuffXPFactory(SkBlendMode::kSrc); - - readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kDstRect, - kSrcRect); - - if (!readRTC->readPixels(ii, secondRead, 0, 0, 0)) { - continue; - } - - uint32_t failCount = 0; - for (int y = 0; y < kSize; ++y) { - for (int x = 0; x <= y; ++x) { - if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) { - if (++failCount >= bestFailCount) { - break; - } - } + 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; } } - if (failCount < bestFailCount) { - bestFailCount = failCount; - bestRule = i; - } - } - if (bestFailCount > 0) { - *pmToUPMRule = kPMConversionCnt; - *upmToPMRule = kPMConversionCnt; - } else { - *pmToUPMRule = kConversionRules[bestRule][0]; - *upmToPMRule = kConversionRules[bestRule][1]; } + + return true; } sk_sp GrConfigConversionEffect::Make(sk_sp fp, diff --git a/src/gpu/effects/GrConfigConversionEffect.h b/src/gpu/effects/GrConfigConversionEffect.h index 4cc2aeb80a..4b0c02dc96 100644 --- a/src/gpu/effects/GrConfigConversionEffect.h +++ b/src/gpu/effects/GrConfigConversionEffect.h @@ -20,14 +20,8 @@ public: * The PM->UPM or UPM->PM conversions to apply. */ enum PMConversion { - kMulByAlpha_RoundUp_PMConversion = 0, - kMulByAlpha_RoundDown_PMConversion, - kMulByAlpha_RoundNearest_PMConversion, - - kDivByAlpha_RoundUp_PMConversion, - kDivByAlpha_RoundDown_PMConversion, - kDivByAlpha_RoundNearest_PMConversion, - + kToPremul_PMConversion = 0, + kToUnpremul_PMConversion, kPMConversionCnt }; @@ -46,9 +40,8 @@ public: // if pixels are read back to a UPM buffer, written back to PM to the GPU, and read back again // both reads will produce the same result. This test is quite expensive and should not be run // multiple times for a given context. - static void TestForPreservingPMConversions(GrContext* context, - PMConversion* PMToUPMRule, - PMConversion* UPMToPMRule); + static bool TestForPreservingPMConversions(GrContext* context); + private: GrConfigConversionEffect(PMConversion);