X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=gui%2Fmainwindow.cpp;h=ff51d956be5150c6aed258393ce2fc4bff4a3854;hb=a69f0de6b922160bec029cfd18b5002b904afccf;hp=472ff27a4cac4a8fe78c2aef8472026df776fc65;hpb=2caa06d01202fe4e8ba9893439de17bf62d0bf85;p=apitrace diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 472ff27..3278330 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -5,251 +5,1458 @@ #include "apicalldelegate.h" #include "apitracemodel.h" #include "apitracefilter.h" +#include "argumentseditor.h" +#include "imageviewer.h" +#include "jumpwidget.h" +#include "profiledialog.h" +#include "retracer.h" +#include "searchwidget.h" +#include "settingsdialog.h" +#include "shaderssourcewidget.h" +#include "tracedialog.h" +#include "traceprocess.h" +#include "trimprocess.h" +#include "thumbnail.h" +#include "ui_retracerdialog.h" +#include "ui_profilereplaydialog.h" +#include "vertexdatainterpreter.h" +#include "trace_profiler.hpp" #include +#include #include +#include +#include #include #include #include #include -#include #include #include +#include +#include +#include #include MainWindow::MainWindow() : QMainWindow(), - m_replayProcess(0), - m_findingState(false) + m_api(trace::API_GL), + m_initalCallNum(-1), + m_selectedEvent(0), + m_stateEvent(0), + m_nonDefaultsLookupEvent(0) { m_ui.setupUi(this); + updateActionsState(false); + initObjects(); + initConnections(); +} + +MainWindow::~MainWindow() +{ + delete m_trace; + m_trace = 0; + + delete m_proxyModel; + delete m_model; +} + +void MainWindow::createTrace() +{ + if (!m_traceProcess->canTrace()) { + QMessageBox::warning( + this, + tr("Unsupported"), + tr("Current configuration doesn't support tracing.")); + return; + } + + TraceDialog dialog; + if (dialog.exec() == QDialog::Accepted) { + qDebug()<< "App : " <setApi(dialog.api()); + m_traceProcess->setExecutablePath(dialog.applicationPath()); + m_traceProcess->setArguments(dialog.arguments()); + m_traceProcess->start(); + } +} + +void MainWindow::openTrace() +{ + QString fileName = + QFileDialog::getOpenFileName( + this, + tr("Open Trace"), + QDir::homePath(), + tr("Trace Files (*.trace)")); + + if (!fileName.isEmpty() && QFile::exists(fileName)) { + newTraceFile(fileName); + } +} + +void MainWindow::loadTrace(const QString &fileName, int callNum) +{ + if (!QFile::exists(fileName)) { + QMessageBox::warning(this, tr("File Missing"), + tr("File '%1' doesn't exist.").arg(fileName)); + return; + } + + m_initalCallNum = callNum; + newTraceFile(fileName); +} + +void MainWindow::setRemoteTarget(const QString &host) +{ + m_retracer->setRemoteTarget(host); +} + +void MainWindow::callItemSelected(const QModelIndex &index) +{ + ApiTraceEvent *event = + index.data(ApiTraceModel::EventRole).value(); + + if (event && event->type() == ApiTraceEvent::Call) { + ApiTraceCall *call = static_cast(event); + m_ui.detailsDock->setWindowTitle( + tr("Details View. Frame %1, Call %2") + .arg(call->parentFrame() ? call->parentFrame()->number : 0) + .arg(call->index())); + m_ui.detailsWebView->setHtml(call->toHtml()); + m_ui.detailsDock->show(); + if (call->hasBinaryData()) { + QByteArray data = + call->arguments()[call->binaryDataIndex()].toByteArray(); + m_vdataInterpreter->setData(data); + + QVector args = call->arguments(); + for (int i = 0; i < call->argNames().count(); ++i) { + QString name = call->argNames()[i]; + if (name == QLatin1String("stride")) { + int stride = args[i].toInt(); + m_ui.vertexStrideSB->setValue(stride); + } else if (name == QLatin1String("size")) { + int components = args[i].toInt(); + m_ui.vertexComponentsSB->setValue(components); + } else if (name == QLatin1String("type")) { + QString val = args[i].toString(); + int textIndex = m_ui.vertexTypeCB->findText(val); + if (textIndex >= 0) { + m_ui.vertexTypeCB->setCurrentIndex(textIndex); + } + } + } + } + m_ui.backtraceBrowser->setText(call->backtrace()); + m_ui.backtraceDock->setVisible(!call->backtrace().isNull()); + m_ui.vertexDataDock->setVisible(call->hasBinaryData()); + m_selectedEvent = call; + } else { + if (event && event->type() == ApiTraceEvent::Frame) { + m_selectedEvent = static_cast(event); + } else { + m_selectedEvent = 0; + } + m_ui.detailsDock->hide(); + m_ui.backtraceDock->hide(); + m_ui.vertexDataDock->hide(); + } + if (m_selectedEvent && m_selectedEvent->hasState()) { + fillStateForFrame(); + } else { + m_ui.stateDock->hide(); + } +} + +void MainWindow::callItemActivated(const QModelIndex &index) { + lookupState(); +} + +void MainWindow::replayStart() +{ + if (m_trace->isSaving()) { + QMessageBox::warning( + this, + tr("Trace Saving"), + tr("QApiTrace is currently saving the edited trace file. " + "Please wait until it finishes and try again.")); + return; + } + + QDialog dlg; + Ui_RetracerDialog dlgUi; + dlgUi.setupUi(&dlg); + + dlgUi.doubleBufferingCB->setChecked( + m_retracer->isDoubleBuffered()); + + dlgUi.errorCheckCB->setChecked( + !m_retracer->isBenchmarking()); + + dlgUi.singlethreadCB->setChecked( + m_retracer->isSinglethread()); + + if (dlg.exec() == QDialog::Accepted) { + m_retracer->setDoubleBuffered( + dlgUi.doubleBufferingCB->isChecked()); + + m_retracer->setBenchmarking( + !dlgUi.errorCheckCB->isChecked()); + + m_retracer->setSinglethread( + dlgUi.singlethreadCB->isChecked()); + + m_retracer->setProfiling(false, false, false); + + replayTrace(false, false); + } +} + +void MainWindow::replayProfile() +{ + if (m_trace->isSaving()) { + QMessageBox::warning( + this, + tr("Trace Saving"), + tr("QApiTrace is currently saving the edited trace file. " + "Please wait until it finishes and try again.")); + return; + } + + QDialog dlg; + Ui_ProfileReplayDialog dlgUi; + dlgUi.setupUi(&dlg); + + if (dlg.exec() == QDialog::Accepted) { + m_retracer->setProfiling( + dlgUi.gpuTimesCB->isChecked(), + dlgUi.cpuTimesCB->isChecked(), + dlgUi.pixelsDrawnCB->isChecked()); + + replayTrace(false, false); + } +} + +void MainWindow::replayStop() +{ + m_retracer->quit(); + updateActionsState(true, true); +} + +void MainWindow::newTraceFile(const QString &fileName) +{ + qDebug()<< "Loading : " <setValue(0); + m_trace->setFileName(fileName); + + if (fileName.isEmpty()) { + updateActionsState(false); + setWindowTitle(tr("QApiTrace")); + } else { + updateActionsState(true); + QFileInfo info(fileName); + setWindowTitle( + tr("QApiTrace - %1").arg(info.fileName())); + } +} + +void MainWindow::replayFinished(const QString &message) +{ + updateActionsState(true); + m_progressBar->hide(); + statusBar()->showMessage(message, 2000); + m_stateEvent = 0; + m_ui.actionShowErrorsDock->setEnabled(m_trace->hasErrors()); + m_ui.errorsDock->setVisible(m_trace->hasErrors()); + if (!m_trace->hasErrors()) { + m_ui.errorsTreeWidget->clear(); + } +} + +void MainWindow::replayError(const QString &message) +{ + updateActionsState(true); + m_stateEvent = 0; + m_nonDefaultsLookupEvent = 0; + + m_progressBar->hide(); + statusBar()->showMessage( + tr("Replaying unsuccessful."), 2000); + QMessageBox::warning( + this, tr("Replay Failed"), message); +} + +void MainWindow::startedLoadingTrace() +{ + Q_ASSERT(m_trace); + m_progressBar->show(); + QFileInfo info(m_trace->fileName()); + statusBar()->showMessage( + tr("Loading %1...").arg(info.fileName())); +} + +void MainWindow::finishedLoadingTrace() +{ + m_progressBar->hide(); + if (!m_trace) { + return; + } + m_api = m_trace->api(); + QFileInfo info(m_trace->fileName()); + statusBar()->showMessage( + tr("Loaded %1").arg(info.fileName()), 3000); + if (m_initalCallNum >= 0) { + m_trace->findCallIndex(m_initalCallNum); + m_initalCallNum = -1; + } +} + +void MainWindow::replayTrace(bool dumpState, bool dumpThumbnails) +{ + if (m_trace->fileName().isEmpty()) { + return; + } + + m_retracer->setFileName(m_trace->fileName()); + m_retracer->setAPI(m_api); + m_retracer->setCaptureState(dumpState); + m_retracer->setCaptureThumbnails(dumpThumbnails); + if (m_retracer->captureState() && m_selectedEvent) { + int index = 0; + if (m_selectedEvent->type() == ApiTraceEvent::Call) { + index = static_cast(m_selectedEvent)->index(); + } else if (m_selectedEvent->type() == ApiTraceEvent::Frame) { + ApiTraceFrame *frame = + static_cast(m_selectedEvent); + if (frame->isEmpty()) { + //XXX i guess we could still get the current state + qDebug()<<"tried to get a state for an empty frame"; + return; + } + index = frame->lastCallIndex(); + } else { + qDebug()<<"Unknown event type"; + return; + } + m_retracer->setCaptureAtCallNumber(index); + } + m_retracer->start(); + + m_ui.actionStop->setEnabled(true); + m_progressBar->show(); + if (dumpState || dumpThumbnails) { + if (dumpState && dumpThumbnails) { + statusBar()->showMessage( + tr("Looking up the state and capturing thumbnails...")); + } else if (dumpState) { + statusBar()->showMessage( + tr("Looking up the state...")); + } else if (dumpThumbnails) { + statusBar()->showMessage( + tr("Capturing thumbnails...")); + } + } else if (m_retracer->isProfiling()) { + statusBar()->showMessage( + tr("Profiling draw calls in trace file...")); + } else { + statusBar()->showMessage( + tr("Replaying the trace file...")); + } +} + +void MainWindow::trimEvent() +{ + int trimIndex = 0; + + Q_ASSERT(m_trimEvent->type() == ApiTraceEvent::Call || + m_trimEvent->type() == ApiTraceEvent::Frame); + + if (m_trimEvent->type() == ApiTraceEvent::Call) { + ApiTraceCall *call = static_cast(m_trimEvent); + trimIndex = call->index(); + } else if (m_trimEvent->type() == ApiTraceEvent::Frame) { + ApiTraceFrame *frame = static_cast(m_trimEvent); + const QList frames = m_trace->frames(); + trimIndex = frame->lastCallIndex(); + } + + m_trimProcess->setTracePath(m_trace->fileName()); + m_trimProcess->setTrimIndex(trimIndex); + + m_trimProcess->start(); +} + +void MainWindow::lookupState() +{ + if (!m_selectedEvent) { + QMessageBox::warning( + this, tr("Unknown Event"), + tr("To inspect the state select an event in the event list.")); + return; + } + if (m_trace->isSaving()) { + QMessageBox::warning( + this, + tr("Trace Saving"), + tr("QApiTrace is currently saving the edited trace file. " + "Please wait until it finishes and try again.")); + return; + } + m_stateEvent = m_selectedEvent; + replayTrace(true, false); +} + +void MainWindow::showThumbnails() +{ + replayTrace(false, true); +} + +void MainWindow::trim() +{ + if (!m_selectedEvent) { + QMessageBox::warning( + this, tr("Unknown Event"), + tr("To trim select a frame or an event in the event list.")); + return; + } + m_trimEvent = m_selectedEvent; + trimEvent(); +} + +static void +variantToString(const QVariant &var, QString &str) +{ + if (var.type() == QVariant::List) { + QVector lst = var.toList().toVector(); + str += QLatin1String("["); + for (int i = 0; i < lst.count(); ++i) { + QVariant val = lst[i]; + variantToString(val, str); + if (i < lst.count() - 1) + str += QLatin1String(", "); + } + str += QLatin1String("]"); + } else if (var.type() == QVariant::Map) { + Q_ASSERT(!"unsupported state type"); + } else if (var.type() == QVariant::Hash) { + Q_ASSERT(!"unsupported state type"); + } else { + str += var.toString(); + } +} + +static QTreeWidgetItem * +variantToItem(const QString &key, const QVariant &var, + const QVariant &defaultVar); + +static void +variantMapToItems(const QVariantMap &map, const QVariantMap &defaultMap, + QList &items) +{ + QVariantMap::const_iterator itr; + for (itr = map.constBegin(); itr != map.constEnd(); ++itr) { + QString key = itr.key(); + QVariant var = itr.value(); + QVariant defaultVar = defaultMap[key]; + + QTreeWidgetItem *item = variantToItem(key, var, defaultVar); + if (item) { + items.append(item); + } + } +} + +static void +variantListToItems(const QVector &lst, + const QVector &defaultLst, + QList &items) +{ + for (int i = 0; i < lst.count(); ++i) { + QString key = QString::number(i); + QVariant var = lst[i]; + QVariant defaultVar; + + if (i < defaultLst.count()) { + defaultVar = defaultLst[i]; + } + + QTreeWidgetItem *item = variantToItem(key, var, defaultVar); + if (item) { + items.append(item); + } + } +} + +static bool +isVariantDeep(const QVariant &var) +{ + if (var.type() == QVariant::List) { + QVector lst = var.toList().toVector(); + for (int i = 0; i < lst.count(); ++i) { + if (isVariantDeep(lst[i])) { + return true; + } + } + return false; + } else if (var.type() == QVariant::Map) { + return true; + } else if (var.type() == QVariant::Hash) { + return true; + } else { + return false; + } +} + +static QTreeWidgetItem * +variantToItem(const QString &key, const QVariant &var, + const QVariant &defaultVar) +{ + if (var == defaultVar) { + return NULL; + } + + QString val; + + bool deep = isVariantDeep(var); + if (!deep) { + variantToString(var, val); + } + + //qDebug()<<"key = "<setText(1, descr); + QLabel *l = new QLabel(descr, tree); + l->setWordWrap(true); + tree->setItemWidget(item, 1, l); + + item->setData(0, Qt::UserRole, surface.base64Data()); +} + +void MainWindow::fillStateForFrame() +{ + if (!m_selectedEvent || !m_selectedEvent->hasState()) { + return; + } + + if (m_nonDefaultsLookupEvent) { + m_ui.nonDefaultsCB->blockSignals(true); + m_ui.nonDefaultsCB->setChecked(true); + m_ui.nonDefaultsCB->blockSignals(false); + } + + bool nonDefaults = m_ui.nonDefaultsCB->isChecked(); + QVariantMap defaultParams; + if (nonDefaults) { + ApiTraceState defaultState = m_trace->defaultState(); + defaultParams = defaultState.parameters(); + } + + const ApiTraceState &state = *m_selectedEvent->state(); + m_ui.stateTreeWidget->clear(); + QList items; + variantMapToItems(state.parameters(), defaultParams, items); + m_ui.stateTreeWidget->insertTopLevelItems(0, items); + + QMap shaderSources = state.shaderSources(); + if (shaderSources.isEmpty()) { + m_sourcesWidget->setShaders(shaderSources); + } else { + m_sourcesWidget->setShaders(shaderSources); + } + + m_ui.uniformsTreeWidget->clear(); + QList uniformsItems; + variantMapToItems(state.uniforms(), QVariantMap(), uniformsItems); + m_ui.uniformsTreeWidget->insertTopLevelItems(0, uniformsItems); + + const QList &textures = + state.textures(); + const QList &fbos = + state.framebuffers(); + + m_ui.surfacesTreeWidget->clear(); + if (textures.isEmpty() && fbos.isEmpty()) { + m_ui.surfacesTab->setDisabled(false); + } else { + m_ui.surfacesTreeWidget->setIconSize(QSize(THUMBNAIL_SIZE, THUMBNAIL_SIZE)); + if (!textures.isEmpty()) { + QTreeWidgetItem *textureItem = + new QTreeWidgetItem(m_ui.surfacesTreeWidget); + textureItem->setText(0, tr("Textures")); + if (textures.count() <= 6) { + textureItem->setExpanded(true); + } + + for (int i = 0; i < textures.count(); ++i) { + const ApiTexture &texture = + textures[i]; + addSurfaceItem(texture, texture.label(), + textureItem, + m_ui.surfacesTreeWidget); + } + } + if (!fbos.isEmpty()) { + QTreeWidgetItem *fboItem = + new QTreeWidgetItem(m_ui.surfacesTreeWidget); + fboItem->setText(0, tr("Framebuffers")); + if (fbos.count() <= 6) { + fboItem->setExpanded(true); + } + + for (int i = 0; i < fbos.count(); ++i) { + const ApiFramebuffer &fbo = + fbos[i]; + addSurfaceItem(fbo, fbo.type(), + fboItem, + m_ui.surfacesTreeWidget); + } + } + m_ui.surfacesTab->setEnabled(true); + } + m_ui.stateDock->show(); +} + +void MainWindow::showSettings() +{ + SettingsDialog dialog; + dialog.setFilterModel(m_proxyModel); + + dialog.exec(); +} + +void MainWindow::openHelp(const QUrl &url) +{ + QDesktopServices::openUrl(url); +} + +void MainWindow::showSurfacesMenu(const QPoint &pos) +{ + QTreeWidget *tree = m_ui.surfacesTreeWidget; + QTreeWidgetItem *item = tree->itemAt(pos); + if (!item) { + return; + } + + QMenu menu(tr("Surfaces"), this); + + QAction *act = menu.addAction(tr("View Image")); + act->setStatusTip(tr("View the currently selected surface")); + connect(act, SIGNAL(triggered()), + SLOT(showSelectedSurface())); + + act = menu.addAction(tr("Save Image")); + act->setStatusTip(tr("Save the currently selected surface")); + connect(act, SIGNAL(triggered()), + SLOT(saveSelectedSurface())); + + menu.exec(tree->viewport()->mapToGlobal(pos)); +} + +void MainWindow::showSelectedSurface() +{ + QTreeWidgetItem *item = + m_ui.surfacesTreeWidget->currentItem(); + + if (!item) { + return; + } + + ImageViewer *viewer = new ImageViewer(this); + + QString title; + if (selectedCall()) { + title = tr("QApiTrace - Surface at %1 (%2)") + .arg(selectedCall()->name()) + .arg(selectedCall()->index()); + } else { + title = tr("QApiTrace - Surface Viewer"); + } + viewer->setWindowTitle(title); + + viewer->setAttribute(Qt::WA_DeleteOnClose, true); + + QVariant var = item->data(0, Qt::UserRole); + QByteArray base64Data = var.value(); + viewer->setBase64Data(base64Data); + + viewer->show(); + viewer->raise(); + viewer->activateWindow(); +} + +void MainWindow::initObjects() +{ + m_ui.stateTreeWidget->sortByColumn(0, Qt::AscendingOrder); + m_ui.uniformsTreeWidget->sortByColumn(0, Qt::AscendingOrder); + + m_sourcesWidget = new ShadersSourceWidget(m_ui.shadersTab); + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(m_sourcesWidget); + m_ui.shadersTab->setLayout(layout); m_trace = new ApiTrace(); - connect(m_trace, SIGNAL(startedLoadingTrace()), - this, SLOT(startedLoadingTrace())); - connect(m_trace, SIGNAL(finishedLoadingTrace()), - this, SLOT(finishedLoadingTrace())); + m_retracer = new Retracer(this); + + m_vdataInterpreter = new VertexDataInterpreter(this); + m_vdataInterpreter->setListWidget(m_ui.vertexDataListWidget); + m_vdataInterpreter->setStride( + m_ui.vertexStrideSB->value()); + m_vdataInterpreter->setComponents( + m_ui.vertexComponentsSB->value()); + m_vdataInterpreter->setStartingOffset( + m_ui.startingOffsetSB->value()); + m_vdataInterpreter->setTypeFromString( + m_ui.vertexTypeCB->currentText()); m_model = new ApiTraceModel(); m_model->setApiTrace(m_trace); m_proxyModel = new ApiTraceFilter(); m_proxyModel->setSourceModel(m_model); m_ui.callView->setModel(m_proxyModel); - m_ui.callView->setItemDelegate(new ApiCallDelegate); - for (int column = 0; column < m_model->columnCount(); ++column) - m_ui.callView->resizeColumnToContents(column); - - QToolBar *toolBar = addToolBar(tr("Navigation")); - m_filterEdit = new QLineEdit(toolBar); - toolBar->addWidget(m_filterEdit); + m_ui.callView->setItemDelegate( + new ApiCallDelegate(m_ui.callView)); + m_ui.callView->resizeColumnToContents(0); + m_ui.callView->header()->swapSections(0, 1); + m_ui.callView->setColumnWidth(1, 42); + m_ui.callView->setContextMenuPolicy(Qt::CustomContextMenu); m_progressBar = new QProgressBar(); - m_progressBar->setRange(0, 0); + m_progressBar->setRange(0, 100); statusBar()->addPermanentWidget(m_progressBar); m_progressBar->hide(); + m_argsEditor = new ArgumentsEditor(this); + m_ui.detailsDock->hide(); + m_ui.backtraceDock->hide(); + m_ui.errorsDock->hide(); + m_ui.vertexDataDock->hide(); + m_ui.stateDock->hide(); + setDockOptions(dockOptions() | QMainWindow::ForceTabbedDocks); + + tabifyDockWidget(m_ui.stateDock, m_ui.vertexDataDock); + tabifyDockWidget(m_ui.detailsDock, m_ui.errorsDock); + tabifyDockWidget(m_ui.detailsDock, m_ui.backtraceDock); + + m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + + m_ui.detailsWebView->page()->setLinkDelegationPolicy( + QWebPage::DelegateExternalLinks); + + m_jumpWidget = new JumpWidget(this); + m_ui.centralLayout->addWidget(m_jumpWidget); + m_jumpWidget->hide(); + + m_searchWidget = new SearchWidget(this); + m_ui.centralLayout->addWidget(m_searchWidget); + m_searchWidget->hide(); + + m_traceProcess = new TraceProcess(this); + m_trimProcess = new TrimProcess(this); + + m_profileDialog = new ProfileDialog(); +} + +void MainWindow::initConnections() +{ + connect(m_trace, SIGNAL(startedLoadingTrace()), + this, SLOT(startedLoadingTrace())); + connect(m_trace, SIGNAL(loaded(int)), + this, SLOT(loadProgess(int))); + connect(m_trace, SIGNAL(finishedLoadingTrace()), + this, SLOT(finishedLoadingTrace())); + connect(m_trace, SIGNAL(startedSaving()), + this, SLOT(slotStartedSaving())); + connect(m_trace, SIGNAL(saved()), + this, SLOT(slotSaved())); + connect(m_trace, SIGNAL(changed(ApiTraceEvent*)), + this, SLOT(slotTraceChanged(ApiTraceEvent*))); + connect(m_trace, SIGNAL(findResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)), + this, SLOT(slotSearchResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*))); + connect(m_trace, SIGNAL(foundFrameStart(ApiTraceFrame*)), + this, SLOT(slotFoundFrameStart(ApiTraceFrame*))); + connect(m_trace, SIGNAL(foundFrameEnd(ApiTraceFrame*)), + this, SLOT(slotFoundFrameEnd(ApiTraceFrame*))); + connect(m_trace, SIGNAL(foundCallIndex(ApiTraceCall*)), + this, SLOT(slotJumpToResult(ApiTraceCall*))); + + connect(m_retracer, SIGNAL(finished(const QString&)), + this, SLOT(replayFinished(const QString&))); + connect(m_retracer, SIGNAL(error(const QString&)), + this, SLOT(replayError(const QString&))); + connect(m_retracer, SIGNAL(foundState(ApiTraceState*)), + this, SLOT(replayStateFound(ApiTraceState*))); + connect(m_retracer, SIGNAL(foundProfile(trace::Profile*)), + this, SLOT(replayProfileFound(trace::Profile*))); + connect(m_retracer, SIGNAL(foundThumbnails(const QList&)), + this, SLOT(replayThumbnailsFound(const QList&))); + connect(m_retracer, SIGNAL(retraceErrors(const QList&)), + this, SLOT(slotRetraceErrors(const QList&))); + + connect(m_ui.vertexInterpretButton, SIGNAL(clicked()), + m_vdataInterpreter, SLOT(interpretData())); + connect(m_ui.vertexTypeCB, SIGNAL(currentIndexChanged(const QString&)), + m_vdataInterpreter, SLOT(setTypeFromString(const QString&))); + connect(m_ui.vertexStrideSB, SIGNAL(valueChanged(int)), + m_vdataInterpreter, SLOT(setStride(int))); + connect(m_ui.vertexComponentsSB, SIGNAL(valueChanged(int)), + m_vdataInterpreter, SLOT(setComponents(int))); + connect(m_ui.startingOffsetSB, SIGNAL(valueChanged(int)), + m_vdataInterpreter, SLOT(setStartingOffset(int))); + + connect(m_ui.actionNew, SIGNAL(triggered()), + this, SLOT(createTrace())); connect(m_ui.actionOpen, SIGNAL(triggered()), this, SLOT(openTrace())); connect(m_ui.actionQuit, SIGNAL(triggered()), this, SLOT(close())); + connect(m_ui.actionFind, SIGNAL(triggered()), + this, SLOT(slotSearch())); + connect(m_ui.actionGo, SIGNAL(triggered()), + this, SLOT(slotGoTo())); + connect(m_ui.actionGoFrameStart, SIGNAL(triggered()), + this, SLOT(slotGoFrameStart())); + connect(m_ui.actionGoFrameEnd, SIGNAL(triggered()), + this, SLOT(slotGoFrameEnd())); + connect(m_ui.actionReplay, SIGNAL(triggered()), this, SLOT(replayStart())); + connect(m_ui.actionProfile, SIGNAL(triggered()), + this, SLOT(replayProfile())); connect(m_ui.actionStop, SIGNAL(triggered()), this, SLOT(replayStop())); connect(m_ui.actionLookupState, SIGNAL(triggered()), this, SLOT(lookupState())); + connect(m_ui.actionTrim, SIGNAL(triggered()), + this, SLOT(trim())); + connect(m_ui.actionShowThumbnails, SIGNAL(triggered()), + this, SLOT(showThumbnails())); + connect(m_ui.actionOptions, SIGNAL(triggered()), + this, SLOT(showSettings())); - connect(m_ui.callView, SIGNAL(activated(const QModelIndex &)), + connect(m_ui.callView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(callItemSelected(const QModelIndex &))); - connect(m_filterEdit, SIGNAL(returnPressed()), - this, SLOT(filterTrace())); + connect(m_ui.callView, SIGNAL(doubleClicked(const QModelIndex &)), + this, SLOT(callItemActivated(const QModelIndex &))); + connect(m_ui.callView, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(customContextMenuRequested(QPoint))); + + connect(m_ui.surfacesTreeWidget, + SIGNAL(customContextMenuRequested(const QPoint &)), + SLOT(showSurfacesMenu(const QPoint &))); + connect(m_ui.surfacesTreeWidget, + SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), + SLOT(showSelectedSurface())); + + connect(m_ui.detailsWebView, SIGNAL(linkClicked(const QUrl&)), + this, SLOT(openHelp(const QUrl&))); + + connect(m_ui.nonDefaultsCB, SIGNAL(toggled(bool)), + this, SLOT(fillState(bool))); + + connect(m_jumpWidget, SIGNAL(jumpTo(int)), + SLOT(slotJumpTo(int))); + + connect(m_searchWidget, + SIGNAL(searchNext(const QString&, Qt::CaseSensitivity)), + SLOT(slotSearchNext(const QString&, Qt::CaseSensitivity))); + connect(m_searchWidget, + SIGNAL(searchPrev(const QString&, Qt::CaseSensitivity)), + SLOT(slotSearchPrev(const QString&, Qt::CaseSensitivity))); + + connect(m_traceProcess, SIGNAL(tracedFile(const QString&)), + SLOT(createdTrace(const QString&))); + connect(m_traceProcess, SIGNAL(error(const QString&)), + SLOT(traceError(const QString&))); + + connect(m_trimProcess, SIGNAL(trimmedFile(const QString&)), + SLOT(createdTrim(const QString&))); + connect(m_trimProcess, SIGNAL(error(const QString&)), + SLOT(trimError(const QString&))); + + connect(m_ui.errorsDock, SIGNAL(visibilityChanged(bool)), + m_ui.actionShowErrorsDock, SLOT(setChecked(bool))); + connect(m_ui.actionShowErrorsDock, SIGNAL(triggered(bool)), + m_ui.errorsDock, SLOT(setVisible(bool))); + connect(m_ui.errorsTreeWidget, + SIGNAL(itemActivated(QTreeWidgetItem*, int)), + this, SLOT(slotErrorSelected(QTreeWidgetItem*))); + + connect(m_ui.actionShowProfileDialog, SIGNAL(triggered(bool)), + m_profileDialog, SLOT(show())); + connect(m_profileDialog, SIGNAL(jumpToCall(int)), + this, SLOT(slotJumpTo(int))); } -void MainWindow::openTrace() +void MainWindow::updateActionsState(bool traceLoaded, bool stopped) { - QString fileName = - QFileDialog::getOpenFileName( - this, - tr("Open Trace"), - QDir::homePath(), - tr("Trace Files (*.trace)")); + if (traceLoaded) { + /* Edit */ + m_ui.actionFind ->setEnabled(true); + m_ui.actionGo ->setEnabled(true); + m_ui.actionGoFrameStart ->setEnabled(true); + m_ui.actionGoFrameEnd ->setEnabled(true); - qDebug()<< "File name : " <setEnabled(false); + m_ui.actionReplay->setEnabled(true); + } + else { + m_ui.actionStop->setEnabled(true); + m_ui.actionReplay->setEnabled(false); + } - newTraceFile(fileName); + m_ui.actionProfile ->setEnabled(true); + m_ui.actionLookupState ->setEnabled(true); + m_ui.actionShowThumbnails->setEnabled(true); + m_ui.actionTrim ->setEnabled(true); + } + else { + /* Edit */ + m_ui.actionFind ->setEnabled(false); + m_ui.actionGo ->setEnabled(false); + m_ui.actionGoFrameStart ->setEnabled(false); + m_ui.actionGoFrameEnd ->setEnabled(false); + + /* Trace */ + m_ui.actionReplay ->setEnabled(false); + m_ui.actionProfile ->setEnabled(false); + m_ui.actionStop ->setEnabled(false); + m_ui.actionLookupState ->setEnabled(false); + m_ui.actionShowThumbnails->setEnabled(false); + m_ui.actionTrim ->setEnabled(false); + } } -void MainWindow::loadTrace(const QString &fileName) +void MainWindow::closeEvent(QCloseEvent * event) { - if (!QFile::exists(fileName)) { - QMessageBox::warning(this, tr("File Missing"), - tr("File '%1' doesn't exist.").arg(fileName)); - return; - } - qDebug()<< "Loading : " <close(); + QMainWindow::closeEvent(event); +} - m_progressBar->setValue(0); - newTraceFile(fileName); +void MainWindow::replayProfileFound(trace::Profile *profile) +{ + m_ui.actionShowProfileDialog->setEnabled(true); + m_profileDialog->setProfile(profile); + m_profileDialog->show(); + m_profileDialog->activateWindow(); + m_profileDialog->setFocus(); } -void MainWindow::callItemSelected(const QModelIndex &index) +void MainWindow::replayStateFound(ApiTraceState *state) { - ApiTraceCall *call = index.data().value(); - if (call) { - m_ui.detailsWebView->setHtml(call->toHtml()); - m_ui.detailsDock->show(); - m_currentFrame = call->parentFrame; + m_stateEvent->setState(state); + m_model->stateSetOnEvent(m_stateEvent); + if (m_selectedEvent == m_stateEvent || + m_nonDefaultsLookupEvent == m_selectedEvent) { + fillStateForFrame(); } else { - m_currentFrame = index.data().value(); - m_ui.detailsDock->hide(); + m_ui.stateDock->hide(); } + m_nonDefaultsLookupEvent = 0; } -void MainWindow::filterTrace() +void MainWindow::replayThumbnailsFound(const QList &thumbnails) { - m_proxyModel->setFilterString(m_filterEdit->text()); + m_ui.callView->setUniformRowHeights(false); + m_trace->bindThumbnailsToFrames(thumbnails); } -void MainWindow::replayStart() +void MainWindow::slotGoTo() { - replayTrace(false); + m_searchWidget->hide(); + m_jumpWidget->show(); } -void MainWindow::replayStop() +void MainWindow::slotJumpTo(int callNum) { - if (m_replayProcess) { - m_replayProcess->kill(); + m_trace->findCallIndex(callNum); +} - m_ui.actionStop->setEnabled(false); - m_ui.actionReplay->setEnabled(true); - m_ui.actionLookupState->setEnabled(true); - } +void MainWindow::createdTrace(const QString &path) +{ + qDebug()<<"Done tracing "<setFileName(fileName); + QMessageBox::warning( + this, + tr("Tracing Error"), + msg); +} - if (m_traceFileName.isEmpty()) { - m_ui.actionReplay->setEnabled(false); - m_ui.actionLookupState->setEnabled(false); - } else { - m_ui.actionReplay->setEnabled(true); - m_ui.actionLookupState->setEnabled(true); +void MainWindow::createdTrim(const QString &path) +{ + qDebug()<<"Done trimming "<hide(); + m_searchWidget->show(); +} + +void MainWindow::slotSearchNext(const QString &str, + Qt::CaseSensitivity sensitivity) +{ + ApiTraceCall *call = currentCall(); + ApiTraceFrame *frame = currentFrame(); + + Q_ASSERT(call || frame); + if (!frame) { + frame = call->parentFrame(); } + Q_ASSERT(frame); + + m_trace->findNext(frame, call, str, sensitivity); } -void MainWindow::replayFinished() +void MainWindow::slotSearchPrev(const QString &str, + Qt::CaseSensitivity sensitivity) { - m_ui.actionStop->setEnabled(false); - m_ui.actionReplay->setEnabled(true); - m_ui.actionLookupState->setEnabled(true); + ApiTraceCall *call = currentCall(); + ApiTraceFrame *frame = currentFrame(); - QByteArray output = m_replayProcess->readAllStandardOutput(); + Q_ASSERT(call || frame); + if (!frame) { + frame = call->parentFrame(); + } + Q_ASSERT(frame); -#if 1 - qDebug()<<"Process finished = "; - qDebug()<<"\terr = "<readAllStandardError(); - qDebug()<<"\tout = "<findPrev(frame, call, str, sensitivity); +} - if (m_findingState) { - qDebug()<<"json parse"; - } else if (output.length() < 80) { - statusBar()->showMessage(output); +void MainWindow::fillState(bool nonDefaults) +{ + if (nonDefaults) { + ApiTraceState defaultState = m_trace->defaultState(); + if (defaultState.isEmpty()) { + m_ui.nonDefaultsCB->blockSignals(true); + m_ui.nonDefaultsCB->setChecked(false); + m_ui.nonDefaultsCB->blockSignals(false); + ApiTraceFrame *firstFrame = + m_trace->frameAt(0); + if (!firstFrame) { + return; + } + if (!firstFrame->isLoaded()) { + m_trace->loadFrame(firstFrame); + return; + } + ApiTraceCall *firstCall = firstFrame->calls().first(); + ApiTraceEvent *oldSelected = m_selectedEvent; + m_nonDefaultsLookupEvent = m_selectedEvent; + m_selectedEvent = firstCall; + lookupState(); + m_selectedEvent = oldSelected; + } } + fillStateForFrame(); } -void MainWindow::replayError(QProcess::ProcessError err) +void MainWindow::customContextMenuRequested(QPoint pos) { - m_ui.actionStop->setEnabled(false); - m_ui.actionReplay->setEnabled(true); - m_ui.actionLookupState->setEnabled(true); - m_findingState = false; + QModelIndex index = m_ui.callView->indexAt(pos); - qDebug()<<"Process error = "<(); + if (!event) { + return; + } + + QMenu menu; + menu.addAction(QIcon(":/resources/media-record.png"), + tr("Lookup state"), this, SLOT(lookupState())); + if (event->type() == ApiTraceEvent::Call) { + menu.addAction(tr("Edit"), this, SLOT(editCall())); + } + + menu.exec(QCursor::pos()); } -void MainWindow::startedLoadingTrace() +void MainWindow::editCall() +{ + if (m_selectedEvent && m_selectedEvent->type() == ApiTraceEvent::Call) { + ApiTraceCall *call = static_cast(m_selectedEvent); + m_argsEditor->setCall(call); + m_argsEditor->show(); + } +} + +void MainWindow::slotStartedSaving() { - Q_ASSERT(m_trace); m_progressBar->show(); - QFileInfo info(m_trace->fileName()); statusBar()->showMessage( - tr("Loading %1...").arg(info.fileName())); + tr("Saving to %1").arg(m_trace->fileName())); } -void MainWindow::finishedLoadingTrace() +void MainWindow::slotSaved() { + statusBar()->showMessage( + tr("Saved to %1").arg(m_trace->fileName()), 2000); m_progressBar->hide(); - if (!m_trace) { - return; +} + +void MainWindow::slotGoFrameStart() +{ + ApiTraceFrame *frame = currentFrame(); + ApiTraceCall *call = currentCall(); + + if (!frame && call) { + frame = call->parentFrame(); } - QFileInfo info(m_trace->fileName()); - statusBar()->showMessage( - tr("Loaded %1").arg(info.fileName()), 3000); + + m_trace->findFrameStart(frame); +} + +void MainWindow::slotGoFrameEnd() +{ + ApiTraceFrame *frame = currentFrame(); + ApiTraceCall *call = currentCall(); + + if (!frame && call) { + frame = call->parentFrame(); + } + + m_trace->findFrameEnd(frame); +} + +ApiTraceFrame * MainWindow::selectedFrame() const +{ + if (m_selectedEvent) { + if (m_selectedEvent->type() == ApiTraceEvent::Frame) { + return static_cast(m_selectedEvent); + } else { + Q_ASSERT(m_selectedEvent->type() == ApiTraceEvent::Call); + ApiTraceCall *call = static_cast(m_selectedEvent); + return call->parentFrame(); + } + } + return NULL; } -void MainWindow::replayTrace(bool dumpState) +void MainWindow::slotTraceChanged(ApiTraceEvent *event) { - if (!m_replayProcess) { -#ifdef Q_OS_WIN - QString format = QLatin1String("%1;"); -#else - QString format = QLatin1String("%1:"); -#endif - QString buildPath = format.arg(BUILD_DIR); - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert("PATH", buildPath + env.value("PATH")); + Q_ASSERT(event); + if (event == m_selectedEvent) { + if (event->type() == ApiTraceEvent::Call) { + ApiTraceCall *call = static_cast(event); + m_ui.detailsWebView->setHtml(call->toHtml()); + } + } +} + +void MainWindow::slotRetraceErrors(const QList &errors) +{ + m_ui.errorsTreeWidget->clear(); + + foreach(ApiTraceError error, errors) { + m_trace->setCallError(error); - qputenv("PATH", env.value("PATH").toLatin1()); + QTreeWidgetItem *item = + new QTreeWidgetItem(m_ui.errorsTreeWidget); + item->setData(0, Qt::DisplayRole, error.callIndex); + item->setData(0, Qt::UserRole, error.callIndex); + QString type = error.type; + type[0] = type[0].toUpper(); + item->setData(1, Qt::DisplayRole, type); + item->setData(2, Qt::DisplayRole, error.message); + } +} - m_replayProcess = new QProcess(this); - m_replayProcess->setProcessEnvironment(env); +void MainWindow::slotErrorSelected(QTreeWidgetItem *current) +{ + if (current) { + int callIndex = + current->data(0, Qt::UserRole).toInt(); + m_trace->findCallIndex(callIndex); + } +} - connect(m_replayProcess, SIGNAL(finished(int, QProcess::ExitStatus)), - this, SLOT(replayFinished())); - connect(m_replayProcess, SIGNAL(error(QProcess::ProcessError)), - this, SLOT(replayError(QProcess::ProcessError))); +ApiTraceCall * MainWindow::selectedCall() const +{ + if (m_selectedEvent && + m_selectedEvent->type() == ApiTraceEvent::Call) { + return static_cast(m_selectedEvent); } + return NULL; +} - if (m_traceFileName.isEmpty()) +void MainWindow::saveSelectedSurface() +{ + QTreeWidgetItem *item = + m_ui.surfacesTreeWidget->currentItem(); + + if (!item || !m_trace) { return; + } + + QVariant var = item->data(0, Qt::UserRole); + QImage img = var.value(); - QStringList arguments; - if (dumpState && - m_currentFrame && !m_currentFrame->calls.isEmpty()) { - arguments << QLatin1String("-D"); - arguments << QString::number(m_currentFrame->calls.first()->index); + QString imageIndex; + if (selectedCall()) { + imageIndex = tr("_call_%1") + .arg(selectedCall()->index()); + } else if (selectedFrame()) { + ApiTraceCall *firstCall = selectedFrame()->call(0); + if (firstCall) { + imageIndex = tr("_frame_%1") + .arg(firstCall->index()); + } else { + qDebug()<<"unknown frame number"; + imageIndex = tr("_frame_%1") + .arg(firstCall->index()); + } + } + + //which of the surfaces are we saving + QTreeWidgetItem *parent = item->parent(); + int parentIndex = + m_ui.surfacesTreeWidget->indexOfTopLevelItem(parent); + if (parentIndex < 0) { + parentIndex = 0; + } + int childIndex = 0; + if (parent) { + childIndex = parent->indexOfChild(item); + } else { + childIndex = m_ui.surfacesTreeWidget->indexOfTopLevelItem(item); } - arguments << m_traceFileName; - m_replayProcess->start(QLatin1String("glretrace"), - arguments); - m_ui.actionStop->setEnabled(true); + QString fileName = + tr("%1%2-%3_%4.png") + .arg(m_trace->fileName()) + .arg(imageIndex) + .arg(parentIndex) + .arg(childIndex); + //qDebug()<<"save "<showMessage( tr("Saved '%1'").arg(fileName), 5000); } -void MainWindow::lookupState() +void MainWindow::loadProgess(int percent) { - if (!m_currentFrame) { - QMessageBox::warning( - this, tr("Unknown Frame"), - tr("To inspect the state select a frame in the trace.")); + m_progressBar->setValue(percent); +} + +void MainWindow::slotSearchResult(const ApiTrace::SearchRequest &request, + ApiTrace::SearchResult result, + ApiTraceCall *call) +{ + switch (result) { + case ApiTrace::SearchResult_NotFound: + m_searchWidget->setFound(false); + break; + case ApiTrace::SearchResult_Found: { + QModelIndex index = m_proxyModel->indexForCall(call); + + if (index.isValid()) { + m_ui.callView->setCurrentIndex(index); + m_searchWidget->setFound(true); + } else { + //call is filtered out, so continue searching but from the + // filtered call + if (!call) { + qDebug()<<"Error: search success with no call"; + return; + } +// qDebug()<<"filtered! search from "<searchText() +// <<", call idx = "<index(); + + if (request.direction == ApiTrace::SearchRequest::Next) { + m_trace->findNext(call->parentFrame(), call, + request.text, request.cs); + } else { + m_trace->findNext(call->parentFrame(), call, + request.text, request.cs); + } + } + } + break; + case ApiTrace::SearchResult_Wrapped: + m_searchWidget->setFound(false); + break; + } +} + +ApiTraceFrame * MainWindow::currentFrame() const +{ + QModelIndex index = m_ui.callView->currentIndex(); + ApiTraceEvent *event = 0; + + if (!index.isValid()) { + index = m_proxyModel->index(0, 0, QModelIndex()); + if (!index.isValid()) { + qDebug()<<"no currently valid index"; + return 0; + } + } + + event = index.data(ApiTraceModel::EventRole).value(); + Q_ASSERT(event); + if (!event) { + return 0; + } + + ApiTraceFrame *frame = 0; + if (event->type() == ApiTraceCall::Frame) { + frame = static_cast(event); + } + return frame; +} + +ApiTraceCall * MainWindow::currentCall() const +{ + QModelIndex index = m_ui.callView->currentIndex(); + ApiTraceEvent *event = 0; + + if (!index.isValid()) { + index = m_proxyModel->index(0, 0, QModelIndex()); + if (!index.isValid()) { + qDebug()<<"no currently valid index"; + return 0; + } + } + + event = index.data(ApiTraceModel::EventRole).value(); + Q_ASSERT(event); + if (!event) { + return 0; + } + + ApiTraceCall *call = 0; + if (event->type() == ApiTraceCall::Call) { + call = static_cast(event); + } + + return call; + +} + +void MainWindow::slotFoundFrameStart(ApiTraceFrame *frame) +{ + Q_ASSERT(frame->isLoaded()); + if (!frame || frame->isEmpty()) { return; } - m_findingState = true; - replayTrace(true); + + QVector::const_iterator itr; + QVector calls = frame->calls(); + + itr = calls.constBegin(); + while (itr != calls.constEnd()) { + ApiTraceCall *call = *itr; + QModelIndex idx = m_proxyModel->indexForCall(call); + if (idx.isValid()) { + m_ui.callView->setCurrentIndex(idx); + m_ui.callView->scrollTo(idx, QAbstractItemView::PositionAtTop); + break; + } + ++itr; + } +} + +void MainWindow::slotFoundFrameEnd(ApiTraceFrame *frame) +{ + Q_ASSERT(frame->isLoaded()); + if (!frame || frame->isEmpty()) { + return; + } + QVector::const_iterator itr; + QVector calls = frame->calls(); + + itr = calls.constEnd(); + do { + --itr; + ApiTraceCall *call = *itr; + QModelIndex idx = m_proxyModel->indexForCall(call); + if (idx.isValid()) { + m_ui.callView->setCurrentIndex(idx); + m_ui.callView->scrollTo(idx, QAbstractItemView::PositionAtBottom); + break; + } + } while (itr != calls.constBegin()); +} + +void MainWindow::slotJumpToResult(ApiTraceCall *call) +{ + QModelIndex idx = m_proxyModel->indexForCall(call); + if (idx.isValid()) { + activateWindow(); + m_ui.callView->setFocus(); + m_ui.callView->setCurrentIndex(idx); + m_ui.callView->scrollTo(idx, QAbstractItemView::PositionAtCenter); + } else { + statusBar()->showMessage(tr("Call has been filtered out.")); + } } #include "mainwindow.moc"