]> git.cworth.org Git - apitrace/blob - gui/mainwindow.cpp
Implement jumping to a specific call.
[apitrace] / gui / mainwindow.cpp
1 #include "mainwindow.h"
2
3 #include "apitrace.h"
4 #include "apitracecall.h"
5 #include "apicalldelegate.h"
6 #include "apitracemodel.h"
7 #include "apitracefilter.h"
8 #include "imageviewer.h"
9 #include "jumpwidget.h"
10 #include "retracer.h"
11 #include "settingsdialog.h"
12 #include "shaderssourcewidget.h"
13 #include "tracedialog.h"
14 #include "ui_retracerdialog.h"
15 #include "vertexdatainterpreter.h"
16
17 #include <QAction>
18 #include <QDebug>
19 #include <QDesktopServices>
20 #include <QDir>
21 #include <QFileDialog>
22 #include <QLineEdit>
23 #include <QMessageBox>
24 #include <QProgressBar>
25 #include <QShortcut>
26 #include <QToolBar>
27 #include <QUrl>
28 #include <QVBoxLayout>
29 #include <QWebPage>
30 #include <QWebView>
31
32
33 MainWindow::MainWindow()
34     : QMainWindow(),
35       m_selectedEvent(0),
36       m_stateEvent(0)
37 {
38     m_ui.setupUi(this);
39     initObjects();
40     initConnections();
41 }
42
43 void MainWindow::createTrace()
44 {
45     TraceDialog dialog;
46
47     if (dialog.exec() == QDialog::Accepted) {
48         qDebug()<< "App : " <<dialog.applicationPath();
49         qDebug()<< "  Arguments: "<<dialog.arguments();
50     }
51 }
52
53 void MainWindow::openTrace()
54 {
55     QString fileName =
56         QFileDialog::getOpenFileName(
57             this,
58             tr("Open Trace"),
59             QDir::homePath(),
60             tr("Trace Files (*.trace)"));
61
62     qDebug()<< "File name : " <<fileName;
63
64     newTraceFile(fileName);
65 }
66
67 void MainWindow::loadTrace(const QString &fileName)
68 {
69     if (!QFile::exists(fileName)) {
70         QMessageBox::warning(this, tr("File Missing"),
71                              tr("File '%1' doesn't exist.").arg(fileName));
72         return;
73     }
74     qDebug()<< "Loading  : " <<fileName;
75
76     m_progressBar->setValue(0);
77     newTraceFile(fileName);
78 }
79
80 void MainWindow::callItemSelected(const QModelIndex &index)
81 {
82     ApiTraceEvent *event =
83         index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
84
85     if (event && event->type() == ApiTraceEvent::Call) {
86         ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
87         m_ui.detailsWebView->setHtml(call->toHtml());
88         m_ui.detailsDock->show();
89         if (call->hasBinaryData()) {
90             QByteArray data =
91                 call->argValues[call->binaryDataIndex()].toByteArray();
92             m_vdataInterpreter->setData(data);
93
94             for (int i = 0; i < call->argNames.count(); ++i) {
95                 QString name = call->argNames[i];
96                 if (name == QLatin1String("stride")) {
97                     int stride = call->argValues[i].toInt();
98                     m_ui.vertexStrideSB->setValue(stride);
99                 } else if (name == QLatin1String("size")) {
100                     int components = call->argValues[i].toInt();
101                     m_ui.vertexComponentsSB->setValue(components);
102                 } else if (name == QLatin1String("type")) {
103                     QString val = call->argValues[i].toString();
104                     int textIndex = m_ui.vertexTypeCB->findText(val);
105                     if (textIndex >= 0)
106                         m_ui.vertexTypeCB->setCurrentIndex(textIndex);
107                 }
108             }
109         }
110         m_ui.vertexDataDock->setVisible(call->hasBinaryData());
111         m_selectedEvent = call;
112     } else {
113         if (event && event->type() == ApiTraceEvent::Frame) {
114             m_selectedEvent = static_cast<ApiTraceFrame*>(event);
115         } else
116             m_selectedEvent = 0;
117         m_ui.detailsDock->hide();
118         m_ui.vertexDataDock->hide();
119     }
120     if (m_selectedEvent && !m_selectedEvent->state().isEmpty()) {
121         fillStateForFrame();
122     } else
123         m_ui.stateDock->hide();
124 }
125
126 void MainWindow::filterTrace()
127 {
128     m_proxyModel->setFilterString(m_filterEdit->text());
129 }
130
131 void MainWindow::replayStart()
132 {
133     QDialog dlg;
134     Ui_RetracerDialog dlgUi;
135     dlgUi.setupUi(&dlg);
136
137     dlgUi.doubleBufferingCB->setChecked(
138         m_retracer->isDoubleBuffered());
139     dlgUi.benchmarkCB->setChecked(
140         m_retracer->isBenchmarking());
141
142     if (dlg.exec() == QDialog::Accepted) {
143         m_retracer->setDoubleBuffered(
144             dlgUi.doubleBufferingCB->isChecked());
145         m_retracer->setBenchmarking(
146             dlgUi.benchmarkCB->isChecked());
147         replayTrace(false);
148     }
149 }
150
151 void MainWindow::replayStop()
152 {
153     m_retracer->quit();
154     m_ui.actionStop->setEnabled(false);
155     m_ui.actionReplay->setEnabled(true);
156     m_ui.actionLookupState->setEnabled(true);
157 }
158
159 void MainWindow::newTraceFile(const QString &fileName)
160 {
161     m_traceFileName = fileName;
162     m_trace->setFileName(fileName);
163
164     if (m_traceFileName.isEmpty()) {
165         m_ui.actionReplay->setEnabled(false);
166         m_ui.actionLookupState->setEnabled(false);
167         setWindowTitle(tr("QApiTrace"));
168     } else {
169         QFileInfo info(fileName);
170         m_ui.actionReplay->setEnabled(true);
171         m_ui.actionLookupState->setEnabled(true);
172         setWindowTitle(
173             tr("QApiTrace - %1").arg(info.fileName()));
174     }
175 }
176
177 void MainWindow::replayFinished(const QString &output)
178 {
179     m_ui.actionStop->setEnabled(false);
180     m_ui.actionReplay->setEnabled(true);
181     m_ui.actionLookupState->setEnabled(true);
182
183     m_progressBar->hide();
184     if (output.length() < 80) {
185         statusBar()->showMessage(output);
186     }
187     m_stateEvent = 0;
188     statusBar()->showMessage(
189         tr("Replaying finished!"), 2000);
190 }
191
192 void MainWindow::replayError(const QString &message)
193 {
194     m_ui.actionStop->setEnabled(false);
195     m_ui.actionReplay->setEnabled(true);
196     m_ui.actionLookupState->setEnabled(true);
197     m_stateEvent = 0;
198
199     m_progressBar->hide();
200     statusBar()->showMessage(
201         tr("Replaying unsuccessful."), 2000);
202     QMessageBox::warning(
203         this, tr("Replay Failed"), message);
204 }
205
206 void MainWindow::startedLoadingTrace()
207 {
208     Q_ASSERT(m_trace);
209     m_progressBar->show();
210     QFileInfo info(m_trace->fileName());
211     statusBar()->showMessage(
212         tr("Loading %1...").arg(info.fileName()));
213 }
214
215 void MainWindow::finishedLoadingTrace()
216 {
217     m_progressBar->hide();
218     if (!m_trace) {
219         return;
220     }
221     QFileInfo info(m_trace->fileName());
222     statusBar()->showMessage(
223         tr("Loaded %1").arg(info.fileName()), 3000);
224 }
225
226 void MainWindow::replayTrace(bool dumpState)
227 {
228     if (m_traceFileName.isEmpty())
229         return;
230
231     m_retracer->setFileName(m_traceFileName);
232     m_retracer->setCaptureState(dumpState);
233     if (m_retracer->captureState() && m_selectedEvent) {
234         int index = 0;
235         if (m_selectedEvent->type() == ApiTraceEvent::Call) {
236             index = static_cast<ApiTraceCall*>(m_selectedEvent)->index;
237         } else if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
238             ApiTraceFrame *frame =
239                 static_cast<ApiTraceFrame*>(m_selectedEvent);
240             if (frame->calls.isEmpty()) {
241                 //XXX i guess we could still get the current state
242                 qDebug()<<"tried to get a state for an empty frame";
243                 return;
244             }
245             index = frame->calls.first()->index;
246         } else {
247             qDebug()<<"Unknown event type";
248             return;
249         }
250         m_retracer->setCaptureAtCallNumber(index);
251     }
252     m_retracer->start();
253
254     m_ui.actionStop->setEnabled(true);
255     m_progressBar->show();
256     if (dumpState)
257         statusBar()->showMessage(
258             tr("Looking up the state..."));
259     else
260         statusBar()->showMessage(
261             tr("Replaying the trace file..."));
262 }
263
264 void MainWindow::lookupState()
265 {
266     if (!m_selectedEvent) {
267         QMessageBox::warning(
268             this, tr("Unknown Event"),
269             tr("To inspect the state select an event in the event list."));
270         return;
271     }
272     m_stateEvent = m_selectedEvent;
273     replayTrace(true);
274 }
275
276 MainWindow::~MainWindow()
277 {
278 }
279
280 static void
281 variantToString(const QVariant &var, QString &str)
282 {
283     if (var.type() == QVariant::List) {
284         QVariantList lst = var.toList();
285         str += QLatin1String("[");
286         for (int i = 0; i < lst.count(); ++i) {
287             QVariant val = lst[i];
288             variantToString(val, str);
289             if (i < lst.count() - 1)
290                 str += QLatin1String(", ");
291         }
292         str += QLatin1String("]");
293     } else if (var.type() == QVariant::Map) {
294         Q_ASSERT(!"unsupported state type");
295     } else if (var.type() == QVariant::Hash) {
296         Q_ASSERT(!"unsupported state type");
297     } else {
298         str += var.toString();
299     }
300 }
301
302 void MainWindow::fillStateForFrame()
303 {
304     QVariantMap::const_iterator itr;
305     QVariantMap params;
306
307     if (!m_selectedEvent || m_selectedEvent->state().isEmpty())
308         return;
309
310     const ApiTraceState &state = m_selectedEvent->state();
311     m_ui.stateTreeWidget->clear();
312     params = state.parameters();
313     QList<QTreeWidgetItem *> items;
314     for (itr = params.constBegin(); itr != params.constEnd(); ++itr) {
315         QString key = itr.key();
316         QString val;
317
318         variantToString(itr.value(), val);
319         //qDebug()<<"key = "<<key;
320         //qDebug()<<"val = "<<val;
321         QStringList lst;
322         lst += key;
323         lst += val;
324         items.append(new QTreeWidgetItem((QTreeWidget*)0, lst));
325     }
326     m_ui.stateTreeWidget->insertTopLevelItems(0, items);
327
328     QStringList shaderSources = state.shaderSources();
329     if (shaderSources.isEmpty()) {
330         m_sourcesWidget->setShaders(shaderSources);
331     } else {
332         m_sourcesWidget->setShaders(shaderSources);
333     }
334
335     const QList<ApiTexture> &textures =
336         state.textures();
337     const QList<ApiFramebuffer> &fbos =
338         state.framebuffers();
339
340     m_ui.surfacesTreeWidget->clear();
341     if (textures.isEmpty() && fbos.isEmpty()) {
342         m_ui.surfacesTab->setDisabled(false);
343     } else {
344         m_ui.surfacesTreeWidget->setIconSize(QSize(64, 64));
345         if (!textures.isEmpty()) {
346             QTreeWidgetItem *textureItem =
347                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
348             textureItem->setText(0, tr("Textures"));
349             for (int i = 0; i < textures.count(); ++i) {
350                 const ApiTexture &texture =
351                     textures[i];
352                 QIcon icon(QPixmap::fromImage(texture.thumb()));
353                 QTreeWidgetItem *item = new QTreeWidgetItem(textureItem);
354                 item->setIcon(0, icon);
355                 int width = texture.size().width();
356                 int height = texture.size().height();
357                 QString descr =
358                     QString::fromLatin1("%1, %2 x %3")
359                     .arg(texture.target())
360                     .arg(width)
361                     .arg(height);
362                 item->setText(1, descr);
363
364                 item->setData(0, Qt::UserRole,
365                               texture.image());
366             }
367         }
368         if (!fbos.isEmpty()) {
369             QTreeWidgetItem *fboItem =
370                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
371             fboItem->setText(0, tr("Framebuffers"));
372             for (int i = 0; i < fbos.count(); ++i) {
373                 const ApiFramebuffer &fbo =
374                     fbos[i];
375                 QIcon icon(QPixmap::fromImage(fbo.thumb()));
376                 QTreeWidgetItem *item = new QTreeWidgetItem(fboItem);
377                 item->setIcon(0, icon);
378                 int width = fbo.size().width();
379                 int height = fbo.size().height();
380                 QString descr =
381                     QString::fromLatin1("%1, %2 x %3")
382                     .arg(fbo.type())
383                     .arg(width)
384                     .arg(height);
385                 item->setText(1, descr);
386
387                 item->setData(0, Qt::UserRole,
388                               fbo.image());
389             }
390         }
391         m_ui.surfacesTab->setEnabled(true);
392     }
393     m_ui.stateDock->show();
394 }
395
396 void MainWindow::showSettings()
397 {
398     SettingsDialog dialog;
399     dialog.setFilterOptions(m_proxyModel->filterOptions());
400
401     if (dialog.exec() == QDialog::Accepted) {
402         m_proxyModel->setFilterOptions(dialog.filterOptions());
403     }
404 }
405
406 void MainWindow::openHelp(const QUrl &url)
407 {
408     QDesktopServices::openUrl(url);
409 }
410
411 void MainWindow::showSurfacesMenu(const QPoint &pos)
412 {
413     QTreeWidget *tree = m_ui.surfacesTreeWidget;
414     QTreeWidgetItem *item = tree->itemAt(pos);
415     if (!item)
416         return;
417
418     QMenu menu(tr("Surfaces"), this);
419     //add needed actions
420     QAction *act = menu.addAction(tr("View Image"));
421     act->setStatusTip(tr("View the currently selected surface"));
422     connect(act, SIGNAL(triggered()),
423             SLOT(showSelectedSurface()));
424
425     menu.exec(tree->viewport()->mapToGlobal(pos));
426 }
427
428 void MainWindow::showSelectedSurface()
429 {
430     QTreeWidgetItem *item =
431         m_ui.surfacesTreeWidget->currentItem();
432
433     if (!item)
434         return;
435
436     QVariant var = item->data(0, Qt::UserRole);
437     m_imageViewer->setImage(var.value<QImage>());
438     m_imageViewer->show();
439     m_imageViewer->raise();
440     m_imageViewer->activateWindow();
441 }
442
443 void MainWindow::initObjects()
444 {
445     m_ui.stateTreeWidget->sortByColumn(0, Qt::AscendingOrder);
446
447     m_sourcesWidget = new ShadersSourceWidget(m_ui.shadersTab);
448     QVBoxLayout *layout = new QVBoxLayout;
449     layout->addWidget(m_sourcesWidget);
450     m_ui.shadersTab->setLayout(layout);
451
452     m_trace = new ApiTrace();
453     m_retracer = new Retracer(this);
454
455     m_vdataInterpreter = new VertexDataInterpreter(this);
456     m_vdataInterpreter->setListWidget(m_ui.vertexDataListWidget);
457     m_vdataInterpreter->setStride(
458         m_ui.vertexStrideSB->value());
459     m_vdataInterpreter->setComponents(
460         m_ui.vertexComponentsSB->value());
461     m_vdataInterpreter->setStartingOffset(
462         m_ui.startingOffsetSB->value());
463     m_vdataInterpreter->setTypeFromString(
464         m_ui.vertexTypeCB->currentText());
465
466     m_imageViewer = new ImageViewer(this);
467
468     m_model = new ApiTraceModel();
469     m_model->setApiTrace(m_trace);
470     m_proxyModel = new ApiTraceFilter();
471     m_proxyModel->setSourceModel(m_model);
472     m_ui.callView->setModel(m_proxyModel);
473     m_ui.callView->setItemDelegate(new ApiCallDelegate);
474     m_ui.callView->resizeColumnToContents(0);
475     m_ui.callView->header()->swapSections(0, 1);
476     m_ui.callView->setColumnWidth(1, 42);
477
478     QToolBar *toolBar = addToolBar(tr("Navigation"));
479     m_filterEdit = new QLineEdit(toolBar);
480     toolBar->addWidget(m_filterEdit);
481
482     m_progressBar = new QProgressBar();
483     m_progressBar->setRange(0, 0);
484     statusBar()->addPermanentWidget(m_progressBar);
485     m_progressBar->hide();
486
487     m_ui.detailsDock->hide();
488     m_ui.vertexDataDock->hide();
489     m_ui.stateDock->hide();
490     setDockOptions(dockOptions() | QMainWindow::ForceTabbedDocks);
491
492     tabifyDockWidget(m_ui.stateDock, m_ui.vertexDataDock);
493
494     m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
495
496     m_ui.detailsWebView->page()->setLinkDelegationPolicy(
497         QWebPage::DelegateExternalLinks);
498
499     m_jumpWidget = new JumpWidget(this);
500     m_ui.centralLayout->addWidget(m_jumpWidget);
501     m_jumpWidget->hide();
502
503
504     new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_G),
505                   this, SLOT(slotGoTo()));
506 }
507
508 void MainWindow::initConnections()
509 {
510     connect(m_trace, SIGNAL(startedLoadingTrace()),
511             this, SLOT(startedLoadingTrace()));
512     connect(m_trace, SIGNAL(finishedLoadingTrace()),
513             this, SLOT(finishedLoadingTrace()));
514
515     connect(m_retracer, SIGNAL(finished(const QString&)),
516             this, SLOT(replayFinished(const QString&)));
517     connect(m_retracer, SIGNAL(error(const QString&)),
518             this, SLOT(replayError(const QString&)));
519     connect(m_retracer, SIGNAL(foundState(const ApiTraceState&)),
520             this, SLOT(replayStateFound(const ApiTraceState&)));
521
522     connect(m_ui.vertexInterpretButton, SIGNAL(clicked()),
523             m_vdataInterpreter, SLOT(interpretData()));
524     connect(m_ui.vertexTypeCB, SIGNAL(currentIndexChanged(const QString&)),
525             m_vdataInterpreter, SLOT(setTypeFromString(const QString&)));
526     connect(m_ui.vertexStrideSB, SIGNAL(valueChanged(int)),
527             m_vdataInterpreter, SLOT(setStride(int)));
528     connect(m_ui.vertexComponentsSB, SIGNAL(valueChanged(int)),
529             m_vdataInterpreter, SLOT(setComponents(int)));
530     connect(m_ui.startingOffsetSB, SIGNAL(valueChanged(int)),
531             m_vdataInterpreter, SLOT(setStartingOffset(int)));
532
533
534     connect(m_ui.actionNew, SIGNAL(triggered()),
535             this, SLOT(createTrace()));
536     connect(m_ui.actionOpen, SIGNAL(triggered()),
537             this, SLOT(openTrace()));
538     connect(m_ui.actionQuit, SIGNAL(triggered()),
539             this, SLOT(close()));
540
541     connect(m_ui.actionReplay, SIGNAL(triggered()),
542             this, SLOT(replayStart()));
543     connect(m_ui.actionStop, SIGNAL(triggered()),
544             this, SLOT(replayStop()));
545     connect(m_ui.actionLookupState, SIGNAL(triggered()),
546             this, SLOT(lookupState()));
547     connect(m_ui.actionOptions, SIGNAL(triggered()),
548             this, SLOT(showSettings()));
549
550     connect(m_ui.callView, SIGNAL(activated(const QModelIndex &)),
551             this, SLOT(callItemSelected(const QModelIndex &)));
552     connect(m_filterEdit, SIGNAL(returnPressed()),
553             this, SLOT(filterTrace()));
554
555     connect(m_ui.surfacesTreeWidget,
556             SIGNAL(customContextMenuRequested(const QPoint &)),
557             SLOT(showSurfacesMenu(const QPoint &)));
558     connect(m_ui.surfacesTreeWidget,
559             SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)),
560             SLOT(showSelectedSurface()));
561
562     connect(m_ui.detailsWebView, SIGNAL(linkClicked(const QUrl&)),
563             this, SLOT(openHelp(const QUrl&)));
564
565     connect(m_jumpWidget, SIGNAL(jumpTo(int)),
566             SLOT(slotJumpTo(int)));
567 }
568
569 void MainWindow::replayStateFound(const ApiTraceState &state)
570 {
571     m_stateEvent->setState(state);
572     m_model->stateSetOnEvent(m_stateEvent);
573     if (m_selectedEvent == m_stateEvent) {
574         fillStateForFrame();
575     } else {
576         m_ui.stateDock->hide();
577     }
578 }
579
580 void MainWindow::slotGoTo()
581 {
582     m_jumpWidget->show();
583 }
584
585 void MainWindow::slotJumpTo(int callNum)
586 {
587     QModelIndex index = m_proxyModel->callIndex(callNum);
588     if (index.isValid()) {
589         m_ui.callView->setCurrentIndex(index);
590     }
591 }
592
593 #include "mainwindow.moc"