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