]> git.cworth.org Git - apitrace/blob - gui/mainwindow.cpp
Expand surface items if there's not too many of them.
[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             if (textures.count() <= 6)
362                 textureItem->setExpanded(true);
363
364             for (int i = 0; i < textures.count(); ++i) {
365                 const ApiTexture &texture =
366                     textures[i];
367                 QIcon icon(QPixmap::fromImage(texture.thumb()));
368                 QTreeWidgetItem *item = new QTreeWidgetItem(textureItem);
369                 item->setIcon(0, icon);
370                 int width = texture.size().width();
371                 int height = texture.size().height();
372                 QString descr =
373                     QString::fromLatin1("%1, %2 x %3")
374                     .arg(texture.target())
375                     .arg(width)
376                     .arg(height);
377                 item->setText(1, descr);
378
379                 item->setData(0, Qt::UserRole,
380                               texture.image());
381             }
382         }
383         if (!fbos.isEmpty()) {
384             QTreeWidgetItem *fboItem =
385                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
386             fboItem->setText(0, tr("Framebuffers"));
387             if (fbos.count() <= 6)
388                 fboItem->setExpanded(true);
389
390             for (int i = 0; i < fbos.count(); ++i) {
391                 const ApiFramebuffer &fbo =
392                     fbos[i];
393                 QIcon icon(QPixmap::fromImage(fbo.thumb()));
394                 QTreeWidgetItem *item = new QTreeWidgetItem(fboItem);
395                 item->setIcon(0, icon);
396                 int width = fbo.size().width();
397                 int height = fbo.size().height();
398                 QString descr =
399                     QString::fromLatin1("%1, %2 x %3")
400                     .arg(fbo.type())
401                     .arg(width)
402                     .arg(height);
403                 item->setText(1, descr);
404
405                 item->setData(0, Qt::UserRole,
406                               fbo.image());
407             }
408         }
409         m_ui.surfacesTab->setEnabled(true);
410     }
411     m_ui.stateDock->show();
412 }
413
414 void MainWindow::showSettings()
415 {
416     SettingsDialog dialog;
417     dialog.setFilterOptions(m_proxyModel->filterOptions());
418
419     if (dialog.exec() == QDialog::Accepted) {
420         m_proxyModel->setFilterOptions(dialog.filterOptions());
421     }
422 }
423
424 void MainWindow::openHelp(const QUrl &url)
425 {
426     QDesktopServices::openUrl(url);
427 }
428
429 void MainWindow::showSurfacesMenu(const QPoint &pos)
430 {
431     QTreeWidget *tree = m_ui.surfacesTreeWidget;
432     QTreeWidgetItem *item = tree->itemAt(pos);
433     if (!item)
434         return;
435
436     QMenu menu(tr("Surfaces"), this);
437     //add needed actions
438     QAction *act = menu.addAction(tr("View Image"));
439     act->setStatusTip(tr("View the currently selected surface"));
440     connect(act, SIGNAL(triggered()),
441             SLOT(showSelectedSurface()));
442
443     menu.exec(tree->viewport()->mapToGlobal(pos));
444 }
445
446 void MainWindow::showSelectedSurface()
447 {
448     QTreeWidgetItem *item =
449         m_ui.surfacesTreeWidget->currentItem();
450
451     if (!item)
452         return;
453
454     QVariant var = item->data(0, Qt::UserRole);
455     m_imageViewer->setImage(var.value<QImage>());
456     m_imageViewer->show();
457     m_imageViewer->raise();
458     m_imageViewer->activateWindow();
459 }
460
461 void MainWindow::initObjects()
462 {
463     m_ui.stateTreeWidget->sortByColumn(0, Qt::AscendingOrder);
464
465     m_sourcesWidget = new ShadersSourceWidget(m_ui.shadersTab);
466     QVBoxLayout *layout = new QVBoxLayout;
467     layout->addWidget(m_sourcesWidget);
468     m_ui.shadersTab->setLayout(layout);
469
470     m_trace = new ApiTrace();
471     m_retracer = new Retracer(this);
472
473     m_vdataInterpreter = new VertexDataInterpreter(this);
474     m_vdataInterpreter->setListWidget(m_ui.vertexDataListWidget);
475     m_vdataInterpreter->setStride(
476         m_ui.vertexStrideSB->value());
477     m_vdataInterpreter->setComponents(
478         m_ui.vertexComponentsSB->value());
479     m_vdataInterpreter->setStartingOffset(
480         m_ui.startingOffsetSB->value());
481     m_vdataInterpreter->setTypeFromString(
482         m_ui.vertexTypeCB->currentText());
483
484     m_imageViewer = new ImageViewer(this);
485
486     m_model = new ApiTraceModel();
487     m_model->setApiTrace(m_trace);
488     m_proxyModel = new ApiTraceFilter();
489     m_proxyModel->setSourceModel(m_model);
490     m_ui.callView->setModel(m_proxyModel);
491     m_ui.callView->setItemDelegate(new ApiCallDelegate);
492     m_ui.callView->resizeColumnToContents(0);
493     m_ui.callView->header()->swapSections(0, 1);
494     m_ui.callView->setColumnWidth(1, 42);
495
496     QToolBar *toolBar = addToolBar(tr("Navigation"));
497     m_filterEdit = new QLineEdit(toolBar);
498     toolBar->addWidget(m_filterEdit);
499
500     m_progressBar = new QProgressBar();
501     m_progressBar->setRange(0, 0);
502     statusBar()->addPermanentWidget(m_progressBar);
503     m_progressBar->hide();
504
505     m_ui.detailsDock->hide();
506     m_ui.vertexDataDock->hide();
507     m_ui.stateDock->hide();
508     setDockOptions(dockOptions() | QMainWindow::ForceTabbedDocks);
509
510     tabifyDockWidget(m_ui.stateDock, m_ui.vertexDataDock);
511
512     m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
513
514     m_ui.detailsWebView->page()->setLinkDelegationPolicy(
515         QWebPage::DelegateExternalLinks);
516
517     m_jumpWidget = new JumpWidget(this);
518     m_ui.centralLayout->addWidget(m_jumpWidget);
519     m_jumpWidget->hide();
520
521     m_traceProcess = new TraceProcess(this);
522
523     new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_G),
524                   this, SLOT(slotGoTo()));
525 }
526
527 void MainWindow::initConnections()
528 {
529     connect(m_trace, SIGNAL(startedLoadingTrace()),
530             this, SLOT(startedLoadingTrace()));
531     connect(m_trace, SIGNAL(finishedLoadingTrace()),
532             this, SLOT(finishedLoadingTrace()));
533
534     connect(m_retracer, SIGNAL(finished(const QString&)),
535             this, SLOT(replayFinished(const QString&)));
536     connect(m_retracer, SIGNAL(error(const QString&)),
537             this, SLOT(replayError(const QString&)));
538     connect(m_retracer, SIGNAL(foundState(const ApiTraceState&)),
539             this, SLOT(replayStateFound(const ApiTraceState&)));
540
541     connect(m_ui.vertexInterpretButton, SIGNAL(clicked()),
542             m_vdataInterpreter, SLOT(interpretData()));
543     connect(m_ui.vertexTypeCB, SIGNAL(currentIndexChanged(const QString&)),
544             m_vdataInterpreter, SLOT(setTypeFromString(const QString&)));
545     connect(m_ui.vertexStrideSB, SIGNAL(valueChanged(int)),
546             m_vdataInterpreter, SLOT(setStride(int)));
547     connect(m_ui.vertexComponentsSB, SIGNAL(valueChanged(int)),
548             m_vdataInterpreter, SLOT(setComponents(int)));
549     connect(m_ui.startingOffsetSB, SIGNAL(valueChanged(int)),
550             m_vdataInterpreter, SLOT(setStartingOffset(int)));
551
552
553     connect(m_ui.actionNew, SIGNAL(triggered()),
554             this, SLOT(createTrace()));
555     connect(m_ui.actionOpen, SIGNAL(triggered()),
556             this, SLOT(openTrace()));
557     connect(m_ui.actionQuit, SIGNAL(triggered()),
558             this, SLOT(close()));
559
560     connect(m_ui.actionReplay, SIGNAL(triggered()),
561             this, SLOT(replayStart()));
562     connect(m_ui.actionStop, SIGNAL(triggered()),
563             this, SLOT(replayStop()));
564     connect(m_ui.actionLookupState, SIGNAL(triggered()),
565             this, SLOT(lookupState()));
566     connect(m_ui.actionOptions, SIGNAL(triggered()),
567             this, SLOT(showSettings()));
568
569     connect(m_ui.callView, SIGNAL(activated(const QModelIndex &)),
570             this, SLOT(callItemSelected(const QModelIndex &)));
571     connect(m_filterEdit, SIGNAL(returnPressed()),
572             this, SLOT(filterTrace()));
573
574     connect(m_ui.surfacesTreeWidget,
575             SIGNAL(customContextMenuRequested(const QPoint &)),
576             SLOT(showSurfacesMenu(const QPoint &)));
577     connect(m_ui.surfacesTreeWidget,
578             SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)),
579             SLOT(showSelectedSurface()));
580
581     connect(m_ui.detailsWebView, SIGNAL(linkClicked(const QUrl&)),
582             this, SLOT(openHelp(const QUrl&)));
583
584     connect(m_jumpWidget, SIGNAL(jumpTo(int)),
585             SLOT(slotJumpTo(int)));
586
587     connect(m_traceProcess, SIGNAL(tracedFile(const QString&)),
588             SLOT(createdTrace(const QString&)));
589     connect(m_traceProcess, SIGNAL(error(const QString&)),
590             SLOT(traceError(const QString&)));
591 }
592
593 void MainWindow::replayStateFound(const ApiTraceState &state)
594 {
595     m_stateEvent->setState(state);
596     m_model->stateSetOnEvent(m_stateEvent);
597     if (m_selectedEvent == m_stateEvent) {
598         fillStateForFrame();
599     } else {
600         m_ui.stateDock->hide();
601     }
602 }
603
604 void MainWindow::slotGoTo()
605 {
606     m_jumpWidget->show();
607 }
608
609 void MainWindow::slotJumpTo(int callNum)
610 {
611     QModelIndex index = m_proxyModel->callIndex(callNum);
612     if (index.isValid()) {
613         m_ui.callView->setCurrentIndex(index);
614     }
615 }
616
617 void MainWindow::createdTrace(const QString &path)
618 {
619     qDebug()<<"Done tracing "<<path;
620     newTraceFile(path);
621 }
622
623 void MainWindow::traceError(const QString &msg)
624 {
625     QMessageBox::warning(
626             this,
627             tr("Tracing Error"),
628             msg);
629 }
630
631 #include "mainwindow.moc"