Skip to content

Commit

Permalink
Add tolerance to ProcessorCloneTest and dump images on failure.
Browse files Browse the repository at this point in the history
Change-Id: Id36aef0392f03aafe3f90d5d905b2b2f30a67c04
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/234317
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
  • Loading branch information
bsalomon authored and Skia Commit-Bot committed Aug 13, 2019
1 parent 309c6c0 commit cd8b6d5
Showing 1 changed file with 79 additions and 24 deletions.
103 changes: 79 additions & 24 deletions tests/ProcessorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
* found in the LICENSE file.
*/

#include "include/core/SkTypes.h"
#include "tests/Test.h"

#include "include/gpu/GrContext.h"
Expand All @@ -22,7 +21,6 @@
#include "src/gpu/ops/GrFillRectOp.h"
#include "src/gpu/ops/GrMeshDrawOp.h"
#include "tests/TestUtils.h"

#include <atomic>
#include <random>

Expand Down Expand Up @@ -334,23 +332,31 @@ sk_sp<GrTextureProxy> make_input_texture(GrProxyProvider* proxyProvider, int wid
SkBackingFit::kExact);
}

bool log_surface_context(sk_sp<GrSurfaceContext> src, SkString* dst) {
SkImageInfo ii = SkImageInfo::Make(src->width(), src->height(), kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
// We tag logged data as unpremul to avoid conversion when encoding as PNG. The input texture
// actually contains unpremul data. Also, even though we made the result data by rendering into
// a "unpremul" GrRenderTargetContext, our input texture is unpremul and outside of the random
// effect configuration, we didn't do anything to ensure the output is actually premul. We just
// don't currently allow kUnpremul GrRenderTargetContexts.
static constexpr auto kLogAlphaType = kUnpremul_SkAlphaType;

bool log_pixels(GrColor* pixels, int widthHeight, SkString* dst) {
auto info = SkImageInfo::Make(widthHeight, widthHeight, kRGBA_8888_SkColorType, kLogAlphaType);
SkBitmap bmp;
bmp.installPixels(info, pixels, widthHeight * sizeof(GrColor));
return bitmap_to_base64_data_uri(bmp, dst);
}

bool log_texture_proxy(GrContext* context, sk_sp<GrTextureProxy> src, SkString* dst) {
sk_sp<GrSurfaceContext> sContext(
context->priv().makeWrappedSurfaceContext(src, GrColorType::kRGBA_8888, kLogAlphaType));
SkImageInfo ii =
SkImageInfo::Make(src->width(), src->height(), kRGBA_8888_SkColorType, kLogAlphaType);
SkBitmap bm;
SkAssertResult(bm.tryAllocPixels(ii));
SkAssertResult(src->readPixels(ii, bm.getPixels(), bm.rowBytes(), {0, 0}));

SkAssertResult(sContext->readPixels(ii, bm.getPixels(), bm.rowBytes(), {0, 0}));
return bitmap_to_base64_data_uri(bm, dst);
}

bool log_surface_proxy(GrContext* context, sk_sp<GrSurfaceProxy> src, SkString* dst) {
// All the inputs are made from kRGBA_8888_SkColorType/kPremul_SkAlphaType bitmaps.
sk_sp<GrSurfaceContext> sContext(context->priv().makeWrappedSurfaceContext(
src, GrColorType::kRGBA_8888, kPremul_SkAlphaType));
return log_surface_context(sContext, dst);
}

bool fuzzy_color_equals(const SkPMColor4f& c1, const SkPMColor4f& c2) {
// With the loss of precision of rendering into 32-bit color, then estimating the FP's output
// from that, it is not uncommon for a valid output to differ from estimate by up to 0.01
Expand Down Expand Up @@ -620,9 +626,9 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, repor
if (!loggedFirstFailure) {
// Print with ERRORF to make sure the encoded image is output
SkString input;
log_surface_proxy(context, inputTexture1, &input);
log_texture_proxy(context, inputTexture1, &input);
SkString output;
log_surface_context(rtc, &output);
log_pixels(readData1.get(), kRenderSize, &output);
ERRORF(reporter, "Input image: %s\n\n"
"===========================================================\n\n"
"Output image: %s\n", input.c_str(), output.c_str());
Expand All @@ -644,9 +650,9 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, repor
}
if (!loggedFirstWarning) {
SkString input;
log_surface_proxy(context, inputTexture1, &input);
log_texture_proxy(context, inputTexture1, &input);
SkString output;
log_surface_context(rtc, &output);
log_pixels(readData1.get(), kRenderSize, &output);
INFOF(reporter, "Input image: %s\n\n"
"===========================================================\n\n"
"Output image: %s\n", input.c_str(), output.c_str());
Expand Down Expand Up @@ -681,8 +687,19 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorCloneTest, reporter, ctxInfo) {
auto inputTexture = make_input_texture(proxyProvider, kRenderSize, kRenderSize, 0.0f);
std::unique_ptr<GrColor[]> readData1(new GrColor[kRenderSize * kRenderSize]);
std::unique_ptr<GrColor[]> readData2(new GrColor[kRenderSize * kRenderSize]);
auto readInfo = SkImageInfo::Make(kRenderSize, kRenderSize, kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
// On failure we write out images, but just write the first failing set as the print is very
// large.
bool loggedFirstFailure = false;

// This test has a history of being flaky on a number of devices. If an FP clone is logically
// wrong, it's reasonable to expect it produce a large number of pixel differences in the image
// Sporadic pixel violations are more indicative device errors and represents a separate
// problem.
#if defined(SK_BUILD_FOR_SKQP)
static constexpr int kMaxAcceptableFailedPixels = 0; // Strict when running as SKQP
#else
static constexpr int kMaxAcceptableFailedPixels = 2 * kRenderSize; // ~0.7% of the image
#endif

// Because processor factories configure themselves in random ways, this is not exhaustive.
for (int i = 0; i < GrFragmentProcessorTestFactory::Count(); ++i) {
Expand Down Expand Up @@ -712,16 +729,54 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorCloneTest, reporter, ctxInfo) {

// Check that the results are the same.
bool passing = true;
int failedPixelCount = 0;
int firstWrongX = 0;
int firstWrongY = 0;
for (int y = 0; y < kRenderSize && passing; ++y) {
for (int x = 0; x < kRenderSize && passing; ++x) {
int idx = y * kRenderSize + x;
if (readData1[idx] != readData2[idx]) {
if (!failedPixelCount) {
firstWrongX = x;
firstWrongY = y;
}
++failedPixelCount;
}
if (failedPixelCount > kMaxAcceptableFailedPixels) {
passing = false;
idx = firstWrongY * kRenderSize + firstWrongX;
ERRORF(reporter,
"Processor %s made clone produced different output. "
"Processor %s made clone produced different output at (%d, %d). "
"Input color: 0x%08x, Original Output Color: 0x%08x, "
"Clone Output Color: 0x%08x..",
name, input_texel_color(x, y, 0.0f), readData1[idx], readData2[idx]);
passing = false;
"Clone Output Color: 0x%08x.",
name, firstWrongX, firstWrongY, input_texel_color(x, y, 0.0f),
readData1[idx], readData2[idx]);
if (!loggedFirstFailure) {
// Write the images out as data urls for inspection.
// We mark the data as unpremul to avoid conversion when encoding as
// PNG. Also, even though we made the data by rendering into
// a "unpremul" GrRenderTargetContext, our input texture is unpremul and
// outside of the random effect configuration, we didn't do anything to
// ensure the output is actually premul.
auto info = SkImageInfo::Make(kRenderSize, kRenderSize,
kRGBA_8888_SkColorType,
kUnpremul_SkAlphaType);
SkString input, orig, clone;
if (log_texture_proxy(context, inputTexture, &input) &&
log_pixels(readData1.get(), kRenderSize, &orig) &&
log_pixels(readData2.get(), kRenderSize, &clone)) {
ERRORF(reporter,
"\nInput image:\n%s\n\n"
"==========================================================="
"\n\n"
"Orig output image:\n%s\n"
"==========================================================="
"\n\n"
"Clone output image:\n%s\n",
input.c_str(), orig.c_str(), clone.c_str());
loggedFirstFailure = true;
}
}
}
}
}
Expand Down

0 comments on commit cd8b6d5

Please sign in to comment.