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