MatrixClipCollapseTest.cpp revision 5e0995e4b36178e1e4465a9f50114ed39f038c27
1/* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "Test.h" 9#include "SkCanvas.h" 10#include "SkDebugCanvas.h" 11#include "SkPicture.h" 12#include "SkPictureFlat.h" 13#include "SkPictureRecord.h" 14 15// This test exercises the Matrix/Clip State collapsing system. It generates 16// example skps and the compares the actual stored operations to the expected 17// operations. The test works by emitting canvas operations at three levels: 18// overall structure, bodies that draw something and model/clip state changes. 19// 20// Structure methods only directly emit save and restores but call the 21// ModelClip and Body helper methods to fill in the structure. Since they only 22// emit saves and restores the operations emitted by the structure methods will 23// be completely removed by the matrix/clip collapse. Note: every save in 24// a structure method is followed by a call to a ModelClip helper. 25// 26// Body methods only directly emit draw ops and saveLayer/restore pairs but call 27// the ModelClip helper methods. Since the body methods emit the ops that cannot 28// be collapsed (i.e., draw ops, saveLayer/restore) they also generate the 29// expected result information. Note: every saveLayer in a body method is 30// followed by a call to a ModelClip helper. 31// 32// The ModelClip methods output matrix and clip ops in various orders and 33// combinations. They contribute to the expected result by outputting the 34// expected matrix & clip ops. Note that, currently, the entire clip stack 35// is output for each MC state so the clip operations accumulate down the 36// save/restore stack. 37 38// TODOs: 39// check on clip offsets 40// - not sure if this is possible. The desire is to verify that the clip 41// operations' offsets point to the correct follow-on operations. This 42// could be difficult since there is no good way to communicate the 43// offset stored in the SkPicture to the debugger's clip objects 44// add comparison of rendered before & after images? 45// - not sure if this would be useful since it somewhat duplicates the 46// correctness test of running render_pictures in record mode and 47// rendering before and after images. Additionally the matrix/clip collapse 48// is sure to cause some small differences so an automated test might 49// yield too many false positives. 50// run the matrix/clip collapse system on the 10K skp set 51// - this should give us warm fuzzies that the matrix clip collapse 52// system is ready for prime time 53// bench the recording times with/without matrix/clip collapsing 54 55#ifdef COLLAPSE_MATRIX_CLIP_STATE 56 57// Extract the command ops from the input SkPicture 58static void gets_ops(SkPicture& input, SkTDArray<DrawType>* ops) { 59 SkDebugCanvas debugCanvas(input.width(), input.height()); 60 debugCanvas.setBounds(input.width(), input.height()); 61 input.draw(&debugCanvas); 62 63 ops->setCount(debugCanvas.getSize()); 64 for (int i = 0; i < debugCanvas.getSize(); ++i) { 65 (*ops)[i] = debugCanvas.getDrawCommandAt(i)->getType(); 66 } 67} 68 69enum ClipType { 70 kNone_ClipType, 71 kRect_ClipType, 72 kRRect_ClipType, 73 kPath_ClipType, 74 kRegion_ClipType, 75 76 kLast_ClipType = kRRect_ClipType 77}; 78 79static const int kClipTypeCount = kLast_ClipType + 1; 80 81enum MatType { 82 kNone_MatType, 83 kTranslate_MatType, 84 kScale_MatType, 85 kSkew_MatType, 86 kRotate_MatType, 87 kConcat_MatType, 88 kSetMatrix_MatType, 89 90 kLast_MatType = kScale_MatType 91}; 92 93static const int kMatTypeCount = kLast_MatType + 1; 94 95// TODO: implement the rest of the draw ops 96enum DrawOpType { 97 kNone_DrawOpType, 98#if 0 99 kBitmap_DrawOpType, 100 kBitmapMatrix_DrawOpType, 101 kBitmapNone_DrawOpType, 102 kBitmapRectToRect_DrawOpType, 103#endif 104 kClear_DrawOpType, 105#if 0 106 kData_DrawOpType, 107#endif 108 kOval_DrawOpType, 109#if 0 110 kPaint_DrawOpType, 111 kPath_DrawOpType, 112 kPicture_DrawOpType, 113 kPoints_DrawOpType, 114 kPosText_DrawOpType, 115 kPosTextTopBottom_DrawOpType, 116 kPosTextH_DrawOpType, 117 kPosTextHTopBottom_DrawOpType, 118#endif 119 kRect_DrawOpType, 120 kRRect_DrawOpType, 121#if 0 122 kSprite_DrawOpType, 123 kText_DrawOpType, 124 kTextOnPath_DrawOpType, 125 kTextTopBottom_DrawOpType, 126 kDrawVertices_DrawOpType, 127#endif 128 129 kLast_DrawOpType = kRect_DrawOpType 130}; 131 132static const int kDrawOpTypeCount = kLast_DrawOpType + 1; 133 134typedef void (*PFEmitMC)(SkCanvas* canvas, MatType mat, ClipType clip, 135 DrawOpType draw, SkTDArray<DrawType>* expected, 136 int accumulatedClips); 137typedef void (*PFEmitBody)(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 138 ClipType clip, DrawOpType draw, 139 SkTDArray<DrawType>* expected, int accumulatedClips); 140typedef void (*PFEmitStruct)(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 141 ClipType clip, PFEmitBody emitBody, DrawOpType draw, 142 SkTDArray<DrawType>* expected); 143 144////////////////////////////////////////////////////////////////////////////// 145 146// TODO: expand the testing to include the different ops & AA types! 147static void emit_clip(SkCanvas* canvas, ClipType clip) { 148 switch (clip) { 149 case kNone_ClipType: 150 break; 151 case kRect_ClipType: { 152 SkRect r = SkRect::MakeLTRB(10, 10, 90, 90); 153 canvas->clipRect(r, SkRegion::kIntersect_Op, true); 154 break; 155 } 156 case kRRect_ClipType: { 157 SkRect r = SkRect::MakeLTRB(10, 10, 90, 90); 158 SkRRect rr; 159 rr.setRectXY(r, 10, 10); 160 canvas->clipRRect(rr, SkRegion::kIntersect_Op, true); 161 break; 162 } 163 case kPath_ClipType: { 164 SkPath p; 165 p.moveTo(5.0f, 5.0f); 166 p.lineTo(50.0f, 50.0f); 167 p.lineTo(100.0f, 5.0f); 168 p.close(); 169 canvas->clipPath(p, SkRegion::kIntersect_Op, true); 170 break; 171 } 172 case kRegion_ClipType: { 173 SkIRect rects[2] = { 174 { 1, 1, 55, 55 }, 175 { 45, 45, 99, 99 }, 176 }; 177 SkRegion r; 178 r.setRects(rects, 2); 179 canvas->clipRegion(r, SkRegion::kIntersect_Op); 180 break; 181 } 182 default: 183 SkASSERT(0); 184 } 185} 186 187static void add_clip(ClipType clip, MatType mat, SkTDArray<DrawType>* expected) { 188 if (NULL == expected) { 189 // expected is NULL if this clip will be fused into later clips 190 return; 191 } 192 193 switch (clip) { 194 case kNone_ClipType: 195 break; 196 case kRect_ClipType: 197 *expected->append() = CONCAT; 198 *expected->append() = CLIP_RECT; 199 break; 200 case kRRect_ClipType: 201 *expected->append() = CONCAT; 202 *expected->append() = CLIP_RRECT; 203 break; 204 case kPath_ClipType: 205 *expected->append() = CONCAT; 206 *expected->append() = CLIP_PATH; 207 break; 208 case kRegion_ClipType: 209 *expected->append() = CONCAT; 210 *expected->append() = CLIP_REGION; 211 break; 212 default: 213 SkASSERT(0); 214 } 215} 216 217static void emit_mat(SkCanvas* canvas, MatType mat) { 218 switch (mat) { 219 case kNone_MatType: 220 break; 221 case kTranslate_MatType: 222 canvas->translate(5.0f, 5.0f); 223 break; 224 case kScale_MatType: 225 canvas->scale(1.1f, 1.1f); 226 break; 227 case kSkew_MatType: 228 canvas->skew(1.1f, 1.1f); 229 break; 230 case kRotate_MatType: 231 canvas->rotate(1.0f); 232 break; 233 case kConcat_MatType: { 234 SkMatrix m; 235 m.setTranslate(1.0f, 1.0f); 236 canvas->concat(m); 237 break; 238 } 239 case kSetMatrix_MatType: { 240 SkMatrix m; 241 m.setTranslate(1.0f, 1.0f); 242 canvas->setMatrix(m); 243 break; 244 } 245 default: 246 SkASSERT(0); 247 } 248} 249 250static void add_mat(MatType mat, SkTDArray<DrawType>* expected) { 251 if (NULL == expected) { 252 // expected is NULL if this matrix call will be fused into later ones 253 return; 254 } 255 256 switch (mat) { 257 case kNone_MatType: 258 break; 259 case kTranslate_MatType: // fall thru 260 case kScale_MatType: // fall thru 261 case kSkew_MatType: // fall thru 262 case kRotate_MatType: // fall thru 263 case kConcat_MatType: // fall thru 264 case kSetMatrix_MatType: 265 // TODO: this system currently converts a setMatrix to concat. If we wanted to 266 // really preserve the setMatrix semantics we should keep it a setMatrix. I'm 267 // not sure if this is a good idea though since this would keep things like pinch 268 // zoom from working. 269 *expected->append() = CONCAT; 270 break; 271 default: 272 SkASSERT(0); 273 } 274} 275 276static void emit_draw(SkCanvas* canvas, DrawOpType draw, SkTDArray<DrawType>* expected) { 277 switch (draw) { 278 case kNone_DrawOpType: 279 break; 280 case kClear_DrawOpType: 281 canvas->clear(SK_ColorRED); 282 *expected->append() = DRAW_CLEAR; 283 break; 284 case kOval_DrawOpType: { 285 SkRect r = SkRect::MakeLTRB(10, 10, 90, 90); 286 SkPaint p; 287 canvas->drawOval(r, p); 288 *expected->append() = DRAW_OVAL; 289 break; 290 } 291 case kRect_DrawOpType: { 292 SkRect r = SkRect::MakeLTRB(10, 10, 90, 90); 293 SkPaint p; 294 canvas->drawRect(r, p); 295 *expected->append() = DRAW_RECT; 296 break; 297 } 298 case kRRect_DrawOpType: { 299 SkRect r = SkRect::MakeLTRB(10.0f, 10.0f, 90.0f, 90.0f); 300 SkRRect rr; 301 rr.setRectXY(r, 5.0f, 5.0f); 302 SkPaint p; 303 canvas->drawRRect(rr, p); 304 *expected->append() = DRAW_RRECT; 305 break; 306 } 307 default: 308 SkASSERT(0); 309 } 310} 311 312////////////////////////////////////////////////////////////////////////////// 313 314// Emit: 315// clip 316// matrix 317// Simple case - the clip isn't effect by the matrix 318static void emit_clip_and_mat(SkCanvas* canvas, MatType mat, ClipType clip, 319 DrawOpType draw, SkTDArray<DrawType>* expected, 320 int accumulatedClips) { 321 if (kNone_DrawOpType == draw) { 322 return; 323 } 324 325 emit_clip(canvas, clip); 326 emit_mat(canvas, mat); 327 328 for (int i = 0; i < accumulatedClips; ++i) { 329 add_clip(clip, mat, expected); 330 } 331 add_mat(mat, expected); 332} 333 334// Emit: 335// matrix 336// clip 337// Emitting the matrix first is more challenging since the matrix has to be 338// pushed across (i.e., applied to) the clip. 339static void emit_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip, 340 DrawOpType draw, SkTDArray<DrawType>* expected, 341 int accumulatedClips) { 342 if (kNone_DrawOpType == draw) { 343 return; 344 } 345 346 emit_mat(canvas, mat); 347 emit_clip(canvas, clip); 348 349 // the matrix & clip order will be reversed once collapsed! 350 for (int i = 0; i < accumulatedClips; ++i) { 351 add_clip(clip, mat, expected); 352 } 353 add_mat(mat, expected); 354} 355 356// Emit: 357// matrix 358// clip 359// matrix 360// clip 361// This tests that the matrices and clips coalesce when collapsed 362static void emit_double_mat_and_clip(SkCanvas* canvas, MatType mat, ClipType clip, 363 DrawOpType draw, SkTDArray<DrawType>* expected, 364 int accumulatedClips) { 365 if (kNone_DrawOpType == draw) { 366 return; 367 } 368 369 emit_mat(canvas, mat); 370 emit_clip(canvas, clip); 371 emit_mat(canvas, mat); 372 emit_clip(canvas, clip); 373 374 for (int i = 0; i < accumulatedClips; ++i) { 375 add_clip(clip, mat, expected); 376 add_clip(clip, mat, expected); 377 } 378 add_mat(mat, expected); 379} 380 381// Emit: 382// matrix 383// clip 384// clip 385// This tests accumulation of clips in same transform state. It also tests pushing 386// of the matrix across both the clips. 387static void emit_mat_clip_clip(SkCanvas* canvas, MatType mat, ClipType clip, 388 DrawOpType draw, SkTDArray<DrawType>* expected, 389 int accumulatedClips) { 390 if (kNone_DrawOpType == draw) { 391 return; 392 } 393 394 emit_mat(canvas, mat); 395 emit_clip(canvas, clip); 396 emit_clip(canvas, clip); 397 398 for (int i = 0; i < accumulatedClips; ++i) { 399 add_clip(clip, mat, expected); 400 add_clip(clip, mat, expected); 401 } 402 add_mat(mat, expected); 403} 404 405////////////////////////////////////////////////////////////////////////////// 406 407// Emit: 408// matrix & clip calls 409// draw op 410static void emit_body0(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 411 ClipType clip, DrawOpType draw, 412 SkTDArray<DrawType>* expected, int accumulatedClips) { 413 bool needsSaveRestore = kNone_DrawOpType != draw && 414 (kNone_MatType != mat || kNone_ClipType != clip); 415 416 if (needsSaveRestore) { 417 *expected->append() = SAVE; 418 } 419 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+1); 420 emit_draw(canvas, draw, expected); 421 if (needsSaveRestore) { 422 *expected->append() = RESTORE; 423 } 424} 425 426// Emit: 427// matrix & clip calls 428// draw op 429// matrix & clip calls 430// draw op 431static void emit_body1(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 432 ClipType clip, DrawOpType draw, 433 SkTDArray<DrawType>* expected, int accumulatedClips) { 434 bool needsSaveRestore = kNone_DrawOpType != draw && 435 (kNone_MatType != mat || kNone_ClipType != clip); 436 437 if (needsSaveRestore) { 438 *expected->append() = SAVE; 439 } 440 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+1); 441 emit_draw(canvas, draw, expected); 442 if (needsSaveRestore) { 443 *expected->append() = RESTORE; 444 *expected->append() = SAVE; 445 } 446 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+2); 447 emit_draw(canvas, draw, expected); 448 if (needsSaveRestore) { 449 *expected->append() = RESTORE; 450 } 451} 452 453// Emit: 454// matrix & clip calls 455// SaveLayer 456// matrix & clip calls 457// draw op 458// Restore 459static void emit_body2(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 460 ClipType clip, DrawOpType draw, 461 SkTDArray<DrawType>* expected, int accumulatedClips) { 462 bool needsSaveRestore = kNone_DrawOpType != draw && 463 (kNone_MatType != mat || kNone_ClipType != clip); 464 465 if (needsSaveRestore) { 466 *expected->append() = SAVE_LAYER; 467 } 468 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops 469 // TODO: widen testing to exercise saveLayer's parameters 470 canvas->saveLayer(NULL, NULL); 471 if (needsSaveRestore) { 472 *expected->append() = SAVE; 473 } 474 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+2); 475 emit_draw(canvas, draw, expected); 476 if (needsSaveRestore) { 477 *expected->append() = RESTORE; 478 } 479 canvas->restore(); 480 if (needsSaveRestore) { 481 *expected->append() = RESTORE; 482 } 483} 484 485// Emit: 486// matrix & clip calls 487// SaveLayer 488// matrix & clip calls 489// SaveLayer 490// matrix & clip calls 491// draw op 492// Restore 493// matrix & clip calls (will be ignored) 494// Restore 495static void emit_body3(SkCanvas* canvas, PFEmitMC emitMC, MatType mat, 496 ClipType clip, DrawOpType draw, 497 SkTDArray<DrawType>* expected, int accumulatedClips) { 498 bool needsSaveRestore = kNone_DrawOpType != draw && 499 (kNone_MatType != mat || kNone_ClipType != clip); 500 501 // This saveLayer will always be forced b.c. we currently can't tell 502 // ahead of time if it will be empty (see comment in SkMatrixClipStateMgr::save) 503 *expected->append() = SAVE_LAYER; 504 505 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops 506 // TODO: widen testing to exercise saveLayer's parameters 507 canvas->saveLayer(NULL, NULL); 508 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops 509 if (needsSaveRestore) { 510 *expected->append() = SAVE_LAYER; 511 } 512 // TODO: widen testing to exercise saveLayer's parameters 513 canvas->saveLayer(NULL, NULL); 514 if (needsSaveRestore) { 515 *expected->append() = SAVE; 516 } 517 (*emitMC)(canvas, mat, clip, draw, expected, accumulatedClips+3); 518 emit_draw(canvas, draw, expected); 519 if (needsSaveRestore) { 520 *expected->append() = RESTORE; 521 } 522 canvas->restore(); 523 if (needsSaveRestore) { 524 *expected->append() = RESTORE; 525 } 526 canvas->restore(); 527 528 // required to match forced SAVE_LAYER 529 *expected->append() = RESTORE; 530} 531 532////////////////////////////////////////////////////////////////////////////// 533 534// Emit: 535// Save 536// some body 537// Restore 538// Note: the outer save/restore are provided by beginRecording/endRecording 539static void emit_struct0(SkCanvas* canvas, 540 PFEmitMC emitMC, MatType mat, ClipType clip, 541 PFEmitBody emitBody, DrawOpType draw, 542 SkTDArray<DrawType>* expected) { 543 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 0); 544} 545 546// Emit: 547// Save 548// matrix & clip calls 549// Save 550// some body 551// Restore 552// matrix & clip calls (will be ignored) 553// Restore 554// Note: the outer save/restore are provided by beginRecording/endRecording 555static void emit_struct1(SkCanvas* canvas, 556 PFEmitMC emitMC, MatType mat, ClipType clip, 557 PFEmitBody emitBody, DrawOpType draw, 558 SkTDArray<DrawType>* expected) { 559 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these get fused into later ops 560 canvas->save(); 561 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); 562 canvas->restore(); 563 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get removed 564} 565 566// Emit: 567// Save 568// matrix & clip calls 569// Save 570// some body 571// Restore 572// Save 573// some body 574// Restore 575// matrix & clip calls (will be ignored) 576// Restore 577// Note: the outer save/restore are provided by beginRecording/endRecording 578static void emit_struct2(SkCanvas* canvas, 579 PFEmitMC emitMC, MatType mat, ClipType clip, 580 PFEmitBody emitBody, DrawOpType draw, 581 SkTDArray<DrawType>* expected) { 582 (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get fused into later ops 583 canvas->save(); 584 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); 585 canvas->restore(); 586 canvas->save(); 587 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); 588 canvas->restore(); 589 (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get removed 590} 591 592// Emit: 593// Save 594// matrix & clip calls 595// Save 596// some body 597// Restore 598// Save 599// matrix & clip calls 600// Save 601// some body 602// Restore 603// Restore 604// matrix & clip calls (will be ignored) 605// Restore 606// Note: the outer save/restore are provided by beginRecording/endRecording 607static void emit_struct3(SkCanvas* canvas, 608 PFEmitMC emitMC, MatType mat, ClipType clip, 609 PFEmitBody emitBody, DrawOpType draw, 610 SkTDArray<DrawType>* expected) { 611 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get fused into later ops 612 canvas->save(); 613 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 1); 614 canvas->restore(); 615 canvas->save(); 616 (*emitMC)(canvas, mat, clip, draw, NULL, 1); // these will get fused into later ops 617 canvas->save(); 618 (*emitBody)(canvas, emitMC, mat, clip, draw, expected, 2); 619 canvas->restore(); 620 canvas->restore(); 621 (*emitMC)(canvas, mat, clip, draw, NULL, 0); // these will get removed 622} 623 624////////////////////////////////////////////////////////////////////////////// 625 626#ifdef COLLAPSE_MATRIX_CLIP_STATE 627static void print(const SkTDArray<DrawType>& expected, const SkTDArray<DrawType>& actual) { 628 SkDebugf("\n\nexpected %d --- actual %d\n", expected.count(), actual.count()); 629 int max = SkMax32(expected.count(), actual.count()); 630 631 for (int i = 0; i < max; ++i) { 632 if (i < expected.count()) { 633 SkDebugf("%16s, ", SkDrawCommand::GetCommandString(expected[i])); 634 } else { 635 SkDebugf("%16s, ", " "); 636 } 637 638 if (i < actual.count()) { 639 SkDebugf("%s\n", SkDrawCommand::GetCommandString(actual[i])); 640 } else { 641 SkDebugf("\n"); 642 } 643 } 644 SkDebugf("\n\n"); 645 SkASSERT(0); 646} 647#endif 648 649static void test_collapse(skiatest::Reporter* reporter) { 650 PFEmitStruct gStructure[] = { emit_struct0, emit_struct1, emit_struct2, emit_struct3 }; 651 PFEmitBody gBody[] = { emit_body0, emit_body1, emit_body2, emit_body3 }; 652 PFEmitMC gMCs[] = { emit_clip_and_mat, emit_mat_and_clip, 653 emit_double_mat_and_clip, emit_mat_clip_clip }; 654 655 for (size_t i = 0; i < SK_ARRAY_COUNT(gStructure); ++i) { 656 for (size_t j = 0; j < SK_ARRAY_COUNT(gBody); ++j) { 657 for (size_t k = 0; k < SK_ARRAY_COUNT(gMCs); ++k) { 658 for (int l = 0; l < kMatTypeCount; ++l) { 659 for (int m = 0; m < kClipTypeCount; ++m) { 660 for (int n = 0; n < kDrawOpTypeCount; ++n) { 661#ifdef COLLAPSE_MATRIX_CLIP_STATE 662 static int testID = -1; 663 ++testID; 664 if (testID < -1) { 665 continue; 666 } 667#endif 668 669 SkTDArray<DrawType> expected, actual; 670 671 SkPicture picture; 672 673 // Note: beginRecording/endRecording add a save/restore pair 674 SkCanvas* canvas = picture.beginRecording(100, 100); 675 (*gStructure[i])(canvas, 676 gMCs[k], 677 (MatType) l, 678 (ClipType) m, 679 gBody[j], 680 (DrawOpType) n, 681 &expected); 682 picture.endRecording(); 683 684 gets_ops(picture, &actual); 685 686 REPORTER_ASSERT(reporter, expected.count() == actual.count()); 687 688 if (expected.count() != actual.count()) { 689#ifdef COLLAPSE_MATRIX_CLIP_STATE 690 print(expected, actual); 691#endif 692 continue; 693 } 694 695 for (int i = 0; i < expected.count(); ++i) { 696 REPORTER_ASSERT(reporter, expected[i] == actual[i]); 697#ifdef COLLAPSE_MATRIX_CLIP_STATE 698 if (expected[i] != actual[i]) { 699 print(expected, actual); 700 } 701#endif 702 break; 703 } 704 } 705 } 706 } 707 } 708 } 709 } 710} 711 712DEF_TEST(MatrixClipCollapse, reporter) { 713 test_collapse(reporter); 714} 715 716#endif 717