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