Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(601)

Side by Side Diff: gm/gmmain.cpp

Issue 12825005: gm self-test: make sure we report failures in individual rendering modes (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: update gm self-test expectations to match patchset 3 Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* 1 /*
epoger 2013/03/14 22:36:27 As of patchset 3+4, we now report any differences
2 * Copyright 2011 Google Inc. 2 * Copyright 2011 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 /* 8 /*
9 * Code for the "gm" (Golden Master) rendering comparison tool. 9 * Code for the "gm" (Golden Master) rendering comparison tool.
10 * 10 *
11 * If you make changes to this, re-run the self-tests at gm/tests/run.sh 11 * If you make changes to this, re-run the self-tests at gm/tests/run.sh
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after
187 { " cross-process, shared address", SkGPipeWriter::kCrossProcess_Flag 187 { " cross-process, shared address", SkGPipeWriter::kCrossProcess_Flag
188 | SkGPipeWriter::kSharedAddressSpace_Flag } 188 | SkGPipeWriter::kSharedAddressSpace_Flag }
189 }; 189 };
190 190
191 class GMMain { 191 class GMMain {
192 public: 192 public:
193 GMMain() { 193 GMMain() {
194 // Set default values of member variables, which tool_main() 194 // Set default values of member variables, which tool_main()
195 // may override. 195 // may override.
196 fUseFileHierarchy = false; 196 fUseFileHierarchy = false;
197 fSimulatePipePlaybackFailure = false;
197 fMismatchPath = NULL; 198 fMismatchPath = NULL;
198 } 199 }
199 200
200 SkString make_name(const char shortName[], const char configName[]) { 201 SkString make_name(const char shortName[], const char configName[]) {
201 SkString name; 202 SkString name;
202 if (0 == strlen(configName)) { 203 if (0 == strlen(configName)) {
203 name.append(shortName); 204 name.append(shortName);
204 } else if (fUseFileHierarchy) { 205 } else if (fUseFileHierarchy) {
205 name.appendf("%s%c%s", configName, SkPATH_SEPARATOR, shortName); 206 name.appendf("%s%c%s", configName, SkPATH_SEPARATOR, shortName);
206 } else { 207 } else {
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
254 // The common case: no error means nothing to record. 255 // The common case: no error means nothing to record.
255 if (kEmptyErrorBitfield == errorType) { 256 if (kEmptyErrorBitfield == errorType) {
256 return; 257 return;
257 } 258 }
258 259
259 // If only certain error type(s) were reported, we know we can ignore th em. 260 // If only certain error type(s) were reported, we know we can ignore th em.
260 if (errorType == (errorType & kIgnorable_ErrorBitmask)) { 261 if (errorType == (errorType & kIgnorable_ErrorBitmask)) {
261 return; 262 return;
262 } 263 }
263 264
264 FailRec& rec = fFailedTests.push_back(make_name( 265 SkString completeName = name;
265 name.c_str(), renderModeDescriptor)); 266 completeName.append(renderModeDescriptor);
267 FailRec& rec = fFailedTests.push_back(completeName);
266 rec.fIsPixelError = 268 rec.fIsPixelError =
267 (kEmptyErrorBitfield != (errorType & kImageMismatch_ErrorBitmask)); 269 (kEmptyErrorBitfield != (errorType & kImageMismatch_ErrorBitmask));
268 } 270 }
269 271
270 // List contents of fFailedTests via SkDebug. 272 // List contents of fFailedTests via SkDebug.
271 void ListErrors() { 273 void ListErrors() {
272 for (int i = 0; i < fFailedTests.count(); ++i) { 274 for (int i = 0; i < fFailedTests.count(); ++i) {
273 if (fFailedTests[i].fIsPixelError) { 275 if (fFailedTests[i].fIsPixelError) {
274 gm_fprintf(stderr, "\t\t%s pixel_error\n", fFailedTests[i].fName .c_str()); 276 gm_fprintf(stderr, "\t\t%s pixel_error\n", fFailedTests[i].fName .c_str());
275 } else { 277 } else {
(...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after
587 * _ErrorBitmask values otherwise. 589 * _ErrorBitmask values otherwise.
588 * 590 *
589 * If fMismatchPath has been set, and there are pixel diffs, then the 591 * If fMismatchPath has been set, and there are pixel diffs, then the
590 * actual bitmap will be written out to a file within fMismatchPath. 592 * actual bitmap will be written out to a file within fMismatchPath.
591 * 593 *
592 * @param expectations what expectations to compare actualBitmap against 594 * @param expectations what expectations to compare actualBitmap against
593 * @param actualBitmap the image we actually generated 595 * @param actualBitmap the image we actually generated
594 * @param baseNameString name of test without renderModeDescriptor added 596 * @param baseNameString name of test without renderModeDescriptor added
595 * @param renderModeDescriptor e.g., "-rtree", "-deferred" 597 * @param renderModeDescriptor e.g., "-rtree", "-deferred"
596 * @param addToJsonSummary whether to add these results (both actual and 598 * @param addToJsonSummary whether to add these results (both actual and
597 * expected) to the JSON summary 599 * expected) to the JSON summary. Regardless of this setting, if
598 * 600 * we find an image mismatch in this test, we will write these
599 * TODO: For now, addToJsonSummary is only set to true within 601 * results to the JSON summary. (This is so that we will always
600 * compare_test_results_to_stored_expectations(), so results of our 602 * report errors across rendering modes, such as pipe vs tiled.
601 * in-memory comparisons (Rtree vs regular, etc.) are not written to the 603 * See https://codereview.chromium.org/12825005/
602 * JSON summary. We may wish to change that.
603 */ 604 */
604 ErrorBitfield compare_to_expectations(Expectations expectations, 605 ErrorBitfield compare_to_expectations(Expectations expectations,
605 const SkBitmap& actualBitmap, 606 const SkBitmap& actualBitmap,
606 const SkString& baseNameString, 607 const SkString& baseNameString,
607 const char renderModeDescriptor[], 608 const char renderModeDescriptor[],
608 bool addToJsonSummary=false) { 609 bool addToJsonSummary) {
609 ErrorBitfield retval; 610 ErrorBitfield retval;
610 Checksum actualChecksum = SkBitmapChecksummer::Compute64(actualBitmap); 611 Checksum actualChecksum = SkBitmapChecksummer::Compute64(actualBitmap);
611 SkString completeNameString = baseNameString; 612 SkString completeNameString = baseNameString;
612 completeNameString.append(renderModeDescriptor); 613 completeNameString.append(renderModeDescriptor);
613 const char* completeName = completeNameString.c_str(); 614 const char* completeName = completeNameString.c_str();
614 615
615 if (expectations.empty()) { 616 if (expectations.empty()) {
616 retval = kMissingExpectations_ErrorBitmask; 617 retval = kMissingExpectations_ErrorBitmask;
617 } else if (expectations.match(actualChecksum)) { 618 } else if (expectations.match(actualChecksum)) {
618 retval = kEmptyErrorBitfield; 619 retval = kEmptyErrorBitfield;
619 } else { 620 } else {
621 addToJsonSummary = true;
620 retval = kImageMismatch_ErrorBitmask; 622 retval = kImageMismatch_ErrorBitmask;
621 623
622 // Write out the "actuals" for any mismatches, if we have 624 // Write out the "actuals" for any mismatches, if we have
623 // been directed to do so. 625 // been directed to do so.
624 if (fMismatchPath) { 626 if (fMismatchPath) {
625 SkString path = 627 SkString path =
626 make_filename(fMismatchPath, renderModeDescriptor, 628 make_filename(fMismatchPath, renderModeDescriptor,
627 baseNameString.c_str(), "png"); 629 baseNameString.c_str(), "png");
628 write_bitmap(path, actualBitmap); 630 write_bitmap(path, actualBitmap);
629 } 631 }
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
777 * @param referenceBitmap bitmap we expected to be generated 779 * @param referenceBitmap bitmap we expected to be generated
778 */ 780 */
779 ErrorBitfield compare_test_results_to_reference_bitmap( 781 ErrorBitfield compare_test_results_to_reference_bitmap(
780 GM* gm, const ConfigData& gRec, const char renderModeDescriptor [], 782 GM* gm, const ConfigData& gRec, const char renderModeDescriptor [],
781 SkBitmap& actualBitmap, const SkBitmap* referenceBitmap) { 783 SkBitmap& actualBitmap, const SkBitmap* referenceBitmap) {
782 784
783 SkASSERT(referenceBitmap); 785 SkASSERT(referenceBitmap);
784 SkString name = make_name(gm->shortName(), gRec.fName); 786 SkString name = make_name(gm->shortName(), gRec.fName);
785 Expectations expectations(*referenceBitmap); 787 Expectations expectations(*referenceBitmap);
786 return compare_to_expectations(expectations, actualBitmap, 788 return compare_to_expectations(expectations, actualBitmap,
787 name, renderModeDescriptor); 789 name, renderModeDescriptor, false);
788 } 790 }
789 791
790 static SkPicture* generate_new_picture(GM* gm, BbhType bbhType, uint32_t rec ordFlags, 792 static SkPicture* generate_new_picture(GM* gm, BbhType bbhType, uint32_t rec ordFlags,
791 SkScalar scale = SK_Scalar1) { 793 SkScalar scale = SK_Scalar1) {
792 // Pictures are refcounted so must be on heap 794 // Pictures are refcounted so must be on heap
793 SkPicture* pict; 795 SkPicture* pict;
794 int width = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().w idth()), scale)); 796 int width = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().w idth()), scale));
795 int height = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize(). height()), scale)); 797 int height = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize(). height()), scale));
796 798
797 if (kTileGrid_BbhType == bbhType) { 799 if (kTileGrid_BbhType == bbhType) {
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
898 ErrorBitfield errors = kEmptyErrorBitfield; 900 ErrorBitfield errors = kEmptyErrorBitfield;
899 for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) { 901 for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) {
900 SkBitmap bitmap; 902 SkBitmap bitmap;
901 SkISize size = gm->getISize(); 903 SkISize size = gm->getISize();
902 setup_bitmap(gRec, size, &bitmap); 904 setup_bitmap(gRec, size, &bitmap);
903 SkCanvas canvas(bitmap); 905 SkCanvas canvas(bitmap);
904 PipeController pipeController(&canvas); 906 PipeController pipeController(&canvas);
905 SkGPipeWriter writer; 907 SkGPipeWriter writer;
906 SkCanvas* pipeCanvas = writer.startRecording( 908 SkCanvas* pipeCanvas = writer.startRecording(
907 &pipeController, gPipeWritingFlagCombos[i].flags); 909 &pipeController, gPipeWritingFlagCombos[i].flags);
908 invokeGM(gm, pipeCanvas, false, false); 910 if (!this->fSimulatePipePlaybackFailure) {
911 invokeGM(gm, pipeCanvas, false, false);
912 }
909 complete_bitmap(&bitmap); 913 complete_bitmap(&bitmap);
910 writer.endRecording(); 914 writer.endRecording();
911 SkString string("-pipe"); 915 SkString string("-pipe");
912 string.append(gPipeWritingFlagCombos[i].name); 916 string.append(gPipeWritingFlagCombos[i].name);
913 errors |= compare_test_results_to_reference_bitmap( 917 errors |= compare_test_results_to_reference_bitmap(
914 gm, gRec, string.c_str(), bitmap, &referenceBitmap); 918 gm, gRec, string.c_str(), bitmap, &referenceBitmap);
915 if (errors != kEmptyErrorBitfield) { 919 if (errors != kEmptyErrorBitfield) {
916 break; 920 break;
917 } 921 }
918 } 922 }
(...skipping 25 matching lines...) Expand all
944 } 948 }
945 return errors; 949 return errors;
946 } 950 }
947 951
948 // 952 //
949 // member variables. 953 // member variables.
950 // They are public for now, to allow easier setting by tool_main(). 954 // They are public for now, to allow easier setting by tool_main().
951 // 955 //
952 956
953 bool fUseFileHierarchy; 957 bool fUseFileHierarchy;
958 bool fSimulatePipePlaybackFailure;
954 959
955 const char* fMismatchPath; 960 const char* fMismatchPath;
956 961
957 // information about all failed tests we have encountered so far 962 // information about all failed tests we have encountered so far
958 SkTArray<FailRec> fFailedTests; 963 SkTArray<FailRec> fFailedTests;
959 964
960 // Where to read expectations (expected image checksums, etc.) from. 965 // Where to read expectations (expected image checksums, etc.) from.
961 // If unset, we don't do comparisons. 966 // If unset, we don't do comparisons.
962 SkAutoTUnref<ExpectationsSource> fExpectationsSource; 967 SkAutoTUnref<ExpectationsSource> fExpectationsSource;
963 968
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
1047 " [--modulo <remainder> <divisor>]: only run tests for which \n" 1052 " [--modulo <remainder> <divisor>]: only run tests for which \n"
1048 " testIndex %% divisor == remainder\n" 1053 " testIndex %% divisor == remainder\n"
1049 " [--nopdf]: skip the pdf rendering test pass\n" 1054 " [--nopdf]: skip the pdf rendering test pass\n"
1050 " [--nopipe]: Skip SkGPipe replay\n" 1055 " [--nopipe]: Skip SkGPipe replay\n"
1051 " [--readPath|-r <path>]: read reference images from this dir, and report\n" 1056 " [--readPath|-r <path>]: read reference images from this dir, and report\n"
1052 " any differences between those and the newly generated ones\n" 1057 " any differences between those and the newly generated ones\n"
1053 " [--noreplay]: do not exercise SkPicture replay\n" 1058 " [--noreplay]: do not exercise SkPicture replay\n"
1054 " [--resourcePath|-i <path>]: directory that stores image resources\n" 1059 " [--resourcePath|-i <path>]: directory that stores image resources\n"
1055 " [--nortree]: Do not exercise the R-Tree variant of SkPicture\n" 1060 " [--nortree]: Do not exercise the R-Tree variant of SkPicture\n"
1056 " [--noserialize]: do not exercise SkPicture serialization & deserialization\ n" 1061 " [--noserialize]: do not exercise SkPicture serialization & deserialization\ n"
1062 " [--simulatePipePlaybackFailure]: simulate a rendering failure in pipe mode only\n"
1057 " [--tiledPipe]: Exercise tiled SkGPipe replay\n" 1063 " [--tiledPipe]: Exercise tiled SkGPipe replay\n"
1058 " [--notileGrid]: Do not exercise the tile grid variant of SkPicture\n" 1064 " [--notileGrid]: Do not exercise the tile grid variant of SkPicture\n"
1059 " [--tileGridReplayScales <scales>]: Comma separated list of floating-point s cale\n" 1065 " [--tileGridReplayScales <scales>]: Comma separated list of floating-point s cale\n"
1060 " factors to be used for tileGrid playback testing. Default value: 1.0\n" 1066 " factors to be used for tileGrid playback testing. Default value: 1.0\n"
1061 " [--writeJsonSummary <path>]: write a JSON-formatted result summary to this file\n" 1067 " [--writeJsonSummary <path>]: write a JSON-formatted result summary to this file\n"
1062 " [--verbose] print diagnostics (e.g. list each config to be tested)\n" 1068 " [--verbose] print diagnostics (e.g. list each config to be tested)\n"
1063 " [--writePath|-w <path>]: write rendered images into this directory\n" 1069 " [--writePath|-w <path>]: write rendered images into this directory\n"
1064 " [--writePicturePath|-wp <path>]: write .skp files into this directory\n" 1070 " [--writePicturePath|-wp <path>]: write .skp files into this directory\n"
1065 ); 1071 );
1066 } 1072 }
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
1129 #endif 1135 #endif
1130 } 1136 }
1131 1137
1132 template <typename T> void appendUnique(SkTDArray<T>* array, const T& value) { 1138 template <typename T> void appendUnique(SkTDArray<T>* array, const T& value) {
1133 int index = array->find(value); 1139 int index = array->find(value);
1134 if (index < 0) { 1140 if (index < 0) {
1135 *array->append() = value; 1141 *array->append() = value;
1136 } 1142 }
1137 } 1143 }
1138 1144
1145 /**
1146 * Run this test in a number of different configs (8888, 565, PDF,
1147 * etc.), confirming that the resulting bitmaps match expectations
1148 * (which may be different for each config).
1149 */
1150 ErrorBitfield run_multiple_configs(GMMain &gmmain, GM *gm, const SkTDArray<size_ t> &configs,
1151 GrContextFactory *grFactory, int gpuCacheSize Bytes,
1152 int gpuCacheSizeCount, const char *writePath, bool doPDF,
1153 bool doDeferred) {
1154 uint32_t gmFlags = gm->getFlags();
1155 ErrorBitfield testErrors = kEmptyErrorBitfield;
1156 for (int i = 0; i < configs.count(); i++) {
1157 ConfigData config = gRec[configs[i]];
1158
1159 // Skip any tests that we don't even need to try.
1160 if ((kPDF_Backend == config.fBackend) &&
1161 (!doPDF || (gmFlags & GM::kSkipPDF_Flag)))
1162 {
1163 continue;
1164 }
1165 if ((gmFlags & GM::kSkip565_Flag) &&
1166 (kRaster_Backend == config.fBackend) &&
1167 (SkBitmap::kRGB_565_Config == config.fConfig)) {
1168 continue;
1169 }
1170 if ((gmFlags & GM::kSkipGPU_Flag) &&
1171 kGPU_Backend == config.fBackend) {
1172 continue;
1173 }
1174
1175 // Now we know that we want to run this test and record its
1176 // success or failure.
1177 ErrorBitfield renderErrors = kEmptyErrorBitfield;
1178 GrRenderTarget* renderTarget = NULL;
1179 #if SK_SUPPORT_GPU
1180 SkAutoTUnref<GrRenderTarget> rt;
1181 AutoResetGr autogr;
1182 if ((kEmptyErrorBitfield == renderErrors) &&
1183 kGPU_Backend == config.fBackend) {
1184 GrContext* gr = grFactory->get(config.fGLContextType);
1185 bool grSuccess = false;
1186 if (gr) {
1187 // create a render target to back the device
1188 GrTextureDesc desc;
1189 desc.fConfig = kSkia8888_GrPixelConfig;
1190 desc.fFlags = kRenderTarget_GrTextureFlagBit;
1191 desc.fWidth = gm->getISize().width();
1192 desc.fHeight = gm->getISize().height();
1193 desc.fSampleCnt = config.fSampleCnt;
1194 GrTexture* tex = gr->createUncachedTexture(desc, NULL, 0);
1195 if (tex) {
1196 rt.reset(tex->asRenderTarget());
1197 rt.get()->ref();
1198 tex->unref();
1199 autogr.set(gr);
1200 renderTarget = rt.get();
1201 grSuccess = NULL != renderTarget;
1202 }
1203 // Set the user specified cache limits if non-default.
1204 size_t bytes;
1205 int count;
1206 gr->getTextureCacheLimits(&count, &bytes);
1207 if (-1 != gpuCacheSizeBytes) {
1208 bytes = static_cast<size_t>(gpuCacheSizeBytes);
1209 }
1210 if (-1 != gpuCacheSizeCount) {
1211 count = gpuCacheSizeCount;
1212 }
1213 gr->setTextureCacheLimits(count, bytes);
1214 }
1215 if (!grSuccess) {
1216 renderErrors |= kNoGpuContext_ErrorBitmask;
1217 }
1218 }
1219 #endif
1220
1221 SkBitmap comparisonBitmap;
1222
1223 if (kEmptyErrorBitfield == renderErrors) {
1224 renderErrors |= gmmain.test_drawing(gm, config, writePath,
1225 GetGr(),
1226 renderTarget,
1227 &comparisonBitmap);
1228 }
1229
1230 if (doDeferred && !renderErrors &&
1231 (kGPU_Backend == config.fBackend ||
1232 kRaster_Backend == config.fBackend)) {
1233 renderErrors |= gmmain.test_deferred_drawing(gm, config,
1234 comparisonBitmap,
1235 GetGr(),
1236 renderTarget);
1237 }
1238
1239 testErrors |= renderErrors;
1240 }
1241 return testErrors;
1242 }
1243
1244 /**
1245 * Run this test in a number of different drawing modes (pipe,
1246 * deferred, tiled, etc.), confirming that the resulting bitmaps are
1247 * *exactly* the same in all drawing modes.
1248 *
1249 * TODO(epoger): Right now, we only run the different drawing modes
1250 * with the 8888 config. Would there be value in running all those
1251 * different drawing modes in whatever configs (8888, 565, PDF) we are
1252 * testing?
1253 */
1254 ErrorBitfield run_multiple_drawing_modes(GMMain &gmmain, GM *gm,
1255 const char *writePicturePath, bool doRe play,
1256 bool doSerialize, bool doRTree, bool do TileGrid,
1257 const SkTDArray<SkScalar> &tileGridRepl ayScales,
1258 bool doPipe, bool doTiledPipe) {
1259 uint32_t gmFlags = gm->getFlags();
1260 SkBitmap comparisonBitmap;
1261 const ConfigData compareConfig =
1262 { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType, 0,
1263 kRW_ConfigFlag, "comparison" };
1264 ErrorBitfield testErrors = gmmain.generate_image(gm, compareConfig, NULL, NU LL,
1265 &comparisonBitmap, false);
1266
1267 // run the picture centric GM steps
1268 if (!(gmFlags & GM::kSkipPicture_Flag)) {
1269
1270 ErrorBitfield pictErrors = kEmptyErrorBitfield;
1271
1272 //SkAutoTUnref<SkPicture> pict(generate_new_picture(gm));
1273 SkPicture* pict = gmmain.generate_new_picture(gm, kNone_BbhType, 0);
1274 SkAutoUnref aur(pict);
1275
1276 if ((kEmptyErrorBitfield == testErrors) && doReplay) {
1277 SkBitmap bitmap;
1278 gmmain.generate_image_from_picture(gm, compareConfig, pict,
1279 &bitmap);
1280 pictErrors |= gmmain.compare_test_results_to_reference_bitmap(
1281 gm, compareConfig, "-replay", bitmap, &comparisonBitmap);
1282 }
1283
1284 if ((kEmptyErrorBitfield == testErrors) &&
1285 (kEmptyErrorBitfield == pictErrors) &&
1286 doSerialize) {
1287 SkPicture* repict = gmmain.stream_to_new_picture(*pict);
1288 SkAutoUnref aurr(repict);
1289
1290 SkBitmap bitmap;
1291 gmmain.generate_image_from_picture(gm, compareConfig, repict,
1292 &bitmap);
1293 pictErrors |= gmmain.compare_test_results_to_reference_bitmap(
1294 gm, co mpareConfig, "-serialize", bitmap, &comparisonBitmap);
1295 }
1296
1297 if (writePicturePath) {
1298 const char* pictureSuffix = "skp";
1299 SkString path = make_filename(writePicturePath, "",
1300 gm->shortName(),
1301 pictureSuffix);
1302 SkFILEWStream stream(path.c_str());
1303 pict->serialize(&stream);
1304 }
1305
1306 testErrors |= pictErrors;
1307 }
1308
1309 // TODO: add a test in which the RTree rendering results in a
1310 // different bitmap than the standard rendering. It should
1311 // show up as failed in the JSON summary, and should be listed
1312 // in the stdout also.
1313 if (!(gmFlags & GM::kSkipPicture_Flag) && doRTree) {
1314 SkPicture* pict = gmmain.generate_new_picture(
1315 gm, kRTree_BbhType, SkPict ure::kUsePathBoundsForClip_RecordingFlag);
1316 SkAutoUnref aur(pict);
1317 SkBitmap bitmap;
1318 gmmain.generate_image_from_picture(gm, compareConfig, pict,
1319 &bitmap);
1320 testErrors |= gmmain.compare_test_results_to_reference_bitmap(
1321 gm, compar eConfig, "-rtree", bitmap, &comparisonBitmap);
1322 }
1323
1324 if (!(gmFlags & GM::kSkipPicture_Flag) && doTileGrid) {
1325 for(int scaleIndex = 0; scaleIndex < tileGridReplayScales.count(); ++sca leIndex) {
1326 SkScalar replayScale = tileGridReplayScales[scaleIndex];
1327 if ((gmFlags & GM::kSkipScaledReplay_Flag) && replayScale != 1)
1328 continue;
1329 // We record with the reciprocal scale to obtain a replay
1330 // result that can be validated against comparisonBitmap.
1331 SkScalar recordScale = SkScalarInvert(replayScale);
1332 SkPicture* pict = gmmain.generate_new_picture(
1333 gm, kTileGrid_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag,
1334 recordScale);
1335 SkAutoUnref aur(pict);
1336 SkBitmap bitmap;
1337 gmmain.generate_image_from_picture(gm, compareConfig, pict,
1338 &bitmap, replayScale);
1339 SkString suffix("-tilegrid");
1340 if (SK_Scalar1 != replayScale) {
1341 suffix += "-scale-";
1342 suffix.appendScalar(replayScale);
1343 }
1344 testErrors |= gmmain.compare_test_results_to_reference_bitmap(
1345 gm, co mpareConfig, suffix.c_str(), bitmap,
1346 &compa risonBitmap);
1347 }
1348 }
1349
1350 // run the pipe centric GM steps
1351 if (!(gmFlags & GM::kSkipPipe_Flag)) {
1352
1353 ErrorBitfield pipeErrors = kEmptyErrorBitfield;
1354
1355 if ((kEmptyErrorBitfield == testErrors) && doPipe) {
1356 pipeErrors |= gmmain.test_pipe_playback(gm, compareConfig,
1357 comparisonBitmap);
1358 }
1359
1360 if ((kEmptyErrorBitfield == testErrors) &&
1361 (kEmptyErrorBitfield == pipeErrors) &&
1362 doTiledPipe && !(gmFlags & GM::kSkipTiled_Flag)) {
1363 pipeErrors |= gmmain.test_tiled_pipe_playback(gm, compareConfig,
1364 comparisonBitmap);
1365 }
1366
1367 testErrors |= pipeErrors;
1368 }
1369 return testErrors;
1370 }
1371
1139 int tool_main(int argc, char** argv); 1372 int tool_main(int argc, char** argv);
1140 int tool_main(int argc, char** argv) { 1373 int tool_main(int argc, char** argv) {
1141 1374
1142 #if SK_ENABLE_INST_COUNT 1375 #if SK_ENABLE_INST_COUNT
1143 gPrintInstCount = true; 1376 gPrintInstCount = true;
1144 #endif 1377 #endif
1145 1378
1146 SkGraphics::Init(); 1379 SkGraphics::Init();
1147 // we don't need to see this during a run 1380 // we don't need to see this during a run
1148 gSkSuppressFontCachePurgeSpew = true; 1381 gSkSuppressFontCachePurgeSpew = true;
(...skipping 24 matching lines...) Expand all
1173 1406
1174 SkTDArray<size_t> configs; 1407 SkTDArray<size_t> configs;
1175 SkTDArray<size_t> excludeConfigs; 1408 SkTDArray<size_t> excludeConfigs;
1176 SkTDArray<SkScalar> tileGridReplayScales; 1409 SkTDArray<SkScalar> tileGridReplayScales;
1177 *tileGridReplayScales.append() = SK_Scalar1; // By default only test at scal e 1.0 1410 *tileGridReplayScales.append() = SK_Scalar1; // By default only test at scal e 1.0
1178 bool userConfig = false; 1411 bool userConfig = false;
1179 1412
1180 int moduloRemainder = -1; 1413 int moduloRemainder = -1;
1181 int moduloDivisor = -1; 1414 int moduloDivisor = -1;
1182 1415
1183 #if SK_SUPPORT_GPU 1416 int gpuCacheSizeBytes = -1;
1184 struct { 1417 int gpuCacheSizeCount = -1;
1185 int fBytes; 1418 // -1 means use the default
1186 int fCount;
1187 } gpuCacheSize = { -1, -1 }; // -1s mean use the default
1188 #endif
1189 1419
1190 const char* const commandName = argv[0]; 1420 const char* const commandName = argv[0];
1191 char* const* stop = argv + argc; 1421 char* const* stop = argv + argc;
1192 for (++argv; argv < stop; ++argv) { 1422 for (++argv; argv < stop; ++argv) {
1193 if (strcmp(*argv, "--config") == 0) { 1423 if (strcmp(*argv, "--config") == 0) {
1194 argv++; 1424 argv++;
1195 if (argv < stop) { 1425 if (argv < stop) {
1196 int index = findConfig(*argv); 1426 int index = findConfig(*argv);
1197 if (index >= 0) { 1427 if (index >= 0) {
1198 appendUnique<size_t>(&configs, index); 1428 appendUnique<size_t>(&configs, index);
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
1254 usage(commandName); 1484 usage(commandName);
1255 return -1; 1485 return -1;
1256 } 1486 }
1257 } else if (strcmp(*argv, "--enable-missing-warning") == 0) { 1487 } else if (strcmp(*argv, "--enable-missing-warning") == 0) {
1258 notifyMissingReadReference = true; 1488 notifyMissingReadReference = true;
1259 } else if (strcmp(*argv, "--forceBWtext") == 0) { 1489 } else if (strcmp(*argv, "--forceBWtext") == 0) {
1260 gForceBWtext = true; 1490 gForceBWtext = true;
1261 #if SK_SUPPORT_GPU 1491 #if SK_SUPPORT_GPU
1262 } else if (strcmp(*argv, "--gpuCacheSize") == 0) { 1492 } else if (strcmp(*argv, "--gpuCacheSize") == 0) {
1263 if (stop - argv > 2) { 1493 if (stop - argv > 2) {
1264 gpuCacheSize.fBytes = atoi(*++argv); 1494 gpuCacheSizeBytes = atoi(*++argv);
1265 gpuCacheSize.fCount = atoi(*++argv); 1495 gpuCacheSizeCount = atoi(*++argv);
1266 } else { 1496 } else {
1267 gm_fprintf(stderr, "missing arg for --gpuCacheSize\n"); 1497 gm_fprintf(stderr, "missing arg for --gpuCacheSize\n");
1268 usage(commandName); 1498 usage(commandName);
1269 return -1; 1499 return -1;
1270 } 1500 }
1271 #endif 1501 #endif
1272 } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) { 1502 } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
1273 usage(commandName); 1503 usage(commandName);
1274 return -1; 1504 return -1;
1275 } else if (strcmp(*argv, "--hierarchy") == 0) { 1505 } else if (strcmp(*argv, "--hierarchy") == 0) {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
1313 } else if ((0 == strcmp(*argv, "--resourcePath")) || 1543 } else if ((0 == strcmp(*argv, "--resourcePath")) ||
1314 (0 == strcmp(*argv, "-i"))) { 1544 (0 == strcmp(*argv, "-i"))) {
1315 argv++; 1545 argv++;
1316 if (argv < stop && **argv) { 1546 if (argv < stop && **argv) {
1317 resourcePath = *argv; 1547 resourcePath = *argv;
1318 } 1548 }
1319 } else if (strcmp(*argv, "--serialize") == 0) { 1549 } else if (strcmp(*argv, "--serialize") == 0) {
1320 doSerialize = true; 1550 doSerialize = true;
1321 } else if (strcmp(*argv, "--noserialize") == 0) { 1551 } else if (strcmp(*argv, "--noserialize") == 0) {
1322 doSerialize = false; 1552 doSerialize = false;
1553 } else if (strcmp(*argv, "--simulatePipePlaybackFailure") == 0) {
1554 gmmain.fSimulatePipePlaybackFailure = true;
1323 } else if (strcmp(*argv, "--tiledPipe") == 0) { 1555 } else if (strcmp(*argv, "--tiledPipe") == 0) {
1324 doTiledPipe = true; 1556 doTiledPipe = true;
1325 } else if (!strcmp(*argv, "--verbose") || !strcmp(*argv, "-v")) { 1557 } else if (!strcmp(*argv, "--verbose") || !strcmp(*argv, "-v")) {
1326 doVerbose = true; 1558 doVerbose = true;
1327 } else if ((0 == strcmp(*argv, "--writePath")) || 1559 } else if ((0 == strcmp(*argv, "--writePath")) ||
1328 (0 == strcmp(*argv, "-w"))) { 1560 (0 == strcmp(*argv, "-w"))) {
1329 argv++; 1561 argv++;
1330 if (argv < stop && **argv) { 1562 if (argv < stop && **argv) {
1331 writePath = *argv; 1563 writePath = *argv;
1332 } 1564 }
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
1408 moduloRemainder = -1; 1640 moduloRemainder = -1;
1409 } 1641 }
1410 if (moduloRemainder < 0 || moduloRemainder >= moduloDivisor) { 1642 if (moduloRemainder < 0 || moduloRemainder >= moduloDivisor) {
1411 moduloRemainder = -1; 1643 moduloRemainder = -1;
1412 } 1644 }
1413 1645
1414 // Accumulate success of all tests. 1646 // Accumulate success of all tests.
1415 int testsRun = 0; 1647 int testsRun = 0;
1416 int testsPassed = 0; 1648 int testsPassed = 0;
1417 int testsFailed = 0; 1649 int testsFailed = 0;
1650 int testsWithDrawingModeDiscrepancies = 0;
1418 int testsMissingReferenceImages = 0; 1651 int testsMissingReferenceImages = 0;
1419 1652
1420 #if SK_SUPPORT_GPU 1653 #if SK_SUPPORT_GPU
1421 GrContextFactory* grFactory = new GrContextFactory; 1654 GrContextFactory* grFactory = new GrContextFactory;
1422 #endif 1655 #endif
1423 1656
1424 int gmIndex = -1; 1657 int gmIndex = -1;
1425 SkString moduloStr; 1658 SkString moduloStr;
1426 1659
1427 // If we will be writing out files, prepare subdirectories. 1660 // If we will be writing out files, prepare subdirectories.
(...skipping 29 matching lines...) Expand all
1457 const char* shortName = gm->shortName(); 1690 const char* shortName = gm->shortName();
1458 if (skip_name(fMatches, shortName)) { 1691 if (skip_name(fMatches, shortName)) {
1459 SkDELETE(gm); 1692 SkDELETE(gm);
1460 continue; 1693 continue;
1461 } 1694 }
1462 1695
1463 SkISize size = gm->getISize(); 1696 SkISize size = gm->getISize();
1464 gm_fprintf(stdout, "%sdrawing... %s [%d %d]\n", moduloStr.c_str(), short Name, 1697 gm_fprintf(stdout, "%sdrawing... %s [%d %d]\n", moduloStr.c_str(), short Name,
1465 size.width(), size.height()); 1698 size.width(), size.height());
1466 1699
1467 ErrorBitfield testErrors = kEmptyErrorBitfield; 1700 ErrorBitfield compositeErrors = kEmptyErrorBitfield;
1468 uint32_t gmFlags = gm->getFlags(); 1701 ErrorBitfield multipleConfigErrors = run_multiple_configs(
1702 gmmain, gm, configs, grFactory, gpuCacheSizeBytes, gpuCacheSizeCount , writePath,
1703 doPDF, doDeferred);
1704 compositeErrors |= multipleConfigErrors;
1705 ErrorBitfield multipleModeErrors = run_multiple_drawing_modes(
1706 gmmain, gm, writePicturePath, doReplay, doSerialize, doRTree, doTile Grid,
1707 tileGridReplayScales, doPipe, doTiledPipe);
1708 compositeErrors |= multipleModeErrors;
1469 1709
1470 for (int i = 0; i < configs.count(); i++) { 1710 // A non-ignorable error in run_multiple_configs, or ANY error in
1471 ConfigData config = gRec[configs[i]]; 1711 // run_multiple_drawing_modes, counts as a failure.
1472
1473 // Skip any tests that we don't even need to try.
1474 if ((kPDF_Backend == config.fBackend) &&
1475 (!doPDF || (gmFlags & GM::kSkipPDF_Flag)))
1476 {
1477 continue;
1478 }
1479 if ((gmFlags & GM::kSkip565_Flag) &&
1480 (kRaster_Backend == config.fBackend) &&
1481 (SkBitmap::kRGB_565_Config == config.fConfig)) {
1482 continue;
1483 }
1484 if ((gmFlags & GM::kSkipGPU_Flag) &&
1485 kGPU_Backend == config.fBackend) {
1486 continue;
1487 }
1488
1489 // Now we know that we want to run this test and record its
1490 // success or failure.
1491 ErrorBitfield renderErrors = kEmptyErrorBitfield;
1492 GrRenderTarget* renderTarget = NULL;
1493 #if SK_SUPPORT_GPU
1494 SkAutoTUnref<GrRenderTarget> rt;
1495 AutoResetGr autogr;
1496 if ((kEmptyErrorBitfield == renderErrors) &&
1497 kGPU_Backend == config.fBackend) {
1498 GrContext* gr = grFactory->get(config.fGLContextType);
1499 bool grSuccess = false;
1500 if (gr) {
1501 // create a render target to back the device
1502 GrTextureDesc desc;
1503 desc.fConfig = kSkia8888_GrPixelConfig;
1504 desc.fFlags = kRenderTarget_GrTextureFlagBit;
1505 desc.fWidth = gm->getISize().width();
1506 desc.fHeight = gm->getISize().height();
1507 desc.fSampleCnt = config.fSampleCnt;
1508 GrTexture* tex = gr->createUncachedTexture(desc, NULL, 0);
1509 if (tex) {
1510 rt.reset(tex->asRenderTarget());
1511 rt.get()->ref();
1512 tex->unref();
1513 autogr.set(gr);
1514 renderTarget = rt.get();
1515 grSuccess = NULL != renderTarget;
1516 }
1517 // Set the user specified cache limits if non-default.
1518 size_t bytes;
1519 int count;
1520 gr->getTextureCacheLimits(&count, &bytes);
1521 if (-1 != gpuCacheSize.fBytes) {
1522 bytes = static_cast<size_t>(gpuCacheSize.fBytes);
1523 }
1524 if (-1 != gpuCacheSize.fCount) {
1525 count = gpuCacheSize.fCount;
1526 }
1527 gr->setTextureCacheLimits(count, bytes);
1528 }
1529 if (!grSuccess) {
1530 renderErrors |= kNoGpuContext_ErrorBitmask;
1531 }
1532 }
1533 #endif
1534
1535 SkBitmap comparisonBitmap;
1536
1537 if (kEmptyErrorBitfield == renderErrors) {
1538 renderErrors |= gmmain.test_drawing(gm, config, writePath,
1539 GetGr(),
1540 renderTarget,
1541 &comparisonBitmap);
1542 }
1543
1544 if (doDeferred && !renderErrors &&
1545 (kGPU_Backend == config.fBackend ||
1546 kRaster_Backend == config.fBackend)) {
1547 renderErrors |= gmmain.test_deferred_drawing(gm, config,
1548 comparisonBitmap,
1549 GetGr(),
1550 renderTarget);
1551 }
1552
1553 testErrors |= renderErrors;
1554 }
1555
1556 SkBitmap comparisonBitmap;
1557 const ConfigData compareConfig =
1558 { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextT ype, 0, kRW_ConfigFlag, "comparison" };
1559 testErrors |= gmmain.generate_image(gm, compareConfig, NULL, NULL, &comp arisonBitmap, false);
1560
1561 // run the picture centric GM steps
1562 if (!(gmFlags & GM::kSkipPicture_Flag)) {
1563
1564 ErrorBitfield pictErrors = kEmptyErrorBitfield;
1565
1566 //SkAutoTUnref<SkPicture> pict(generate_new_picture(gm));
1567 SkPicture* pict = gmmain.generate_new_picture(gm, kNone_BbhType, 0);
1568 SkAutoUnref aur(pict);
1569
1570 if ((kEmptyErrorBitfield == testErrors) && doReplay) {
1571 SkBitmap bitmap;
1572 gmmain.generate_image_from_picture(gm, compareConfig, pict,
1573 &bitmap);
1574 pictErrors |= gmmain.compare_test_results_to_reference_bitmap(
1575 gm, compareConfig, "-replay", bitmap, &comparisonBitmap);
1576 }
1577
1578 if ((kEmptyErrorBitfield == testErrors) &&
1579 (kEmptyErrorBitfield == pictErrors) &&
1580 doSerialize) {
1581 SkPicture* repict = gmmain.stream_to_new_picture(*pict);
1582 SkAutoUnref aurr(repict);
1583
1584 SkBitmap bitmap;
1585 gmmain.generate_image_from_picture(gm, compareConfig, repict,
1586 &bitmap);
1587 pictErrors |= gmmain.compare_test_results_to_reference_bitmap(
1588 gm, compareConfig, "-serialize", bitmap, &comparisonBitmap);
1589 }
1590
1591 if (writePicturePath) {
1592 const char* pictureSuffix = "skp";
1593 SkString path = make_filename(writePicturePath, "",
1594 gm->shortName(),
1595 pictureSuffix);
1596 SkFILEWStream stream(path.c_str());
1597 pict->serialize(&stream);
1598 }
1599
1600 testErrors |= pictErrors;
1601 }
1602
1603 // TODO: add a test in which the RTree rendering results in a
1604 // different bitmap than the standard rendering. It should
1605 // show up as failed in the JSON summary, and should be listed
1606 // in the stdout also.
1607 if (!(gmFlags & GM::kSkipPicture_Flag) && doRTree) {
1608 SkPicture* pict = gmmain.generate_new_picture(
1609 gm, kRTree_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFl ag);
1610 SkAutoUnref aur(pict);
1611 SkBitmap bitmap;
1612 gmmain.generate_image_from_picture(gm, compareConfig, pict,
1613 &bitmap);
1614 testErrors |= gmmain.compare_test_results_to_reference_bitmap(
1615 gm, compareConfig, "-rtree", bitmap, &comparisonBitmap);
1616 }
1617
1618 if (!(gmFlags & GM::kSkipPicture_Flag) && doTileGrid) {
1619 for(int scaleIndex = 0; scaleIndex < tileGridReplayScales.count(); + +scaleIndex) {
1620 SkScalar replayScale = tileGridReplayScales[scaleIndex];
1621 if ((gmFlags & GM::kSkipScaledReplay_Flag) && replayScale != 1)
1622 continue;
1623 // We record with the reciprocal scale to obtain a replay
1624 // result that can be validated against comparisonBitmap.
1625 SkScalar recordScale = SkScalarInvert(replayScale);
1626 SkPicture* pict = gmmain.generate_new_picture(
1627 gm, kTileGrid_BbhType, SkPicture::kUsePathBoundsForClip_Reco rdingFlag,
1628 recordScale);
1629 SkAutoUnref aur(pict);
1630 SkBitmap bitmap;
1631 gmmain.generate_image_from_picture(gm, compareConfig, pict,
1632 &bitmap, replayScale);
1633 SkString suffix("-tilegrid");
1634 if (SK_Scalar1 != replayScale) {
1635 suffix += "-scale-";
1636 suffix.appendScalar(replayScale);
1637 }
1638 testErrors |= gmmain.compare_test_results_to_reference_bitmap(
1639 gm, compareConfig, suffix.c_str(), bitmap,
1640 &comparisonBitmap);
1641 }
1642 }
1643
1644 // run the pipe centric GM steps
1645 if (!(gmFlags & GM::kSkipPipe_Flag)) {
1646
1647 ErrorBitfield pipeErrors = kEmptyErrorBitfield;
1648
1649 if ((kEmptyErrorBitfield == testErrors) && doPipe) {
1650 pipeErrors |= gmmain.test_pipe_playback(gm, compareConfig,
1651 comparisonBitmap);
1652 }
1653
1654 if ((kEmptyErrorBitfield == testErrors) &&
1655 (kEmptyErrorBitfield == pipeErrors) &&
1656 doTiledPipe && !(gmFlags & GM::kSkipTiled_Flag)) {
1657 pipeErrors |= gmmain.test_tiled_pipe_playback(gm, compareConfig,
1658 comparisonBitmap);
1659 }
1660
1661 testErrors |= pipeErrors;
1662 }
1663
1664 // Update overall results.
1665 // We only tabulate the particular error types that we currently
1666 // care about (e.g., missing reference images). Later on, if we
1667 // want to also tabulate other error types, we can do so.
1668 testsRun++; 1712 testsRun++;
1669 if (!gmmain.fExpectationsSource.get() || 1713 if (kEmptyErrorBitfield != multipleModeErrors) {
1670 (kEmptyErrorBitfield != (kMissingExpectations_ErrorBitmask & testErr ors))) { 1714 testsWithDrawingModeDiscrepancies++;
1671 testsMissingReferenceImages++; 1715 testsFailed++;
1672 } 1716 } else if (compositeErrors == (compositeErrors & kIgnorable_ErrorBitmask )) {
1673 if (testErrors == (testErrors & kIgnorable_ErrorBitmask)) {
1674 testsPassed++; 1717 testsPassed++;
1675 } else { 1718 } else {
1676 testsFailed++; 1719 testsFailed++;
1677 } 1720 }
1721 // Any other result categories we care to report.
1722 if (!gmmain.fExpectationsSource.get() ||
1723 (kEmptyErrorBitfield != (kMissingExpectations_ErrorBitmask & composi teErrors))) {
1724 testsMissingReferenceImages++;
1725 }
1678 1726
1679 SkDELETE(gm); 1727 SkDELETE(gm);
1680 } 1728 }
1681 gm_fprintf(stdout, "Ran %d tests: %d passed, %d failed, %d missing reference images\n", 1729 gm_fprintf(stdout, "Ran %d tests: %d passed, %d failed, %d with drawing mode discrepancies, "
1682 testsRun, testsPassed, testsFailed, testsMissingReferenceImages); 1730 "%d missing reference images\n",
1731 testsRun, testsPassed, testsFailed, testsWithDrawingModeDiscrepan cies,
1732 testsMissingReferenceImages);
1683 gmmain.ListErrors(); 1733 gmmain.ListErrors();
1684 1734
1685 if (NULL != writeJsonSummaryPath) { 1735 if (NULL != writeJsonSummaryPath) {
1686 Json::Value actualResults; 1736 Json::Value actualResults;
1687 actualResults[kJsonKey_ActualResults_Failed] = 1737 actualResults[kJsonKey_ActualResults_Failed] =
1688 gmmain.fJsonActualResults_Failed; 1738 gmmain.fJsonActualResults_Failed;
1689 actualResults[kJsonKey_ActualResults_FailureIgnored] = 1739 actualResults[kJsonKey_ActualResults_FailureIgnored] =
1690 gmmain.fJsonActualResults_FailureIgnored; 1740 gmmain.fJsonActualResults_FailureIgnored;
1691 actualResults[kJsonKey_ActualResults_NoComparison] = 1741 actualResults[kJsonKey_ActualResults_NoComparison] =
1692 gmmain.fJsonActualResults_NoComparison; 1742 gmmain.fJsonActualResults_NoComparison;
(...skipping 27 matching lines...) Expand all
1720 SkGraphics::Term(); 1770 SkGraphics::Term();
1721 1771
1722 return (0 == testsFailed) ? 0 : -1; 1772 return (0 == testsFailed) ? 0 : -1;
1723 } 1773 }
1724 1774
1725 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) 1775 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
1726 int main(int argc, char * const argv[]) { 1776 int main(int argc, char * const argv[]) {
1727 return tool_main(argc, (char**) argv); 1777 return tool_main(argc, (char**) argv);
1728 } 1778 }
1729 #endif 1779 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698