Remove all config conversion modes except round-to-nearest

On all GPUs where we can perfectly round-trip, this mode does so.
This mode fails on Mali 400 and Tegra 3, but nothing works there.

Bug: skia:
Change-Id: Ifb045fc772a5b1c03b51b5cb2ae039fe792d17bb
Reviewed-on: https://skia-review.googlesource.com/13271
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Brian Osman 2017-04-20 10:24:36 -04:00 committed by Skia Commit-Bot
parent 26eb16f1e3
commit 28804f3571
4 changed files with 54 additions and 132 deletions

View File

@ -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

View File

@ -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<GrFragmentProcessor> GrContext::createPMToUPMEffect(sk_sp<GrFragmentProcessor> fp,
bool useConfigConversionEffect) {
ASSERT_SINGLE_OWNER
@ -898,9 +888,8 @@ sk_sp<GrFragmentProcessor> GrContext::createPMToUPMEffect(sk_sp<GrFragmentProces
// ...and it should have succeeded
SkASSERT(this->validPMUPMConversionExists());
GrConfigConversionEffect::PMConversion pmToUPM =
static_cast<GrConfigConversionEffect::PMConversion>(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<GrFragmentProcessor> GrContext::createUPMToPMEffect(sk_sp<GrFragmentProces
// ...and it should have succeeded
SkASSERT(this->validPMUPMConversionExists());
GrConfigConversionEffect::PMConversion upmToPM =
static_cast<GrConfigConversionEffect::PMConversion>(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<GrFragmentProcessor> GrContext::createUPMToPMEffect(sk_sp<GrFragmentProces
bool GrContext::validPMUPMConversionExists() {
ASSERT_SINGLE_OWNER
if (!fDidTestPMConversions) {
test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion);
fPMUPMConversionsRoundTrip = GrConfigConversionEffect::TestForPreservingPMConversions(this);
fDidTestPMConversions = true;
}
// The PM<->UPM tests fail or succeed together so we only need to check one.
return GrConfigConversionEffect::kPMConversionCnt != fPMToUPMConversion;
return fPMUPMConversionsRoundTrip;
}
//////////////////////////////////////////////////////////////////////////////

View File

@ -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<uint32_t> data(kSize * kSize * 3);
@ -153,8 +127,8 @@ void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context
sk_sp<GrRenderTargetContext> 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<GrTextureProxy> 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<GrFragmentProcessor> pmToUPM(new GrConfigConversionEffect(kToUnpremul_PMConversion));
sk_sp<GrFragmentProcessor> 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<GrFragmentProcessor> pmToUPM(new GrConfigConversionEffect(*pmToUPMRule));
sk_sp<GrFragmentProcessor> 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<GrFragmentProcessor> GrConfigConversionEffect::Make(sk_sp<GrFragmentProcessor> fp,

View File

@ -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);