Make ProcessorOptimizationValidationTest more forgiving

Bug: skia:
Change-Id: I3ff965644a0a59af1e2a2b1ea226a1417a234ced
Reviewed-on: https://skia-review.googlesource.com/c/158342
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Michael Ludwig 2018-10-03 16:04:38 -04:00 committed by Skia Commit-Bot
parent 6989ba488c
commit 314d3772a4

View File

@ -359,6 +359,7 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, repor
// Encoded images are very verbose and this tests many potential images, so only export the
// first failure (subsequent failures have a reasonable chance of being related).
bool loggedFirstFailure = false;
bool loggedFirstWarning = false;
// Because processor factories configure themselves in random ways, this is not exhaustive.
for (int i = 0; i < FPFactory::Count(); ++i) {
int timesToInvokeFactory = 5;
@ -389,7 +390,6 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, repor
rtc->readPixels(SkImageInfo::Make(kRenderSize, kRenderSize, kRGBA_8888_SkColorType,
kPremul_SkAlphaType),
readData.get(), 0, 0, 0);
bool passing = true;
if (0) { // Useful to see what FPs are being tested.
SkString children;
for (int c = 0; c < clone->numChildProcessors(); ++c) {
@ -401,8 +401,26 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, repor
}
SkDebugf("%s %s\n", clone->name(), children.c_str());
}
for (int y = 0; y < kRenderSize && passing; ++y) {
for (int x = 0; x < kRenderSize && passing; ++x) {
// This test has a history of being flaky on a number of devices. If an FP is logically
// violating the optimizations, it's reasonable to expect it to violate requirements on
// a large number of pixels in the image. Sporadic pixel violations are more indicative
// of device errors and represents a separate problem.
#if defined(SK_SKQP_GLOBAL_ERROR_TOLERANCE)
static constexpr int kMaxAcceptableFailedPixels = 0; // Strict when running as SKQP
#else
static constexpr int kMaxAcceptableFailedPixels = 2 * kRenderSize; // ~0.7% of the image
#endif
int failedPixelCount = 0;
// Collect first optimization failure message, to be output later as a warning or an
// error depending on whether the rendering "passed" or failed.
SkString coverageMessage;
SkString opaqueMessage;
SkString constMessage;
for (int y = 0; y < kRenderSize; ++y) {
for (int x = 0; x < kRenderSize; ++x) {
bool passing = true;
GrColor input = input_texel_color(x, y);
GrColor output = readData.get()[y * kRenderSize + x];
if (clone->compatibleWithCoverageAsAlpha()) {
@ -419,11 +437,13 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, repor
GrColorUnpackG(output) <= GrColorUnpackA(input) &&
GrColorUnpackB(output) <= GrColorUnpackA(input);
if (!legalColorModulation && !legalAlphaModulation) {
ERRORF(reporter,
"\"Modulating\" processor %s made color/alpha value larger. "
"Input: 0x%08x, Output: 0x%08x, pixel (%d, %d).",
clone->name(), input, output, x, y);
passing = false;
if (coverageMessage.isEmpty()) {
coverageMessage.printf("\"Modulating\" processor %s made color/"
"alpha value larger. Input: 0x%08x, Output: 0x%08x, pixel "
"(%d, %d).", clone->name(), input, output, x, y);
}
}
}
SkPMColor4f input4f = input_texel_color4f(x, y);
@ -436,46 +456,93 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, repor
float aDiff = fabsf(output4f.fRGBA[3] - expected4f.fA);
static constexpr float kTol = 4 / 255.f;
if (rDiff > kTol || gDiff > kTol || bDiff > kTol || aDiff > kTol) {
ERRORF(reporter,
"Processor %s claimed output for const input doesn't match "
"actual output. Error: %f, Tolerance: %f, input: (%f, %f, %f, "
"%f), actual: (%f, %f, %f, %f), expected(%f, %f, %f, %f)",
clone->name(),
SkTMax(rDiff, SkTMax(gDiff, SkTMax(bDiff, aDiff))), kTol,
input4f.fR, input4f.fG, input4f.fB, input4f.fA,
output4f.fRGBA[0], output4f.fRGBA[1], output4f.fRGBA[2],
output4f.fRGBA[3],
expected4f.fR, expected4f.fG, expected4f.fB, expected4f.fA);
passing = false;
if (constMessage.isEmpty()) {
passing = false;
constMessage.printf("Processor %s claimed output for const input "
"doesn't match actual output. Error: %f, Tolerance: %f, "
"input: (%f, %f, %f, %f), actual: (%f, %f, %f, %f), "
"expected(%f, %f, %f, %f)", clone->name(),
SkTMax(rDiff, SkTMax(gDiff, SkTMax(bDiff, aDiff))), kTol,
input4f.fR, input4f.fG, input4f.fB, input4f.fA,
output4f.fRGBA[0], output4f.fRGBA[1], output4f.fRGBA[2],
output4f.fRGBA[3], expected4f.fR, expected4f.fG,
expected4f.fB, expected4f.fA);
}
}
}
if (GrColorIsOpaque(input) && clone->preservesOpaqueInput() &&
!GrColorIsOpaque(output)) {
ERRORF(reporter,
"Processor %s claimed opaqueness is preserved but it is not. Input: "
"0x%08x, Output: 0x%08x.",
clone->name(), input, output);
passing = false;
}
if (!passing) {
if (loggedFirstFailure) {
// Do not export images
ERRORF(reporter, "Seed: 0x%08x, Processor details: %s", seed,
clone->dumpInfo().c_str());
} else {
SkString input;
log_surface_proxy(context, inputTexture, &input);
SkString output;
log_surface_context(rtc, &output);
ERRORF(reporter, "Seed: 0x%08x, Processor details: %s\n\n"
"===========================================================\n\n"
"Input image: %s\n\n"
"===========================================================\n\n"
"Output image: %s\n", seed, clone->dumpInfo().c_str(),
input.c_str(), output.c_str());
loggedFirstFailure = true;
if (opaqueMessage.isEmpty()) {
opaqueMessage.printf("Processor %s claimed opaqueness is preserved but "
"it is not. Input: 0x%08x, Output: 0x%08x.",
clone->name(), input, output);
}
}
if (!passing) {
// Regardless of how many optimizations the pixel violates, count it as a
// single bad pixel.
failedPixelCount++;
}
}
}
// Finished analyzing the entire image, see if the number of pixel failures meets the
// threshold for an FP violating the optimization requirements.
if (failedPixelCount > kMaxAcceptableFailedPixels) {
ERRORF(reporter, "Processor violated %d of %d pixels, seed: 0x%08x, processor: %s",
", first failing pixel details are below:",
failedPixelCount, kRenderSize * kRenderSize, seed,
clone->dumpInfo().c_str());
// Print first failing pixel's details.
if (!coverageMessage.isEmpty()) {
ERRORF(reporter, coverageMessage.c_str());
}
if (!constMessage.isEmpty()) {
ERRORF(reporter, constMessage.c_str());
}
if (!opaqueMessage.isEmpty()) {
ERRORF(reporter, opaqueMessage.c_str());
}
if (!loggedFirstFailure) {
// Print with ERRORF to make sure the encoded image is output
SkString input;
log_surface_proxy(context, inputTexture, &input);
SkString output;
log_surface_context(rtc, &output);
ERRORF(reporter, "Input image: %s\n\n"
"===========================================================\n\n"
"Output image: %s\n", input.c_str(), output.c_str());
loggedFirstFailure = true;
}
} else {
// Don't trigger an error, but don't just hide the failures either.
INFOF(reporter, "Processor violated %d of %d pixels (below error threshold), seed: "
"0x%08x, processor: %s", failedPixelCount, kRenderSize * kRenderSize,
seed, clone->dumpInfo().c_str());
if (!coverageMessage.isEmpty()) {
INFOF(reporter, coverageMessage.c_str());
}
if (!constMessage.isEmpty()) {
INFOF(reporter, constMessage.c_str());
}
if (!opaqueMessage.isEmpty()) {
INFOF(reporter, opaqueMessage.c_str());
}
if (!loggedFirstWarning) {
SkString input;
log_surface_proxy(context, inputTexture, &input);
SkString output;
log_surface_context(rtc, &output);
INFOF(reporter, "Input image: %s\n\n"
"===========================================================\n\n"
"Output image: %s\n", input.c_str(), output.c_str());
loggedFirstWarning = true;
}
}
}