diff --git a/gm/gm_error.h b/gm/gm_error.h new file mode 100644 index 0000000000..e0dbf1c4ad --- /dev/null +++ b/gm/gm_error.h @@ -0,0 +1,89 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* + * Error codes used by gmmain.cpp. + */ + +namespace skiagm { + + /** + * The complete list of error types we might encounter in GM. + */ + enum ErrorType { +#if SK_SUPPORT_GPU + kNoGpuContext_ErrorType, +#endif + kImageMismatch_ErrorType, + kMissingExpectations_ErrorType, + kWritingReferenceImage_ErrorType, + kLast_ErrorType = kWritingReferenceImage_ErrorType + }; + + /** + * A combination of 0 or more ErrorTypes. + */ + class ErrorCombination { + public: + ErrorCombination() : fBitfield(0) {} + ErrorCombination(const ErrorType type) : fBitfield(1 << type) {} + + /** + * Returns true iff there are NO errors. + */ + bool isEmpty() const { + return (0 == this->fBitfield); + } + + /** + * Adds this ErrorType to this ErrorCombination. + */ + void add(const ErrorType type) { + this->fBitfield |= (1 << type); + } + + /** + * Adds all ErrorTypes in "other" to this ErrorCombination. + */ + void add(const ErrorCombination other) { + this->fBitfield |= other.fBitfield; + } + + /** + * Returns true iff this ErrorCombination includes this ErrorType. + */ + bool includes(const ErrorType type) const { + return !(0 == (this->fBitfield & (1 << type))); + } + + /** + * Returns a new ErrorCombination, which includes the union of all + * ErrorTypes in two ErrorCombination objects (this and other). + */ + ErrorCombination plus(const ErrorCombination& other) const { + ErrorCombination retval; + retval.fBitfield = this->fBitfield | other.fBitfield; + return retval; + } + + /** + * Returns a new ErrorCombination, which is a copy of "this" + * but with all ErrorTypes in "other" removed. + */ + ErrorCombination minus(const ErrorCombination& other) const { + ErrorCombination retval; + retval.fBitfield = this->fBitfield & ~(other.fBitfield); + return retval; + } + + private: + int fBitfield; + }; + + // No errors at all. + const static ErrorCombination kEmpty_ErrorCombination; +} diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp index 42c7889976..55e1c2a3c3 100644 --- a/gm/gmmain.cpp +++ b/gm/gmmain.cpp @@ -14,6 +14,7 @@ */ #include "gm.h" +#include "gm_error.h" #include "gm_expectations.h" #include "system_preferences.h" #include "SkBitmap.h" @@ -52,6 +53,9 @@ #include "GrRenderTarget.h" #include "SkGpuDevice.h" typedef GrContextFactory::GLContextType GLContextType; +#define DEFAULT_CACHE_VALUE -1 +static int gGpuCacheSizeBytes; +static int gGpuCacheSizeCount; #else class GrContextFactory; class GrContext; @@ -80,17 +84,6 @@ extern bool gSkSuppressFontCachePurgeSpew; #define CAN_IMAGE_PDF 0 #endif -typedef int ErrorBitfield; -// an empty bitfield means no errors: -const static ErrorBitfield kEmptyErrorBitfield = 0x00; -// individual error types: -const static ErrorBitfield kNoGpuContext_ErrorBitmask = 0x01; -const static ErrorBitfield kImageMismatch_ErrorBitmask = 0x02; -const static ErrorBitfield kMissingExpectations_ErrorBitmask = 0x04; -const static ErrorBitfield kWritingReferenceImage_ErrorBitmask = 0x08; -// we typically ignore any errors matching this bitmask: -const static ErrorBitfield kIgnorable_ErrorBitmask = kMissingExpectations_ErrorBitmask; - using namespace skiagm; struct FailRec { @@ -195,6 +188,7 @@ public: // Set default values of member variables, which tool_main() // may override. fUseFileHierarchy = false; + fIgnorableErrorCombination.add(kMissingExpectations_ErrorType); fMismatchPath = NULL; } @@ -248,24 +242,24 @@ public: SkImageEncoder::kPNG_Type, 100); } - // Records an error in fFailedTests, if we want to record errors - // of this type. - void RecordError(ErrorBitfield errorType, const SkString& name, + /** + * Records the errors encountered in fFailedTests, except for any error + * types we want to ignore. + */ + void RecordError(const ErrorCombination& errorCombination, const SkString& name, const char renderModeDescriptor []) { // The common case: no error means nothing to record. - if (kEmptyErrorBitfield == errorType) { + if (errorCombination.isEmpty()) { return; } // If only certain error type(s) were reported, we know we can ignore them. - if (errorType == (errorType & kIgnorable_ErrorBitmask)) { + if (errorCombination.minus(fIgnorableErrorCombination).isEmpty()) { return; } - FailRec& rec = fFailedTests.push_back(make_name( - name.c_str(), renderModeDescriptor)); - rec.fIsPixelError = - (kEmptyErrorBitfield != (errorType & kImageMismatch_ErrorBitmask)); + FailRec& rec = fFailedTests.push_back(make_name(name.c_str(), renderModeDescriptor)); + rec.fIsPixelError = errorCombination.includes(kImageMismatch_ErrorType); } // List contents of fFailedTests via SkDebug. @@ -378,11 +372,11 @@ public: canvas->setDrawFilter(NULL); } - static ErrorBitfield generate_image(GM* gm, const ConfigData& gRec, - GrContext* context, - GrRenderTarget* rt, - SkBitmap* bitmap, - bool deferred) { + static ErrorCombination generate_image(GM* gm, const ConfigData& gRec, + GrContext* context, + GrRenderTarget* rt, + SkBitmap* bitmap, + bool deferred) { SkISize size (gm->getISize()); setup_bitmap(gRec, size, bitmap); @@ -401,7 +395,7 @@ public: #if SK_SUPPORT_GPU else { // GPU if (NULL == context) { - return kNoGpuContext_ErrorBitmask; + return ErrorCombination(kNoGpuContext_ErrorType); } SkAutoTUnref device(new SkGpuDevice(context, rt)); if (deferred) { @@ -419,7 +413,7 @@ public: } #endif complete_bitmap(bitmap); - return kEmptyErrorBitfield; + return kEmpty_ErrorCombination; } static void generate_image_from_picture(GM* gm, const ConfigData& gRec, @@ -488,10 +482,9 @@ public: #endif } - ErrorBitfield write_reference_image( - const ConfigData& gRec, const char writePath [], - const char renderModeDescriptor [], const SkString& name, - SkBitmap& bitmap, SkDynamicMemoryWStream* document) { + ErrorCombination write_reference_image(const ConfigData& gRec, const char writePath [], + const char renderModeDescriptor [], const SkString& name, + SkBitmap& bitmap, SkDynamicMemoryWStream* document) { SkString path; bool success = false; if (gRec.fBackend == kRaster_Backend || @@ -513,12 +506,12 @@ public: success = write_document(path, *document); } if (success) { - return kEmptyErrorBitfield; + return kEmpty_ErrorCombination; } else { gm_fprintf(stderr, "FAILED to write %s\n", path.c_str()); - RecordError(kWritingReferenceImage_ErrorBitmask, name, - renderModeDescriptor); - return kWritingReferenceImage_ErrorBitmask; + ErrorCombination errors(kWritingReferenceImage_ErrorType); + RecordError(errors, name, renderModeDescriptor); + return errors; } } @@ -579,9 +572,8 @@ public: } /** - * Compares actual checksum to expectations. Returns - * kEmptyErrorBitfield if they match, or some combination of - * _ErrorBitmask values otherwise. + * Compares actual checksum to expectations, returning the set of errors + * (if any) that we saw along the way. * * If fMismatchPath has been set, and there are pixel diffs, then the * actual bitmap will be written out to a file within fMismatchPath. @@ -598,23 +590,21 @@ public: * in-memory comparisons (Rtree vs regular, etc.) are not written to the * JSON summary. We may wish to change that. */ - ErrorBitfield compare_to_expectations(Expectations expectations, - const SkBitmap& actualBitmap, - const SkString& baseNameString, - const char renderModeDescriptor[], - bool addToJsonSummary=false) { - ErrorBitfield retval; + ErrorCombination compare_to_expectations(Expectations expectations, + const SkBitmap& actualBitmap, + const SkString& baseNameString, + const char renderModeDescriptor[], + bool addToJsonSummary=false) { + ErrorCombination errors; Checksum actualChecksum = SkBitmapChecksummer::Compute64(actualBitmap); SkString completeNameString = baseNameString; completeNameString.append(renderModeDescriptor); const char* completeName = completeNameString.c_str(); if (expectations.empty()) { - retval = kMissingExpectations_ErrorBitmask; - } else if (expectations.match(actualChecksum)) { - retval = kEmptyErrorBitfield; - } else { - retval = kImageMismatch_ErrorBitmask; + errors.add(kMissingExpectations_ErrorType); + } else if (!expectations.match(actualChecksum)) { + errors.add(kImageMismatch_ErrorType); // Write out the "actuals" for any mismatches, if we have // been directed to do so. @@ -632,16 +622,15 @@ public: report_bitmap_diffs(*expectedBitmapPtr, actualBitmap, completeName); } } - RecordError(retval, baseNameString, renderModeDescriptor); + RecordError(errors, baseNameString, renderModeDescriptor); if (addToJsonSummary) { - add_actual_results_to_json_summary(completeName, actualChecksum, - retval, + add_actual_results_to_json_summary(completeName, actualChecksum, errors, expectations.ignoreFailure()); add_expected_results_to_json_summary(completeName, expectations); } - return retval; + return errors; } /** @@ -650,12 +639,12 @@ public: */ void add_actual_results_to_json_summary(const char testName[], Checksum actualChecksum, - ErrorBitfield result, + ErrorCombination result, bool ignoreFailure) { Json::Value actualResults; actualResults[kJsonKey_ActualResults_AnyStatus_Checksum] = asJsonValue(actualChecksum); - if (kEmptyErrorBitfield == result) { + if (result.isEmpty()) { this->fJsonActualResults_Succeeded[testName] = actualResults; } else { if (ignoreFailure) { @@ -663,12 +652,12 @@ public: // actual results against expectations in a JSON file // (where we can set ignore-failure to either true or // false), add test cases that exercise ignored - // failures (both for kMissingExpectations_ErrorBitmask - // and kImageMismatch_ErrorBitmask). + // failures (both for kMissingExpectations_ErrorType + // and kImageMismatch_ErrorType). this->fJsonActualResults_FailureIgnored[testName] = actualResults; } else { - if (kEmptyErrorBitfield != (result & kMissingExpectations_ErrorBitmask)) { + if (result.includes(kMissingExpectations_ErrorType)) { // TODO: What about the case where there IS an // expected image checksum, but that gm test // doesn't actually run? For now, those cases @@ -683,7 +672,7 @@ public: this->fJsonActualResults_NoComparison[testName] = actualResults; } - if (kEmptyErrorBitfield != (result & kImageMismatch_ErrorBitmask)) { + if (result.includes(kImageMismatch_ErrorType)) { this->fJsonActualResults_Failed[testName] = actualResults; } } @@ -716,15 +705,14 @@ public: * @param actualBitmap bitmap generated by this run * @param pdf */ - ErrorBitfield compare_test_results_to_stored_expectations( + ErrorCombination compare_test_results_to_stored_expectations( GM* gm, const ConfigData& gRec, const char writePath[], SkBitmap& actualBitmap, SkDynamicMemoryWStream* pdf) { SkString name = make_name(gm->shortName(), gRec.fName); - ErrorBitfield retval = kEmptyErrorBitfield; + ErrorCombination errors; - ExpectationsSource *expectationsSource = - this->fExpectationsSource.get(); + ExpectationsSource *expectationsSource = this->fExpectationsSource.get(); if (expectationsSource && (gRec.fFlags & kRead_ConfigFlag)) { /* * Get the expected results for this test, as one or more allowed @@ -740,15 +728,15 @@ public: * See comments above complete_bitmap() for more detail. */ Expectations expectations = expectationsSource->get(name.c_str()); - retval |= compare_to_expectations(expectations, actualBitmap, - name, "", true); + errors.add(compare_to_expectations(expectations, actualBitmap, + name, "", true)); } else { // If we are running without expectations, we still want to // record the actual results. Checksum actualChecksum = SkBitmapChecksummer::Compute64(actualBitmap); add_actual_results_to_json_summary(name.c_str(), actualChecksum, - kMissingExpectations_ErrorBitmask, + ErrorCombination(kMissingExpectations_ErrorType), false); } @@ -757,11 +745,11 @@ public: // we don't want to write out the actual bitmaps for all // renderModes of all tests! That would be a lot of files. if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) { - retval |= write_reference_image(gRec, writePath, "", - name, actualBitmap, pdf); + errors.add(write_reference_image(gRec, writePath, "", + name, actualBitmap, pdf)); } - return retval; + return errors; } /** @@ -773,7 +761,7 @@ public: * @param actualBitmap actual bitmap generated by this run * @param referenceBitmap bitmap we expected to be generated */ - ErrorBitfield compare_test_results_to_reference_bitmap( + ErrorCombination compare_test_results_to_reference_bitmap( GM* gm, const ConfigData& gRec, const char renderModeDescriptor [], SkBitmap& actualBitmap, const SkBitmap* referenceBitmap) { @@ -835,20 +823,19 @@ public: // Test: draw into a bitmap or pdf. // Depending on flags, possibly compare to an expected image. - ErrorBitfield test_drawing(GM* gm, - const ConfigData& gRec, - const char writePath [], - GrContext* context, - GrRenderTarget* rt, - SkBitmap* bitmap) { + ErrorCombination test_drawing(GM* gm, + const ConfigData& gRec, + const char writePath [], + GrContext* context, + GrRenderTarget* rt, + SkBitmap* bitmap) { SkDynamicMemoryWStream document; if (gRec.fBackend == kRaster_Backend || gRec.fBackend == kGPU_Backend) { // Early exit if we can't generate the image. - ErrorBitfield errors = generate_image(gm, gRec, context, rt, bitmap, - false); - if (kEmptyErrorBitfield != errors) { + ErrorCombination errors = generate_image(gm, gRec, context, rt, bitmap, false); + if (!errors.isEmpty()) { // TODO: Add a test to exercise what the stdout and // JSON look like if we get an "early error" while // trying to generate the image. @@ -868,11 +855,11 @@ public: gm, gRec, writePath, *bitmap, &document); } - ErrorBitfield test_deferred_drawing(GM* gm, - const ConfigData& gRec, - const SkBitmap& referenceBitmap, - GrContext* context, - GrRenderTarget* rt) { + ErrorCombination test_deferred_drawing(GM* gm, + const ConfigData& gRec, + const SkBitmap& referenceBitmap, + GrContext* context, + GrRenderTarget* rt) { SkDynamicMemoryWStream document; if (gRec.fBackend == kRaster_Backend || @@ -880,19 +867,31 @@ public: SkBitmap bitmap; // Early exit if we can't generate the image, but this is // expected in some cases, so don't report a test failure. - if (!generate_image(gm, gRec, context, rt, &bitmap, true)) { - return kEmptyErrorBitfield; + ErrorCombination errors = generate_image(gm, gRec, context, rt, &bitmap, true); + // TODO(epoger): This logic is the opposite of what is + // described above... if we succeeded in generating the + // -deferred image, we exit early! We should fix this + // ASAP, because it is hiding -deferred errors... but for + // now, I'm leaving the logic as it is so that the + // refactoring change + // https://codereview.chromium.org/12992003/ is unblocked. + // + // Filed as https://code.google.com/p/skia/issues/detail?id=1180 + // ('image-surface gm test is failing in "deferred" mode, + // and gm is not reporting the failure') + if (errors.isEmpty()) { + return kEmpty_ErrorCombination; } return compare_test_results_to_reference_bitmap( gm, gRec, "-deferred", bitmap, &referenceBitmap); } - return kEmptyErrorBitfield; + return kEmpty_ErrorCombination; } - ErrorBitfield test_pipe_playback(GM* gm, - const ConfigData& gRec, - const SkBitmap& referenceBitmap) { - ErrorBitfield errors = kEmptyErrorBitfield; + ErrorCombination test_pipe_playback(GM* gm, + const ConfigData& gRec, + const SkBitmap& referenceBitmap) { + ErrorCombination errors; for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) { SkBitmap bitmap; SkISize size = gm->getISize(); @@ -908,18 +907,18 @@ public: writer.endRecording(); SkString string("-pipe"); string.append(gPipeWritingFlagCombos[i].name); - errors |= compare_test_results_to_reference_bitmap( - gm, gRec, string.c_str(), bitmap, &referenceBitmap); - if (errors != kEmptyErrorBitfield) { + errors.add(compare_test_results_to_reference_bitmap( + gm, gRec, string.c_str(), bitmap, &referenceBitmap)); + if (!errors.isEmpty()) { break; } } return errors; } - ErrorBitfield test_tiled_pipe_playback( - GM* gm, const ConfigData& gRec, const SkBitmap& referenceBitmap) { - ErrorBitfield errors = kEmptyErrorBitfield; + ErrorCombination test_tiled_pipe_playback(GM* gm, const ConfigData& gRec, + const SkBitmap& referenceBitmap) { + ErrorCombination errors; for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) { SkBitmap bitmap; SkISize size = gm->getISize(); @@ -935,9 +934,9 @@ public: writer.endRecording(); SkString string("-tiled pipe"); string.append(gPipeWritingFlagCombos[i].name); - errors |= compare_test_results_to_reference_bitmap( - gm, gRec, string.c_str(), bitmap, &referenceBitmap); - if (errors != kEmptyErrorBitfield) { + errors.add(compare_test_results_to_reference_bitmap( + gm, gRec, string.c_str(), bitmap, &referenceBitmap)); + if (!errors.isEmpty()) { break; } } @@ -950,6 +949,7 @@ public: // bool fUseFileHierarchy; + ErrorCombination fIgnorableErrorCombination; const char* fMismatchPath; @@ -1033,6 +1033,12 @@ static SkString configUsage() { return result; } +// Macro magic to convert a numeric preprocessor token into a string. +// Adapted from http://stackoverflow.com/questions/240353/convert-a-preprocessor-token-to-a-string +// This should probably be moved into one of our common headers... +#define TOSTRING_INTERNAL(x) #x +#define TOSTRING(x) TOSTRING_INTERNAL(x) + // Alphabetized ignoring "no" prefix ("readPath", "noreplay", "resourcePath"). DEFINE_string(config, "", configUsage().c_str()); DEFINE_bool(deferred, true, "Exercise the deferred rendering test pass."); @@ -1042,8 +1048,8 @@ DEFINE_string(excludeConfig, "", "Space delimited list of configs to skip."); DEFINE_bool(forceBWtext, false, "Disable text anti-aliasing."); #if SK_SUPPORT_GPU DEFINE_string(gpuCacheSize, "", " : Limit the gpu cache to byte size or " - "object count. -1 for either value means use the default. 0 for either " - "disables the cache."); + "object count. " TOSTRING(DEFAULT_CACHE_VALUE) " for either value means " + "use the default. 0 for either disables the cache."); #endif DEFINE_bool(hierarchy, false, "Whether to use multilevel directory structure " "when reading/writing files."); @@ -1149,29 +1155,13 @@ template void appendUnique(SkTDArray* array, const T& value) { * * Returns all errors encountered while doing so. */ -ErrorBitfield run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArray &configs, - GrContextFactory *grFactory); -ErrorBitfield run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArray &configs, - GrContextFactory *grFactory) { - ErrorBitfield errorsForAllConfigs = kEmptyErrorBitfield; +ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArray &configs, + GrContextFactory *grFactory); +ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArray &configs, + GrContextFactory *grFactory) { + ErrorCombination errorsForAllConfigs; uint32_t gmFlags = gm->getFlags(); -#if SK_SUPPORT_GPU - struct { - int fBytes; - int fCount; - } gpuCacheSize = { -1, -1 }; // -1s mean use the default - - if (FLAGS_gpuCacheSize.count() > 0) { - if (FLAGS_gpuCacheSize.count() != 2) { - gm_fprintf(stderr, "--gpuCacheSize requires two arguments\n"); - return -1; - } - gpuCacheSize.fBytes = atoi(FLAGS_gpuCacheSize[0]); - gpuCacheSize.fCount = atoi(FLAGS_gpuCacheSize[1]); - } -#endif - for (int i = 0; i < configs.count(); i++) { ConfigData config = gRec[configs[i]]; @@ -1192,12 +1182,12 @@ ErrorBitfield run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArray rt; AutoResetGr autogr; - if ((kEmptyErrorBitfield == errorsForThisConfig) && (kGPU_Backend == config.fBackend)) { + if ((errorsForThisConfig.isEmpty()) && (kGPU_Backend == config.fBackend)) { GrContext* gr = grFactory->get(config.fGLContextType); bool grSuccess = false; if (gr) { @@ -1221,16 +1211,16 @@ ErrorBitfield run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArraygetTextureCacheLimits(&count, &bytes); - if (-1 != gpuCacheSize.fBytes) { - bytes = static_cast(gpuCacheSize.fBytes); + if (DEFAULT_CACHE_VALUE != gGpuCacheSizeBytes) { + bytes = static_cast(gGpuCacheSizeBytes); } - if (-1 != gpuCacheSize.fCount) { - count = gpuCacheSize.fCount; + if (DEFAULT_CACHE_VALUE != gGpuCacheSizeCount) { + count = gGpuCacheSizeCount; } gr->setTextureCacheLimits(count, bytes); } if (!grSuccess) { - errorsForThisConfig |= kNoGpuContext_ErrorBitmask; + errorsForThisConfig.add(kNoGpuContext_ErrorType); } } #endif @@ -1243,19 +1233,18 @@ ErrorBitfield run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArray tileGridReplayScales; - *tileGridReplayScales.append() = SK_Scalar1; // By default only test at scale 1.0 - if (FLAGS_tileGridReplayScales.count() > 0) { - tileGridReplayScales.reset(); - for (int i = 0; i < FLAGS_tileGridReplayScales.count(); i++) { - double val = atof(FLAGS_tileGridReplayScales[i]); - if (0 < val) { - *tileGridReplayScales.append() = SkDoubleToScalar(val); - } - } - if (0 == tileGridReplayScales.count()) { - // Should have at least one scale - gm_fprintf(stderr, "--tileGridReplayScales requires at least one scale.\n"); - return -1; - } - } - - ErrorBitfield errorsForAllModes = kEmptyErrorBitfield; +ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compareConfig, + const SkBitmap &comparisonBitmap, + const SkTDArray &tileGridReplayScales); +ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compareConfig, + const SkBitmap &comparisonBitmap, + const SkTDArray &tileGridReplayScales) { + ErrorCombination errorsForAllModes; uint32_t gmFlags = gm->getFlags(); // run the picture centric GM steps if (!(gmFlags & GM::kSkipPicture_Flag)) { - ErrorBitfield pictErrors = kEmptyErrorBitfield; + ErrorCombination pictErrors; //SkAutoTUnref pict(generate_new_picture(gm)); SkPicture* pict = gmmain.generate_new_picture(gm, kNone_BbhType, 0); @@ -1303,18 +1277,18 @@ ErrorBitfield run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compa if (FLAGS_replay) { SkBitmap bitmap; gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap); - pictErrors |= gmmain.compare_test_results_to_reference_bitmap( - gm, compareConfig, "-replay", bitmap, &comparisonBitmap); + pictErrors.add(gmmain.compare_test_results_to_reference_bitmap( + gm, compareConfig, "-replay", bitmap, &comparisonBitmap)); } - if ((kEmptyErrorBitfield == pictErrors) && FLAGS_serialize) { + if ((pictErrors.isEmpty()) && FLAGS_serialize) { SkPicture* repict = gmmain.stream_to_new_picture(*pict); SkAutoUnref aurr(repict); SkBitmap bitmap; gmmain.generate_image_from_picture(gm, compareConfig, repict, &bitmap); - pictErrors |= gmmain.compare_test_results_to_reference_bitmap( - gm, compareConfig, "-serialize", bitmap, &comparisonBitmap); + pictErrors.add(gmmain.compare_test_results_to_reference_bitmap( + gm, compareConfig, "-serialize", bitmap, &comparisonBitmap)); } if (FLAGS_writePicturePath.count() == 1) { @@ -1325,7 +1299,7 @@ ErrorBitfield run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compa pict->serialize(&stream); } - errorsForAllModes |= pictErrors; + errorsForAllModes.add(pictErrors); } // TODO: add a test in which the RTree rendering results in a @@ -1338,8 +1312,8 @@ ErrorBitfield run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compa SkAutoUnref aur(pict); SkBitmap bitmap; gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap); - errorsForAllModes |= gmmain.compare_test_results_to_reference_bitmap( - gm, compareConfig, "-rtree", bitmap, &comparisonBitmap); + errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap( + gm, compareConfig, "-rtree", bitmap, &comparisonBitmap)); } if (!(gmFlags & GM::kSkipPicture_Flag) && FLAGS_tileGrid) { @@ -1361,26 +1335,26 @@ ErrorBitfield run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compa suffix += "-scale-"; suffix.appendScalar(replayScale); } - errorsForAllModes |= gmmain.compare_test_results_to_reference_bitmap( - gm, compareConfig, suffix.c_str(), bitmap, &comparisonBitmap); + errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap( + gm, compareConfig, suffix.c_str(), bitmap, &comparisonBitmap)); } } // run the pipe centric GM steps if (!(gmFlags & GM::kSkipPipe_Flag)) { - ErrorBitfield pipeErrors = kEmptyErrorBitfield; + ErrorCombination pipeErrors; if (FLAGS_pipe) { - pipeErrors |= gmmain.test_pipe_playback(gm, compareConfig, comparisonBitmap); + pipeErrors.add(gmmain.test_pipe_playback(gm, compareConfig, comparisonBitmap)); } - if ((kEmptyErrorBitfield == pipeErrors) && + if ((pipeErrors.isEmpty()) && FLAGS_tiledPipe && !(gmFlags & GM::kSkipTiled_Flag)) { - pipeErrors |= gmmain.test_tiled_pipe_playback(gm, compareConfig, comparisonBitmap); + pipeErrors.add(gmmain.test_tiled_pipe_playback(gm, compareConfig, comparisonBitmap)); } - errorsForAllModes |= pipeErrors; + errorsForAllModes.add(pipeErrors); } return errorsForAllModes; } @@ -1446,6 +1420,37 @@ int tool_main(int argc, char** argv) { } } +#if SK_SUPPORT_GPU + if (FLAGS_gpuCacheSize.count() > 0) { + if (FLAGS_gpuCacheSize.count() != 2) { + gm_fprintf(stderr, "--gpuCacheSize requires two arguments\n"); + return -1; + } + gGpuCacheSizeBytes = atoi(FLAGS_gpuCacheSize[0]); + gGpuCacheSizeCount = atoi(FLAGS_gpuCacheSize[1]); + } else { + gGpuCacheSizeBytes = DEFAULT_CACHE_VALUE; + gGpuCacheSizeCount = DEFAULT_CACHE_VALUE; + } +#endif + + SkTDArray tileGridReplayScales; + *tileGridReplayScales.append() = SK_Scalar1; // By default only test at scale 1.0 + if (FLAGS_tileGridReplayScales.count() > 0) { + tileGridReplayScales.reset(); + for (int i = 0; i < FLAGS_tileGridReplayScales.count(); i++) { + double val = atof(FLAGS_tileGridReplayScales[i]); + if (0 < val) { + *tileGridReplayScales.append() = SkDoubleToScalar(val); + } + } + if (0 == tileGridReplayScales.count()) { + // Should have at least one scale + gm_fprintf(stderr, "--tileGridReplayScales requires at least one scale.\n"); + return -1; + } + } + if (!userConfig) { // if no config is specified by user, add the defaults for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) { @@ -1584,17 +1589,19 @@ int tool_main(int argc, char** argv) { gm_fprintf(stdout, "%sdrawing... %s [%d %d]\n", moduloStr.c_str(), shortName, size.width(), size.height()); - ErrorBitfield testErrors = kEmptyErrorBitfield; - testErrors |= run_multiple_configs(gmmain, gm, configs, grFactory); + ErrorCombination testErrors; + testErrors.add(run_multiple_configs(gmmain, gm, configs, grFactory)); SkBitmap comparisonBitmap; const ConfigData compareConfig = { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "comparison", false }; - testErrors |= gmmain.generate_image(gm, compareConfig, NULL, NULL, &comparisonBitmap, false); + testErrors.add(gmmain.generate_image( + gm, compareConfig, NULL, NULL, &comparisonBitmap, false)); // TODO(epoger): only run this if gmmain.generate_image() succeeded? // Otherwise, what are we comparing against? - testErrors |= run_multiple_modes(gmmain, gm, compareConfig, comparisonBitmap); + testErrors.add(run_multiple_modes(gmmain, gm, compareConfig, comparisonBitmap, + tileGridReplayScales)); // Update overall results. // We only tabulate the particular error types that we currently @@ -1602,10 +1609,10 @@ int tool_main(int argc, char** argv) { // want to also tabulate other error types, we can do so. testsRun++; if (!gmmain.fExpectationsSource.get() || - (kEmptyErrorBitfield != (kMissingExpectations_ErrorBitmask & testErrors))) { + (testErrors.includes(kMissingExpectations_ErrorType))) { testsMissingReferenceImages++; } - if (testErrors == (testErrors & kIgnorable_ErrorBitmask)) { + if (testErrors.minus(gmmain.fIgnorableErrorCombination).isEmpty()) { testsPassed++; } else { testsFailed++;