1 /* 2 * Copyright 2012 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 "SkDebuggerGUI.h" 9 #include "SkForceLinking.h" 10 #include "SkGraphics.h" 11 #include "SkImageDecoder.h" 12 #include <QListWidgetItem> 13 #include "PictureRenderer.h" 14 #include "SkPicturePlayback.h" 15 #include "SkPictureRecord.h" 16 #include "SkPictureData.h" 17 18 __SK_FORCE_IMAGE_DECODER_LINKING; 19 20 #if defined(SK_BUILD_FOR_WIN32) 21 #include "SysTimer_windows.h" 22 #elif defined(SK_BUILD_FOR_MAC) 23 #include "SysTimer_mach.h" 24 #elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) 25 #include "SysTimer_posix.h" 26 #else 27 #include "SysTimer_c.h" 28 #endif 29 30 31 SkDebuggerGUI::SkDebuggerGUI(QWidget *parent) : 32 QMainWindow(parent) 33 , fCentralSplitter(this) 34 , fStatusBar(this) 35 , fToolBar(this) 36 , fActionOpen(this) 37 , fActionBreakpoint(this) 38 , fActionToggleIndexStyle(this) 39 , fActionProfile(this) 40 , fActionCancel(this) 41 , fActionClearBreakpoints(this) 42 , fActionClearDeletes(this) 43 , fActionClose(this) 44 , fActionCreateBreakpoint(this) 45 , fActionDelete(this) 46 , fActionDirectory(this) 47 , fActionGoToLine(this) 48 , fActionInspector(this) 49 , fActionSettings(this) 50 , fActionPlay(this) 51 , fActionPause(this) 52 , fActionRewind(this) 53 , fActionSave(this) 54 , fActionSaveAs(this) 55 , fActionShowDeletes(this) 56 , fActionStepBack(this) 57 , fActionStepForward(this) 58 , fActionZoomIn(this) 59 , fActionZoomOut(this) 60 , fMapper(this) 61 , fListWidget(&fCentralSplitter) 62 , fDirectoryWidget(&fCentralSplitter) 63 , fCanvasWidget(this, &fDebugger) 64 , fImageWidget(&fDebugger) 65 , fMenuBar(this) 66 , fMenuFile(this) 67 , fMenuNavigate(this) 68 , fMenuView(this) 69 , fBreakpointsActivated(false) 70 , fIndexStyleToggle(false) 71 , fDeletesActivated(false) 72 , fPause(false) 73 , fLoading(false) 74 { 75 setupUi(this); 76 fListWidget.setSelectionMode(QAbstractItemView::ExtendedSelection); 77 connect(&fListWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(registerListClick(QListWidgetItem *))); 78 connect(&fActionOpen, SIGNAL(triggered()), this, SLOT(openFile())); 79 connect(&fActionDirectory, SIGNAL(triggered()), this, SLOT(toggleDirectory())); 80 connect(&fDirectoryWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(loadFile(QListWidgetItem *))); 81 connect(&fActionDelete, SIGNAL(triggered()), this, SLOT(actionDelete())); 82 connect(&fListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(toggleBreakpoint())); 83 connect(&fActionRewind, SIGNAL(triggered()), this, SLOT(actionRewind())); 84 connect(&fActionPlay, SIGNAL(triggered()), this, SLOT(actionPlay())); 85 connect(&fActionStepBack, SIGNAL(triggered()), this, SLOT(actionStepBack())); 86 connect(&fActionStepForward, SIGNAL(triggered()), this, SLOT(actionStepForward())); 87 connect(&fActionBreakpoint, SIGNAL(triggered()), this, SLOT(actionBreakpoints())); 88 connect(&fActionToggleIndexStyle, SIGNAL(triggered()), this, SLOT(actionToggleIndexStyle())); 89 connect(&fActionInspector, SIGNAL(triggered()), this, SLOT(actionInspector())); 90 connect(&fActionSettings, SIGNAL(triggered()), this, SLOT(actionSettings())); 91 connect(&fFilter, SIGNAL(activated(QString)), this, SLOT(toggleFilter(QString))); 92 connect(&fActionProfile, SIGNAL(triggered()), this, SLOT(actionProfile())); 93 connect(&fActionCancel, SIGNAL(triggered()), this, SLOT(actionCancel())); 94 connect(&fActionClearBreakpoints, SIGNAL(triggered()), this, SLOT(actionClearBreakpoints())); 95 connect(&fActionClearDeletes, SIGNAL(triggered()), this, SLOT(actionClearDeletes())); 96 connect(&fActionClose, SIGNAL(triggered()), this, SLOT(actionClose())); 97 connect(&fSettingsWidget, SIGNAL(visibilityFilterChanged()), this, SLOT(actionCommandFilter())); 98 #if SK_SUPPORT_GPU 99 connect(&fSettingsWidget, SIGNAL(glSettingsChanged()), this, SLOT(actionGLWidget())); 100 #endif 101 connect(&fSettingsWidget, SIGNAL(texFilterSettingsChanged()), this, SLOT(actionTextureFilter())); 102 connect(fSettingsWidget.getRasterCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionRasterWidget(bool))); 103 connect(fSettingsWidget.getOverdrawVizCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionOverdrawVizWidget(bool))); 104 connect(fSettingsWidget.getMegaVizCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionMegaVizWidget(bool))); 105 connect(fSettingsWidget.getPathOpsCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionPathOpsWidget(bool))); 106 connect(&fActionPause, SIGNAL(toggled(bool)), this, SLOT(pauseDrawing(bool))); 107 connect(&fActionCreateBreakpoint, SIGNAL(activated()), this, SLOT(toggleBreakpoint())); 108 connect(&fActionShowDeletes, SIGNAL(triggered()), this, SLOT(showDeletes())); 109 connect(&fCanvasWidget, SIGNAL(hitChanged(int)), this, SLOT(selectCommand(int))); 110 connect(&fCanvasWidget, SIGNAL(hitChanged(int)), &fSettingsWidget, SLOT(updateHit(int))); 111 connect(&fCanvasWidget, SIGNAL(scaleFactorChanged(float)), this, SLOT(actionScale(float))); 112 connect(&fCanvasWidget, SIGNAL(commandChanged(int)), &fSettingsWidget, SLOT(updateCommand(int))); 113 connect(&fActionSaveAs, SIGNAL(triggered()), this, SLOT(actionSaveAs())); 114 connect(&fActionSave, SIGNAL(triggered()), this, SLOT(actionSave())); 115 116 fMapper.setMapping(&fActionZoomIn, SkCanvasWidget::kIn_ZoomCommand); 117 fMapper.setMapping(&fActionZoomOut, SkCanvasWidget::kOut_ZoomCommand); 118 119 connect(&fActionZoomIn, SIGNAL(triggered()), &fMapper, SLOT(map())); 120 connect(&fActionZoomOut, SIGNAL(triggered()), &fMapper, SLOT(map())); 121 connect(&fMapper, SIGNAL(mapped(int)), &fCanvasWidget, SLOT(zoom(int))); 122 123 fInspectorWidget.setDisabled(true); 124 fMenuEdit.setDisabled(true); 125 fMenuNavigate.setDisabled(true); 126 fMenuView.setDisabled(true); 127 128 SkGraphics::Init(); 129 } 130 131 SkDebuggerGUI::~SkDebuggerGUI() { 132 SkGraphics::Term(); 133 } 134 135 void SkDebuggerGUI::actionBreakpoints() { 136 fBreakpointsActivated = !fBreakpointsActivated; 137 for (int row = 0; row < fListWidget.count(); row++) { 138 QListWidgetItem *item = fListWidget.item(row); 139 item->setHidden(item->checkState() == Qt::Unchecked && fBreakpointsActivated); 140 } 141 } 142 143 void SkDebuggerGUI::actionToggleIndexStyle() { 144 fIndexStyleToggle = !fIndexStyleToggle; 145 SkListWidget* list = (SkListWidget*) fListWidget.itemDelegate(); 146 list->setIndexStyle(fIndexStyleToggle ? SkListWidget::kIndex_IndexStyle : 147 SkListWidget::kOffset_IndexStyle); 148 fListWidget.update(); 149 } 150 151 void SkDebuggerGUI::showDeletes() { 152 fDeletesActivated = !fDeletesActivated; 153 for (int row = 0; row < fListWidget.count(); row++) { 154 QListWidgetItem *item = fListWidget.item(row); 155 item->setHidden(fDebugger.isCommandVisible(row) && fDeletesActivated); 156 } 157 } 158 159 // The timed picture playback just steps through every operation timing 160 // each one individually. Note that each picture should be replayed multiple 161 // times (via calls to 'draw') before each command's time is accessed via 'time'. 162 class SkTimedPicturePlayback : public SkPicturePlayback { 163 public: 164 165 SkTimedPicturePlayback(const SkPicture* picture, const SkTDArray<bool>& deletedCommands) 166 : INHERITED(picture) 167 , fSkipCommands(deletedCommands) 168 , fTot(0.0) 169 , fCurCommand(0) { 170 fTimes.setCount(deletedCommands.count()); 171 fTypeTimes.setCount(LAST_DRAWTYPE_ENUM+1); 172 this->resetTimes(); 173 } 174 175 virtual void draw(SkCanvas* canvas, SkDrawPictureCallback* callback) SK_OVERRIDE { 176 AutoResetOpID aroi(this); 177 SkASSERT(0 == fCurOffset); 178 179 SkReader32 reader(fPictureData->opData()->bytes(), fPictureData->opData()->size()); 180 181 // Record this, so we can concat w/ it if we encounter a setMatrix() 182 SkMatrix initialMatrix = canvas->getTotalMatrix(); 183 184 SkAutoCanvasRestore acr(canvas, false); 185 186 int opIndex = -1; 187 188 while (!reader.eof()) { 189 if (callback && callback->abortDrawing()) { 190 return; 191 } 192 193 fCurOffset = reader.offset(); 194 uint32_t size; 195 DrawType op = ReadOpAndSize(&reader, &size); 196 if (NOOP == op) { 197 // NOOPs are to be ignored - do not propagate them any further 198 reader.setOffset(fCurOffset + size); 199 continue; 200 } 201 202 opIndex++; 203 204 if (this->preDraw(opIndex, op)) { 205 // This operation is disabled in the debugger's GUI 206 reader.setOffset(fCurOffset + size); 207 continue; 208 } 209 210 this->handleOp(&reader, op, size, canvas, initialMatrix); 211 212 this->postDraw(opIndex); 213 } 214 } 215 216 void resetTimes() { 217 for (int i = 0; i < fTimes.count(); ++i) { 218 fTimes[i] = 0.0; 219 } 220 for (int i = 0; i < fTypeTimes.count(); ++i) { 221 fTypeTimes[i] = 0.0f; 222 } 223 fTot = 0.0; 224 } 225 226 int count() const { return fTimes.count(); } 227 228 // Return the fraction of the total time consumed by the index-th operation 229 double time(int index) const { return fTimes[index] / fTot; } 230 231 const SkTDArray<double>* typeTimes() const { return &fTypeTimes; } 232 233 double totTime() const { return fTot; } 234 235 protected: 236 SysTimer fTimer; 237 SkTDArray<bool> fSkipCommands; // has the command been deleted in the GUI? 238 SkTDArray<double> fTimes; // sum of time consumed for each command 239 SkTDArray<double> fTypeTimes; // sum of time consumed for each type of command (e.g., drawPath) 240 double fTot; // total of all times in 'fTimes' 241 242 int fCurType; 243 int fCurCommand; // the current command being executed/timed 244 245 bool preDraw(int opIndex, int type) { 246 fCurCommand = opIndex; 247 248 if (fSkipCommands[fCurCommand]) { 249 return true; 250 } 251 252 fCurType = type; 253 // The SkDebugCanvas doesn't recognize these types. This class needs to 254 // convert or else we'll wind up with a mismatch between the type counts 255 // the debugger displays and the profile times. 256 if (DRAW_POS_TEXT_TOP_BOTTOM == type) { 257 fCurType = DRAW_POS_TEXT; 258 } else if (DRAW_POS_TEXT_H_TOP_BOTTOM == type) { 259 fCurType = DRAW_POS_TEXT_H; 260 } 261 262 #if defined(SK_BUILD_FOR_WIN32) 263 // CPU timer doesn't work well on Windows 264 fTimer.startWall(); 265 #else 266 fTimer.startCpu(); 267 #endif 268 269 return false; 270 } 271 272 void postDraw(int opIndex) { 273 #if defined(SK_BUILD_FOR_WIN32) 274 // CPU timer doesn't work well on Windows 275 double time = fTimer.endWall(); 276 #else 277 double time = fTimer.endCpu(); 278 #endif 279 280 SkASSERT(opIndex == fCurCommand); 281 SkASSERT(fCurType <= LAST_DRAWTYPE_ENUM); 282 283 fTimes[fCurCommand] += time; 284 fTypeTimes[fCurType] += time; 285 fTot += time; 286 } 287 288 private: 289 typedef SkPicturePlayback INHERITED; 290 }; 291 292 #if 0 293 // Wrap SkPicture to allow installation of an SkTimedPicturePlayback object 294 class SkTimedPicture : public SkPicture { 295 public: 296 static SkTimedPicture* CreateTimedPicture(SkStream* stream, 297 SkPicture::InstallPixelRefProc proc, 298 const SkTDArray<bool>& deletedCommands) { 299 SkPictInfo info; 300 if (!InternalOnly_StreamIsSKP(stream, &info)) { 301 return NULL; 302 } 303 304 // Check to see if there is a playback to recreate. 305 if (stream->readBool()) { 306 SkTimedPicturePlayback* playback = SkTimedPicturePlayback::CreateFromStream( 307 stream, 308 info, proc, 309 deletedCommands); 310 if (NULL == playback) { 311 return NULL; 312 } 313 314 return SkNEW_ARGS(SkTimedPicture, (playback, info.fWidth, info.fHeight)); 315 } 316 317 return NULL; 318 } 319 320 void resetTimes() { ((SkTimedPicturePlayback*) fData.get())->resetTimes(); } 321 322 int count() const { return ((SkTimedPicturePlayback*) fData.get())->count(); } 323 324 // return the fraction of the total time this command consumed 325 double time(int index) const { return ((SkTimedPicturePlayback*) fData.get())->time(index); } 326 327 const SkTDArray<double>* typeTimes() const { return ((SkTimedPicturePlayback*) fData.get())->typeTimes(); } 328 329 double totTime() const { return ((SkTimedPicturePlayback*) fData.get())->totTime(); } 330 331 private: 332 // disallow default ctor b.c. we don't have a good way to setup the fData ptr 333 SkTimedPicture(); 334 // Private ctor only used by CreateTimedPicture, which has created the playback. 335 SkTimedPicture(SkTimedPicturePlayback* playback, int width, int height) 336 : INHERITED(playback, width, height) {} 337 // disallow the copy ctor - enabling would require copying code from SkPicture 338 SkTimedPicture(const SkTimedPicture& src); 339 340 typedef SkPicture INHERITED; 341 }; 342 #endif 343 344 // This is a simplification of PictureBenchmark's run with the addition of 345 // clearing of the times after the first pass (in resetTimes) 346 void SkDebuggerGUI::run(const SkPicture* pict, 347 sk_tools::PictureRenderer* renderer, 348 int repeats) { 349 SkASSERT(pict); 350 if (NULL == pict) { 351 return; 352 } 353 354 SkASSERT(renderer != NULL); 355 if (NULL == renderer) { 356 return; 357 } 358 359 renderer->init(pict, NULL, NULL, NULL, false); 360 361 renderer->setup(); 362 renderer->render(); 363 renderer->resetState(true); // flush, swapBuffers and Finish 364 365 #if 0 366 // We throw this away the first batch of times to remove first time effects (such as paging in this program) 367 pict->resetTimes(); 368 #endif 369 370 for (int i = 0; i < repeats; ++i) { 371 renderer->setup(); 372 renderer->render(); 373 renderer->resetState(false); // flush & swapBuffers, but don't Finish 374 } 375 renderer->resetState(true); // flush, swapBuffers and Finish 376 377 renderer->end(); 378 } 379 380 void SkDebuggerGUI::actionProfile() { 381 // In order to profile we pass the command offsets (that were read-in 382 // in loadPicture by the SkOffsetPicture) to an SkTimedPlaybackPicture. 383 // The SkTimedPlaybackPicture in turn passes the offsets to an 384 // SkTimedPicturePlayback object which uses them to track the performance 385 // of individual commands. 386 if (fFileName.isEmpty()) { 387 return; 388 } 389 390 SkFILEStream inputStream; 391 392 inputStream.setPath(fFileName.c_str()); 393 if (!inputStream.isValid()) { 394 return; 395 } 396 397 SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&inputStream, 398 &SkImageDecoder::DecodeMemory)); // , fSkipCommands)); 399 if (NULL == picture.get()) { 400 return; 401 } 402 403 404 #if 0 405 406 // For now this #if allows switching between tiled and simple rendering 407 // modes. Eventually this will be accomplished via the GUI 408 #if 0 409 // With the current batch of SysTimers, profiling in tiled mode 410 // gets swamped by the timing overhead: 411 // 412 // tile mode simple mode 413 // debugger 64.2ms 12.8ms 414 // bench_pictures 16.9ms 12.4ms 415 // 416 // This is b.c. in tiled mode each command is called many more times 417 // but typically does less work on each invocation (due to clipping) 418 sk_tools::TiledPictureRenderer* renderer = NULL; 419 420 renderer = SkNEW(sk_tools::TiledPictureRenderer); 421 renderer->setTileWidth(256); 422 renderer->setTileHeight(256); 423 #else 424 sk_tools::SimplePictureRenderer* renderer = NULL; 425 426 renderer = SkNEW(sk_tools::SimplePictureRenderer); 427 428 #if SK_SUPPORT_GPU 429 if (fSettingsWidget.isGLActive()) { 430 renderer->setDeviceType(sk_tools::PictureRenderer::kGPU_DeviceType); 431 renderer->setSampleCount(fSettingsWidget.getGLSampleCount()); 432 } 433 #endif 434 435 #endif 436 437 static const int kNumRepeats = 10; 438 439 run(picture.get(), renderer, kNumRepeats); 440 441 SkASSERT(picture->count() == fListWidget.count()); 442 443 // extract the individual command times from the SkTimedPlaybackPicture 444 for (int i = 0; i < picture->count(); ++i) { 445 double temp = picture->time(i); 446 447 QListWidgetItem* item = fListWidget.item(i); 448 449 item->setData(Qt::UserRole + 4, 100.0*temp); 450 } 451 452 setupOverviewText(picture->typeTimes(), picture->totTime(), kNumRepeats); 453 setupClipStackText(); 454 455 #endif 456 } 457 458 void SkDebuggerGUI::actionCancel() { 459 for (int row = 0; row < fListWidget.count(); row++) { 460 fListWidget.item(row)->setHidden(false); 461 } 462 } 463 464 void SkDebuggerGUI::actionClearBreakpoints() { 465 for (int row = 0; row < fListWidget.count(); row++) { 466 QListWidgetItem* item = fListWidget.item(row); 467 item->setCheckState(Qt::Unchecked); 468 item->setData(Qt::DecorationRole, 469 QPixmap(":/blank.png")); 470 } 471 } 472 473 void SkDebuggerGUI::actionClearDeletes() { 474 for (int row = 0; row < fListWidget.count(); row++) { 475 QListWidgetItem* item = fListWidget.item(row); 476 item->setData(Qt::UserRole + 2, QPixmap(":/blank.png")); 477 fDebugger.setCommandVisible(row, true); 478 fSkipCommands[row] = false; 479 } 480 if (fPause) { 481 fCanvasWidget.drawTo(fPausedRow); 482 fImageWidget.draw(); 483 } else { 484 fCanvasWidget.drawTo(fListWidget.currentRow()); 485 fImageWidget.draw(); 486 } 487 } 488 489 void SkDebuggerGUI::actionCommandFilter() { 490 fDebugger.highlightCurrentCommand(fSettingsWidget.getVisibilityFilter()); 491 fCanvasWidget.drawTo(fListWidget.currentRow()); 492 fImageWidget.draw(); 493 } 494 495 void SkDebuggerGUI::actionClose() { 496 this->close(); 497 } 498 499 void SkDebuggerGUI::actionDelete() { 500 501 for (int row = 0; row < fListWidget.count(); ++row) { 502 QListWidgetItem* item = fListWidget.item(row); 503 504 if (!item->isSelected()) { 505 continue; 506 } 507 508 if (fDebugger.isCommandVisible(row)) { 509 item->setData(Qt::UserRole + 2, QPixmap(":/delete.png")); 510 fDebugger.setCommandVisible(row, false); 511 fSkipCommands[row] = true; 512 } else { 513 item->setData(Qt::UserRole + 2, QPixmap(":/blank.png")); 514 fDebugger.setCommandVisible(row, true); 515 fSkipCommands[row] = false; 516 } 517 } 518 519 int currentRow = fListWidget.currentRow(); 520 521 if (fPause) { 522 fCanvasWidget.drawTo(fPausedRow); 523 fImageWidget.draw(); 524 } else { 525 fCanvasWidget.drawTo(currentRow); 526 fImageWidget.draw(); 527 } 528 } 529 530 #if SK_SUPPORT_GPU 531 void SkDebuggerGUI::actionGLWidget() { 532 bool isToggled = fSettingsWidget.isGLActive(); 533 if (isToggled) { 534 fCanvasWidget.setGLSampleCount(fSettingsWidget.getGLSampleCount()); 535 } 536 fCanvasWidget.setWidgetVisibility(SkCanvasWidget::kGPU_WidgetType, !isToggled); 537 } 538 #endif 539 540 void SkDebuggerGUI::actionInspector() { 541 if (fInspectorWidget.isHidden()) { 542 fInspectorWidget.setHidden(false); 543 fImageWidget.setHidden(false); 544 } else { 545 fInspectorWidget.setHidden(true); 546 fImageWidget.setHidden(true); 547 } 548 } 549 550 void SkDebuggerGUI::actionPlay() { 551 for (int row = fListWidget.currentRow() + 1; row < fListWidget.count(); 552 row++) { 553 QListWidgetItem *item = fListWidget.item(row); 554 if (item->checkState() == Qt::Checked) { 555 fListWidget.setCurrentItem(item); 556 return; 557 } 558 } 559 fListWidget.setCurrentRow(fListWidget.count() - 1); 560 } 561 562 void SkDebuggerGUI::actionRasterWidget(bool isToggled) { 563 fCanvasWidget.setWidgetVisibility(SkCanvasWidget::kRaster_8888_WidgetType, !isToggled); 564 } 565 566 void SkDebuggerGUI::actionOverdrawVizWidget(bool isToggled) { 567 fDebugger.setOverdrawViz(isToggled); 568 fCanvasWidget.update(); 569 } 570 571 void SkDebuggerGUI::actionMegaVizWidget(bool isToggled) { 572 fDebugger.setMegaViz(isToggled); 573 fCanvasWidget.update(); 574 } 575 576 void SkDebuggerGUI::actionPathOpsWidget(bool isToggled) { 577 fDebugger.setPathOps(isToggled); 578 fCanvasWidget.update(); 579 } 580 581 void SkDebuggerGUI::actionTextureFilter() { 582 SkPaint::FilterLevel level; 583 bool enabled = fSettingsWidget.getFilterOverride(&level); 584 fDebugger.setTexFilterOverride(enabled, level); 585 fCanvasWidget.update(); 586 } 587 588 void SkDebuggerGUI::actionRewind() { 589 fListWidget.setCurrentRow(0); 590 } 591 592 void SkDebuggerGUI::actionSave() { 593 fFileName = fPath.toAscii().data(); 594 fFileName.append("/"); 595 fFileName.append(fDirectoryWidget.currentItem()->text().toAscii().data()); 596 saveToFile(fFileName); 597 } 598 599 void SkDebuggerGUI::actionSaveAs() { 600 QString filename = QFileDialog::getSaveFileName(this, "Save File", "", 601 "Skia Picture (*skp)"); 602 if (!filename.endsWith(".skp", Qt::CaseInsensitive)) { 603 filename.append(".skp"); 604 } 605 saveToFile(SkString(filename.toAscii().data())); 606 } 607 608 void SkDebuggerGUI::actionScale(float scaleFactor) { 609 fSettingsWidget.setZoomText(scaleFactor); 610 } 611 612 void SkDebuggerGUI::actionSettings() { 613 if (fSettingsWidget.isHidden()) { 614 fSettingsWidget.setHidden(false); 615 } else { 616 fSettingsWidget.setHidden(true); 617 } 618 } 619 620 void SkDebuggerGUI::actionStepBack() { 621 int currentRow = fListWidget.currentRow(); 622 if (currentRow != 0) { 623 fListWidget.setCurrentRow(currentRow - 1); 624 } 625 } 626 627 void SkDebuggerGUI::actionStepForward() { 628 int currentRow = fListWidget.currentRow(); 629 QString curRow = QString::number(currentRow); 630 QString curCount = QString::number(fListWidget.count()); 631 if (currentRow < fListWidget.count() - 1) { 632 fListWidget.setCurrentRow(currentRow + 1); 633 } 634 } 635 636 void SkDebuggerGUI::drawComplete() { 637 fInspectorWidget.setMatrix(fDebugger.getCurrentMatrix()); 638 fInspectorWidget.setClip(fDebugger.getCurrentClip()); 639 } 640 641 void SkDebuggerGUI::saveToFile(const SkString& filename) { 642 SkFILEWStream file(filename.c_str()); 643 SkAutoTUnref<SkPicture> copy(fDebugger.copyPicture()); 644 645 copy->serialize(&file); 646 } 647 648 void SkDebuggerGUI::loadFile(QListWidgetItem *item) { 649 if (fDirectoryWidgetActive) { 650 fFileName = fPath.toAscii().data(); 651 // don't add a '/' to files in the local directory 652 if (fFileName.size() > 0) { 653 fFileName.append("/"); 654 } 655 fFileName.append(item->text().toAscii().data()); 656 loadPicture(fFileName); 657 } 658 } 659 660 void SkDebuggerGUI::openFile() { 661 QString temp = QFileDialog::getOpenFileName(this, tr("Open File"), "", 662 tr("Files (*.*)")); 663 openFile(temp); 664 } 665 666 void SkDebuggerGUI::openFile(const QString &filename) { 667 fDirectoryWidgetActive = false; 668 if (!filename.isEmpty()) { 669 QFileInfo pathInfo(filename); 670 loadPicture(SkString(filename.toAscii().data())); 671 setupDirectoryWidget(pathInfo.path()); 672 } 673 fDirectoryWidgetActive = true; 674 } 675 676 void SkDebuggerGUI::pauseDrawing(bool isPaused) { 677 fPause = isPaused; 678 fPausedRow = fListWidget.currentRow(); 679 fCanvasWidget.drawTo(fPausedRow); 680 fImageWidget.draw(); 681 } 682 683 void SkDebuggerGUI::registerListClick(QListWidgetItem *item) { 684 if(!fLoading) { 685 int currentRow = fListWidget.currentRow(); 686 687 if (currentRow != -1) { 688 if (!fPause) { 689 fCanvasWidget.drawTo(currentRow); 690 fImageWidget.draw(); 691 } 692 SkTDArray<SkString*> *currInfo = fDebugger.getCommandInfo( 693 currentRow); 694 695 /* TODO(chudy): Add command type before parameters. Rename v 696 * to something more informative. */ 697 if (currInfo) { 698 QString info; 699 info.append("<b>Parameters: </b><br/>"); 700 for (int i = 0; i < currInfo->count(); i++) { 701 702 info.append(QString((*currInfo)[i]->c_str())); 703 info.append("<br/>"); 704 } 705 fInspectorWidget.setText(info, SkInspectorWidget::kDetail_TabType); 706 fInspectorWidget.setDisabled(false); 707 } 708 setupClipStackText(); 709 } 710 711 } 712 } 713 714 void SkDebuggerGUI::selectCommand(int command) { 715 if (fPause) { 716 fListWidget.setCurrentRow(command); 717 } 718 } 719 720 void SkDebuggerGUI::toggleBreakpoint() { 721 QListWidgetItem* item = fListWidget.currentItem(); 722 if (item->checkState() == Qt::Unchecked) { 723 item->setCheckState(Qt::Checked); 724 item->setData(Qt::DecorationRole, 725 QPixmap(":/breakpoint_16x16.png")); 726 } else { 727 item->setCheckState(Qt::Unchecked); 728 item->setData(Qt::DecorationRole, 729 QPixmap(":/blank.png")); 730 } 731 } 732 733 void SkDebuggerGUI::toggleDirectory() { 734 fDirectoryWidget.setHidden(!fDirectoryWidget.isHidden()); 735 } 736 737 void SkDebuggerGUI::toggleFilter(QString string) { 738 for (int row = 0; row < fListWidget.count(); row++) { 739 QListWidgetItem *item = fListWidget.item(row); 740 item->setHidden(item->text() != string); 741 } 742 } 743 744 void SkDebuggerGUI::setupUi(QMainWindow *SkDebuggerGUI) { 745 QIcon windowIcon; 746 windowIcon.addFile(QString::fromUtf8(":/skia.png"), QSize(), 747 QIcon::Normal, QIcon::Off); 748 SkDebuggerGUI->setObjectName(QString::fromUtf8("SkDebuggerGUI")); 749 SkDebuggerGUI->resize(1200, 1000); 750 SkDebuggerGUI->setWindowIcon(windowIcon); 751 SkDebuggerGUI->setWindowTitle("Skia Debugger"); 752 753 fActionOpen.setShortcuts(QKeySequence::Open); 754 fActionOpen.setText("Open"); 755 756 QIcon breakpoint; 757 breakpoint.addFile(QString::fromUtf8(":/breakpoint.png"), 758 QSize(), QIcon::Normal, QIcon::Off); 759 fActionBreakpoint.setShortcut(QKeySequence(tr("Ctrl+B"))); 760 fActionBreakpoint.setIcon(breakpoint); 761 fActionBreakpoint.setText("Breakpoints"); 762 763 fActionToggleIndexStyle.setShortcut(QKeySequence(tr("Ctrl+T"))); 764 fActionToggleIndexStyle.setText("Toggle Index Style"); 765 766 QIcon cancel; 767 cancel.addFile(QString::fromUtf8(":/reload.png"), QSize(), 768 QIcon::Normal, QIcon::Off); 769 fActionCancel.setIcon(cancel); 770 fActionCancel.setText("Clear Filter"); 771 772 fActionClearBreakpoints.setShortcut(QKeySequence(tr("Alt+B"))); 773 fActionClearBreakpoints.setText("Clear Breakpoints"); 774 775 fActionClearDeletes.setShortcut(QKeySequence(tr("Alt+X"))); 776 fActionClearDeletes.setText("Clear Deletes"); 777 778 fActionClose.setShortcuts(QKeySequence::Quit); 779 fActionClose.setText("Exit"); 780 781 fActionCreateBreakpoint.setShortcut(QKeySequence(tr("B"))); 782 fActionCreateBreakpoint.setText("Set Breakpoint"); 783 784 fActionDelete.setShortcut(QKeySequence(tr("X"))); 785 fActionDelete.setText("Delete Command"); 786 787 fActionDirectory.setShortcut(QKeySequence(tr("Ctrl+D"))); 788 fActionDirectory.setText("Directory"); 789 790 QIcon profile; 791 profile.addFile(QString::fromUtf8(":/profile.png"), QSize(), 792 QIcon::Normal, QIcon::Off); 793 fActionProfile.setIcon(profile); 794 fActionProfile.setText("Profile"); 795 fActionProfile.setDisabled(true); 796 797 QIcon inspector; 798 inspector.addFile(QString::fromUtf8(":/inspector.png"), 799 QSize(), QIcon::Normal, QIcon::Off); 800 fActionInspector.setShortcut(QKeySequence(tr("Ctrl+I"))); 801 fActionInspector.setIcon(inspector); 802 fActionInspector.setText("Inspector"); 803 804 QIcon settings; 805 settings.addFile(QString::fromUtf8(":/inspector.png"), 806 QSize(), QIcon::Normal, QIcon::Off); 807 fActionSettings.setShortcut(QKeySequence(tr("Ctrl+G"))); 808 fActionSettings.setIcon(settings); 809 fActionSettings.setText("Settings"); 810 811 QIcon play; 812 play.addFile(QString::fromUtf8(":/play.png"), QSize(), 813 QIcon::Normal, QIcon::Off); 814 fActionPlay.setShortcut(QKeySequence(tr("Ctrl+P"))); 815 fActionPlay.setIcon(play); 816 fActionPlay.setText("Play"); 817 818 QIcon pause; 819 pause.addFile(QString::fromUtf8(":/pause.png"), QSize(), 820 QIcon::Normal, QIcon::Off); 821 fActionPause.setShortcut(QKeySequence(tr("Space"))); 822 fActionPause.setCheckable(true); 823 fActionPause.setIcon(pause); 824 fActionPause.setText("Pause"); 825 826 QIcon rewind; 827 rewind.addFile(QString::fromUtf8(":/rewind.png"), QSize(), 828 QIcon::Normal, QIcon::Off); 829 fActionRewind.setShortcut(QKeySequence(tr("Ctrl+R"))); 830 fActionRewind.setIcon(rewind); 831 fActionRewind.setText("Rewind"); 832 833 fActionSave.setShortcut(QKeySequence::Save); 834 fActionSave.setText("Save"); 835 fActionSave.setDisabled(true); 836 fActionSaveAs.setShortcut(QKeySequence::SaveAs); 837 fActionSaveAs.setText("Save As"); 838 fActionSaveAs.setDisabled(true); 839 840 fActionShowDeletes.setShortcut(QKeySequence(tr("Ctrl+X"))); 841 fActionShowDeletes.setText("Deleted Commands"); 842 843 QIcon stepBack; 844 stepBack.addFile(QString::fromUtf8(":/previous.png"), QSize(), 845 QIcon::Normal, QIcon::Off); 846 fActionStepBack.setShortcut(QKeySequence(tr("["))); 847 fActionStepBack.setIcon(stepBack); 848 fActionStepBack.setText("Step Back"); 849 850 QIcon stepForward; 851 stepForward.addFile(QString::fromUtf8(":/next.png"), 852 QSize(), QIcon::Normal, QIcon::Off); 853 fActionStepForward.setShortcut(QKeySequence(tr("]"))); 854 fActionStepForward.setIcon(stepForward); 855 fActionStepForward.setText("Step Forward"); 856 857 fActionZoomIn.setShortcut(QKeySequence(tr("Ctrl+="))); 858 fActionZoomIn.setText("Zoom In"); 859 fActionZoomOut.setShortcut(QKeySequence(tr("Ctrl+-"))); 860 fActionZoomOut.setText("Zoom Out"); 861 862 fListWidget.setItemDelegate(new SkListWidget(&fListWidget)); 863 fListWidget.setObjectName(QString::fromUtf8("listWidget")); 864 fListWidget.setMinimumWidth(250); 865 866 fFilter.addItem("--Filter By Available Commands--"); 867 868 fDirectoryWidget.setMinimumWidth(250); 869 fDirectoryWidget.setStyleSheet("QListWidget::Item {padding: 5px;}"); 870 871 fCanvasWidget.setSizePolicy(QSizePolicy::Expanding, 872 QSizePolicy::Expanding); 873 874 fImageWidget.setFixedSize(SkImageWidget::kImageWidgetWidth, 875 SkImageWidget::kImageWidgetHeight); 876 877 fInspectorWidget.setSizePolicy(QSizePolicy::Expanding, 878 QSizePolicy::Expanding); 879 fInspectorWidget.setMaximumHeight(300); 880 881 fSettingsAndImageLayout.setSpacing(6); 882 fSettingsAndImageLayout.addWidget(&fSettingsWidget); 883 fSettingsAndImageLayout.addWidget(&fImageWidget); 884 885 fSettingsWidget.setSizePolicy(QSizePolicy::Expanding, 886 QSizePolicy::Expanding); 887 fSettingsWidget.setMaximumWidth(250); 888 889 fLeftColumnSplitter.addWidget(&fListWidget); 890 fLeftColumnSplitter.addWidget(&fDirectoryWidget); 891 fLeftColumnSplitter.setOrientation(Qt::Vertical); 892 893 fCanvasSettingsAndImageLayout.setSpacing(6); 894 fCanvasSettingsAndImageLayout.addWidget(&fCanvasWidget); 895 fCanvasSettingsAndImageLayout.addLayout(&fSettingsAndImageLayout); 896 897 fMainAndRightColumnLayout.setSpacing(6); 898 fMainAndRightColumnLayout.addLayout(&fCanvasSettingsAndImageLayout); 899 fMainAndRightColumnLayout.addWidget(&fInspectorWidget); 900 fMainAndRightColumnWidget.setLayout(&fMainAndRightColumnLayout); 901 902 fCentralSplitter.addWidget(&fLeftColumnSplitter); 903 fCentralSplitter.addWidget(&fMainAndRightColumnWidget); 904 fCentralSplitter.setStretchFactor(0, 0); 905 fCentralSplitter.setStretchFactor(1, 1); 906 907 SkDebuggerGUI->setCentralWidget(&fCentralSplitter); 908 SkDebuggerGUI->setStatusBar(&fStatusBar); 909 910 fToolBar.setIconSize(QSize(32, 32)); 911 fToolBar.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); 912 SkDebuggerGUI->addToolBar(Qt::TopToolBarArea, &fToolBar); 913 914 fSpacer.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 915 916 fToolBar.addAction(&fActionRewind); 917 fToolBar.addAction(&fActionStepBack); 918 fToolBar.addAction(&fActionPause); 919 fToolBar.addAction(&fActionStepForward); 920 fToolBar.addAction(&fActionPlay); 921 fToolBar.addSeparator(); 922 fToolBar.addAction(&fActionInspector); 923 fToolBar.addAction(&fActionSettings); 924 fToolBar.addSeparator(); 925 fToolBar.addAction(&fActionProfile); 926 927 fToolBar.addSeparator(); 928 fToolBar.addWidget(&fSpacer); 929 fToolBar.addWidget(&fFilter); 930 fToolBar.addAction(&fActionCancel); 931 932 // TODO(chudy): Remove static call. 933 fDirectoryWidgetActive = false; 934 fFileName = ""; 935 setupDirectoryWidget(""); 936 fDirectoryWidgetActive = true; 937 938 // Menu Bar 939 fMenuFile.setTitle("File"); 940 fMenuFile.addAction(&fActionOpen); 941 fMenuFile.addAction(&fActionSave); 942 fMenuFile.addAction(&fActionSaveAs); 943 fMenuFile.addAction(&fActionClose); 944 945 fMenuEdit.setTitle("Edit"); 946 fMenuEdit.addAction(&fActionDelete); 947 fMenuEdit.addAction(&fActionClearDeletes); 948 fMenuEdit.addSeparator(); 949 fMenuEdit.addAction(&fActionCreateBreakpoint); 950 fMenuEdit.addAction(&fActionClearBreakpoints); 951 952 fMenuNavigate.setTitle("Navigate"); 953 fMenuNavigate.addAction(&fActionRewind); 954 fMenuNavigate.addAction(&fActionStepBack); 955 fMenuNavigate.addAction(&fActionStepForward); 956 fMenuNavigate.addAction(&fActionPlay); 957 fMenuNavigate.addAction(&fActionPause); 958 fMenuNavigate.addAction(&fActionGoToLine); 959 960 fMenuView.setTitle("View"); 961 fMenuView.addAction(&fActionBreakpoint); 962 fMenuView.addAction(&fActionShowDeletes); 963 fMenuView.addAction(&fActionToggleIndexStyle); 964 fMenuView.addAction(&fActionZoomIn); 965 fMenuView.addAction(&fActionZoomOut); 966 967 fMenuWindows.setTitle("Window"); 968 fMenuWindows.addAction(&fActionInspector); 969 fMenuWindows.addAction(&fActionSettings); 970 fMenuWindows.addAction(&fActionDirectory); 971 972 fActionGoToLine.setText("Go to Line..."); 973 fActionGoToLine.setDisabled(true); 974 fMenuBar.addAction(fMenuFile.menuAction()); 975 fMenuBar.addAction(fMenuEdit.menuAction()); 976 fMenuBar.addAction(fMenuView.menuAction()); 977 fMenuBar.addAction(fMenuNavigate.menuAction()); 978 fMenuBar.addAction(fMenuWindows.menuAction()); 979 980 fPause = false; 981 982 SkDebuggerGUI->setMenuBar(&fMenuBar); 983 QMetaObject::connectSlotsByName(SkDebuggerGUI); 984 } 985 986 void SkDebuggerGUI::setupDirectoryWidget(const QString& path) { 987 fPath = path; 988 QDir dir(path); 989 QRegExp r(".skp"); 990 fDirectoryWidget.clear(); 991 const QStringList files = dir.entryList(); 992 foreach (QString f, files) { 993 if (f.contains(r)) 994 fDirectoryWidget.addItem(f); 995 } 996 } 997 998 void SkDebuggerGUI::loadPicture(const SkString& fileName) { 999 fFileName = fileName; 1000 fLoading = true; 1001 SkStream* stream = SkNEW_ARGS(SkFILEStream, (fileName.c_str())); 1002 1003 SkPicture* picture = SkPicture::CreateFromStream(stream); 1004 1005 if (NULL == picture) { 1006 QMessageBox::critical(this, "Error loading file", "Couldn't read file, sorry."); 1007 SkSafeUnref(stream); 1008 return; 1009 } 1010 1011 fCanvasWidget.resetWidgetTransform(); 1012 fDebugger.loadPicture(picture); 1013 1014 fSkipCommands.setCount(fDebugger.getSize()); 1015 for (int i = 0; i < fSkipCommands.count(); ++i) { 1016 fSkipCommands[i] = false; 1017 } 1018 1019 SkSafeUnref(stream); 1020 SkSafeUnref(picture); 1021 1022 // Will this automatically clear out due to nature of refcnt? 1023 SkTArray<SkString>* commands = fDebugger.getDrawCommandsAsStrings(); 1024 SkTDArray<size_t>* offsets = fDebugger.getDrawCommandOffsets(); 1025 SkASSERT(commands->count() == offsets->count()); 1026 1027 fActionProfile.setDisabled(false); 1028 1029 /* fDebugCanvas is reinitialized every load picture. Need it to retain value 1030 * of the visibility filter. 1031 * TODO(chudy): This should be deprecated since fDebugger is not 1032 * recreated. 1033 * */ 1034 fDebugger.highlightCurrentCommand(fSettingsWidget.getVisibilityFilter()); 1035 1036 this->setupListWidget(commands, offsets); 1037 this->setupComboBox(commands); 1038 this->setupOverviewText(NULL, 0.0, 1); 1039 fInspectorWidget.setDisabled(false); 1040 fSettingsWidget.setDisabled(false); 1041 fMenuEdit.setDisabled(false); 1042 fMenuNavigate.setDisabled(false); 1043 fMenuView.setDisabled(false); 1044 fActionSave.setDisabled(false); 1045 fActionSaveAs.setDisabled(false); 1046 fLoading = false; 1047 actionPlay(); 1048 } 1049 1050 void SkDebuggerGUI::setupListWidget(SkTArray<SkString>* commands, SkTDArray<size_t>* offsets) { 1051 SkASSERT(commands->count() == offsets->count()); 1052 fListWidget.clear(); 1053 int counter = 0; 1054 int indent = 0; 1055 for (int i = 0; i < commands->count(); i++) { 1056 QListWidgetItem *item = new QListWidgetItem(); 1057 item->setData(Qt::DisplayRole, (*commands)[i].c_str()); 1058 item->setData(Qt::UserRole + 1, counter++); 1059 1060 if (0 == strcmp("Restore", (*commands)[i].c_str()) || 1061 0 == strcmp("EndCommentGroup", (*commands)[i].c_str()) || 1062 0 == strcmp("PopCull", (*commands)[i].c_str())) { 1063 indent -= 10; 1064 } 1065 1066 item->setData(Qt::UserRole + 3, indent); 1067 1068 if (0 == strcmp("Save", (*commands)[i].c_str()) || 1069 0 == strcmp("Save Layer", (*commands)[i].c_str()) || 1070 0 == strcmp("BeginCommentGroup", (*commands)[i].c_str()) || 1071 0 == strcmp("PushCull", (*commands)[i].c_str())) { 1072 indent += 10; 1073 } 1074 1075 item->setData(Qt::UserRole + 4, -1); 1076 item->setData(Qt::UserRole + 5, (int)(*offsets)[i]); 1077 1078 fListWidget.addItem(item); 1079 } 1080 } 1081 1082 void SkDebuggerGUI::setupOverviewText(const SkTDArray<double>* typeTimes, 1083 double totTime, 1084 int numRuns) { 1085 SkString overview; 1086 fDebugger.getOverviewText(typeTimes, totTime, &overview, numRuns); 1087 fInspectorWidget.setText(overview.c_str(), SkInspectorWidget::kOverview_TabType); 1088 } 1089 1090 void SkDebuggerGUI::setupClipStackText() { 1091 SkString clipStack; 1092 fDebugger.getClipStackText(&clipStack); 1093 fInspectorWidget.setText(clipStack.c_str(), SkInspectorWidget::kClipStack_TabType); 1094 } 1095 1096 void SkDebuggerGUI::setupComboBox(SkTArray<SkString>* command) { 1097 fFilter.clear(); 1098 fFilter.addItem("--Filter By Available Commands--"); 1099 1100 std::map<std::string, int> map; 1101 for (int i = 0; i < command->count(); i++) { 1102 map[(*command)[i].c_str()]++; 1103 } 1104 1105 for (std::map<std::string, int>::iterator it = map.begin(); it != map.end(); 1106 ++it) { 1107 fFilter.addItem((it->first).c_str()); 1108 } 1109 1110 // NOTE(chudy): Makes first item unselectable. 1111 QStandardItemModel* model = qobject_cast<QStandardItemModel*>( 1112 fFilter.model()); 1113 QModelIndex firstIndex = model->index(0, fFilter.modelColumn(), 1114 fFilter.rootModelIndex()); 1115 QStandardItem* firstItem = model->itemFromIndex(firstIndex); 1116 firstItem->setSelectable(false); 1117 } 1118