1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 **************************************************************************/
26 #include <QFileDialog>
27 #include <QHBoxLayout>
28 #include <QItemSelection>
30 #include <QSortFilterProxyModel>
31 #include <QSpacerItem>
32 #include <QToolButton>
33 #include <QMessageBox>
35 #include "ui_vogleditor.h"
36 #include "vogleditor.h"
38 #include "vogleditor_qapicalltreemodel.h"
39 #include "vogleditor_apicalltimelinemodel.h"
41 #include "vogl_platform.h"
42 #include "vogl_assert.h"
43 #include "vogl_file_utils.h"
44 #include "vogl_find_files.h"
46 #include "vogl_texture_format.h"
47 #include "vogl_trace_file_reader.h"
48 #include "vogleditor_qstatetreemodel.h"
49 #include "vogleditor_statetreetextureitem.h"
50 #include "vogleditor_statetreeprogramitem.h"
51 #include "vogleditor_statetreeshaderitem.h"
52 #include "vogleditor_statetreeframebufferitem.h"
53 #include "vogleditor_qtextureexplorer.h"
55 #define VOGLEDITOR_DISABLE_TAB(tab) ui->tabWidget->setTabEnabled(ui->tabWidget->indexOf(tab), false);
56 #define VOGLEDITOR_ENABLE_TAB(tab) ui->tabWidget->setTabEnabled(ui->tabWidget->indexOf(tab), true);
58 //----------------------------------------------------------------------------------------------------------------------
60 //----------------------------------------------------------------------------------------------------------------------
61 static void *g_actual_libgl_module_handle;
62 static QString g_PROJECT_NAME = "Vogl Editor";
64 //----------------------------------------------------------------------------------------------------------------------
65 // vogl_get_proc_address_helper
66 //----------------------------------------------------------------------------------------------------------------------
67 static vogl_void_func_ptr_t vogl_get_proc_address_helper(const char *pName)
71 vogl_void_func_ptr_t pFunc = g_actual_libgl_module_handle ? reinterpret_cast<vogl_void_func_ptr_t>(dlsym(g_actual_libgl_module_handle, pName)) : NULL;
73 if ((!pFunc) && (GL_ENTRYPOINT(glXGetProcAddress)))
74 pFunc = reinterpret_cast<vogl_void_func_ptr_t>( GL_ENTRYPOINT(glXGetProcAddress)(reinterpret_cast<const GLubyte*>(pName)) );
80 //----------------------------------------------------------------------------------------------------------------------
82 //----------------------------------------------------------------------------------------------------------------------
87 g_actual_libgl_module_handle = dlopen("libGL.so.1", RTLD_LAZY);
88 if (!g_actual_libgl_module_handle)
90 vogl_error_printf("%s: Failed loading libGL.so.1!\n", VOGL_FUNCTION_NAME);
94 GL_ENTRYPOINT(glXGetProcAddress) = reinterpret_cast<glXGetProcAddress_func_ptr_t>(dlsym(g_actual_libgl_module_handle, "glXGetProcAddress"));
95 if (!GL_ENTRYPOINT(glXGetProcAddress))
97 vogl_error_printf("%s: Failed getting address of glXGetProcAddress() from libGL.so.1!\n", VOGL_FUNCTION_NAME);
104 VoglEditor::VoglEditor(QWidget *parent) :
106 ui(new Ui::VoglEditor),
108 m_framebufferExplorer(NULL),
109 m_textureExplorer(NULL),
110 m_renderbufferExplorer(NULL),
111 m_programExplorer(NULL),
112 m_shaderExplorer(NULL),
114 m_currentSnapshot(NULL),
115 m_pCurrentCallTreeItem(NULL),
117 m_pPauseButton(NULL),
120 m_pTraceReader(NULL),
121 m_pApicallTreeModel(NULL)
127 vogl_init_actual_gl_entrypoints(vogl_get_proc_address_helper);
130 m_statusLabel = new QLabel(ui->statusBar);
131 m_statusLabel->setBaseSize(150, 12);
132 ui->statusBar->addWidget(m_statusLabel, 1);
134 // setup framebuffer tab
135 QGridLayout* framebufferTab_layout = new QGridLayout;
136 m_framebufferExplorer = new vogleditor_QFramebufferExplorer(ui->framebufferTab);
137 framebufferTab_layout->addWidget(m_framebufferExplorer, 0, 0);
138 ui->framebufferTab->setLayout(framebufferTab_layout);
141 QGridLayout* textureTab_layout = new QGridLayout;
142 m_textureExplorer = new vogleditor_QTextureExplorer(ui->textureTab);
143 textureTab_layout->addWidget(m_textureExplorer, 0, 0);
144 ui->textureTab->setLayout(textureTab_layout);
146 // setup renderbuffer tab
147 QGridLayout* rbTab_layout = new QGridLayout;
148 m_renderbufferExplorer = new vogleditor_QTextureExplorer(ui->renderbufferTab);
149 rbTab_layout->addWidget(m_renderbufferExplorer, 0, 0);
150 ui->renderbufferTab->setLayout(rbTab_layout);
153 QGridLayout* programTab_layout = new QGridLayout;
154 m_programExplorer = new vogleditor_QProgramExplorer(ui->programTab);
155 programTab_layout->addWidget(m_programExplorer, 0, 0);
156 ui->programTab->setLayout(programTab_layout);
159 QGridLayout* shaderTab_layout = new QGridLayout;
160 m_shaderExplorer = new vogleditor_QShaderExplorer(ui->shaderTab);
161 shaderTab_layout->addWidget(m_shaderExplorer, 0, 0);
162 ui->shaderTab->setLayout(shaderTab_layout);
165 m_timeline = new vogleditor_QTimelineView();
166 m_timeline->setMinimumHeight(100);
167 ui->verticalLayout->addWidget(m_timeline);
168 ui->verticalLayout->removeWidget(ui->timelineViewPlaceholder);
170 // add buttons to toolbar
171 m_pPlayButton = new QToolButton(ui->mainToolBar);
172 m_pPlayButton->setText("Play trace");
173 m_pPlayButton->setEnabled(false);
175 m_pPauseButton = new QToolButton(ui->mainToolBar);
176 m_pPauseButton->setText("Pause");
177 m_pPauseButton->setEnabled(false);
179 m_pTrimButton = new QToolButton(ui->mainToolBar);
180 m_pTrimButton->setText("Trim");
181 m_pTrimButton->setEnabled(false);
183 m_pStopButton = new QToolButton(ui->mainToolBar);
184 m_pStopButton->setText("Stop");
185 m_pStopButton->setEnabled(false);
187 // Temporarily hide the other buttons (until asyncronous playback is supported)
188 m_pPauseButton->setVisible(false);
189 m_pTrimButton->setVisible(false);
190 m_pStopButton->setVisible(false);
192 ui->mainToolBar->addWidget(m_pPlayButton);
193 ui->mainToolBar->addWidget(m_pPauseButton);
194 ui->mainToolBar->addWidget(m_pTrimButton);
195 ui->mainToolBar->addWidget(m_pStopButton);
197 connect(m_pPlayButton, SIGNAL(clicked()), this, SLOT(playCurrentTraceFile()));
198 connect(m_pPauseButton, SIGNAL(clicked()), this, SLOT(pauseCurrentTraceFile()));
199 connect(m_pTrimButton, SIGNAL(clicked()), this, SLOT(trimCurrentTraceFile()));
200 connect(m_pStopButton, SIGNAL(clicked()), this, SLOT(stopCurrentTraceFile()));
202 connect(m_programExplorer, SIGNAL(program_edited(vogl_program_state*)), this, SLOT(on_program_edited(vogl_program_state*)));
204 reset_tracefile_ui();
207 VoglEditor::~VoglEditor()
212 if (m_textureExplorer != NULL)
214 delete m_textureExplorer;
215 m_textureExplorer = NULL;
218 if (m_renderbufferExplorer != NULL)
220 delete m_renderbufferExplorer;
221 m_renderbufferExplorer = NULL;
224 if (m_programExplorer != NULL)
226 delete m_programExplorer;
227 m_programExplorer = NULL;
230 if (m_shaderExplorer != NULL)
232 delete m_shaderExplorer;
233 m_shaderExplorer = NULL;
237 void VoglEditor::playCurrentTraceFile()
239 QCursor origCursor = cursor();
240 setCursor(Qt::WaitCursor);
243 m_pPlayButton->setEnabled(false);
244 m_pPauseButton->setEnabled(true);
245 m_pTrimButton->setEnabled(true);
246 m_pStopButton->setEnabled(true);
247 m_statusLabel->clear();
249 if (m_traceReplayer.replay(m_pTraceReader, m_pApicallTreeModel->root(), NULL, 0, true))
251 // replay was successful
252 m_pPlayButton->setEnabled(true);
253 m_pPauseButton->setEnabled(false);
254 m_pTrimButton->setEnabled(false);
255 m_pStopButton->setEnabled(false);
259 m_statusLabel->setText("Failed to replay the trace.");
262 setCursor(origCursor);
265 void VoglEditor::pauseCurrentTraceFile()
267 if (m_traceReplayer.pause())
270 m_pPlayButton->setEnabled(true);
271 m_pPauseButton->setEnabled(false);
272 m_pTrimButton->setEnabled(true);
273 m_pStopButton->setEnabled(true);
274 m_statusLabel->clear();
278 m_statusLabel->setText("Failed to pause the trace replay.");
282 void VoglEditor::trimCurrentTraceFile()
284 if (m_traceReplayer.trim())
286 m_statusLabel->clear();
290 m_statusLabel->setText("Failed to trim the trace replay.");
294 void VoglEditor::stopCurrentTraceFile()
296 if (m_traceReplayer.stop())
299 m_pPlayButton->setEnabled(true);
300 m_pPauseButton->setEnabled(false);
301 m_pTrimButton->setEnabled(false);
302 m_pStopButton->setEnabled(false);
303 m_statusLabel->clear();
307 m_statusLabel->setText("Failed to stop the trace replay.");
311 void VoglEditor::on_actionE_xit_triggered()
316 void VoglEditor::on_action_Open_triggered()
318 QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QString(),
319 tr("GLI Binary Files (*.bin);;JSON Files (*.json)"));
321 if (!fileName.isEmpty()) {
322 vogl::dynamic_string filename;
323 filename.set(fileName.toStdString().c_str());
325 if (open_trace_file(filename) == false) {
326 QMessageBox::critical(this, tr("Error"), tr("Could not open file"));
332 void VoglEditor::on_action_Close_triggered()
337 void VoglEditor::close_trace_file()
339 if (m_pTraceReader != NULL)
341 m_pTraceReader->close();
342 m_pTraceReader = NULL;
344 setWindowTitle(g_PROJECT_NAME);
346 m_openFilename.clear();
347 m_backtraceToJsonMap.clear();
348 m_backtraceDoc.clear();
349 m_searchApicallResults.clear();
351 reset_tracefile_ui();
353 ui->treeView->setModel(NULL);
354 ui->machineInfoText->clear();
355 ui->backtraceText->clear();
356 m_timeline->setModel(NULL);
357 m_timeline->repaint();
359 if (m_pTimelineModel != NULL)
361 delete m_pTimelineModel;
362 m_pTimelineModel = NULL;
367 void VoglEditor::write_child_api_calls(vogleditor_apiCallTreeItem* pItem, FILE* pFile)
369 QString string = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole).toString();
370 vogl_fwrite(string.toStdString().c_str(), 1, string.size(), pFile);
371 vogl_fwrite("\r\n", 1, 2, pFile);
373 for (int i = 0; i < pItem->childCount(); i++)
375 write_child_api_calls(pItem->child(i), pFile);
379 void VoglEditor::on_actionExport_API_Calls_triggered()
381 QString suggestedName = m_openFilename;
383 int lastIndex = suggestedName.lastIndexOf('-');
386 suggestedName = suggestedName.remove(lastIndex, suggestedName.size() - lastIndex);
388 suggestedName += "-ApiCalls.txt";
390 QString fileName = QFileDialog::getSaveFileName(this, tr("Export API Calls"), suggestedName,
393 if (!fileName.isEmpty())
395 vogl::dynamic_string filename;
396 filename.set(fileName.toStdString().c_str());
398 FILE* pFile = vogl_fopen(filename.c_str(), "w");
399 vogleditor_QApiCallTreeModel* pModel = static_cast<vogleditor_QApiCallTreeModel*>(ui->treeView->model());
400 vogleditor_apiCallTreeItem* pRoot = pModel->root();
401 for (int i = 0; i < pRoot->childCount(); i++)
403 write_child_api_calls(pRoot->child(i), pFile);
409 //----------------------------------------------------------------------------------------------------------------------
410 // read_state_snapshot_from_trace
411 //----------------------------------------------------------------------------------------------------------------------
412 vogl_gl_state_snapshot* VoglEditor::read_state_snapshot_from_trace(vogl_trace_file_reader* pTrace_reader)
414 vogl_ctypes trace_gl_ctypes(pTrace_reader->get_sof_packet().m_pointer_sizes);
416 vogl_trace_packet keyframe_trace_packet(&trace_gl_ctypes);
418 pTrace_reader->seek_to_frame(0);
420 vogl_gl_state_snapshot *pSnapshot = NULL;
421 bool found_snapshot = false;
424 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
426 if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
428 vogl_error_printf("%s: Failed reading from keyframe trace file!\n", VOGL_FUNCTION_NAME);
432 if ((read_status == vogl_trace_file_reader::cEOF) || (pTrace_reader->get_packet_type() == cTSPTEOF))
434 vogl_error_printf("%s: Failed finding state snapshot in keyframe file!\n", VOGL_FUNCTION_NAME);
438 if (pTrace_reader->get_packet_type() != cTSPTGLEntrypoint)
441 if (!keyframe_trace_packet.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
443 vogl_error_printf("%s: Failed parsing GL entrypoint packet in keyframe file\n", VOGL_FUNCTION_NAME);
447 const vogl_trace_gl_entrypoint_packet *pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
448 gl_entrypoint_id_t entrypoint_id = static_cast<gl_entrypoint_id_t>(pGL_packet->m_entrypoint_id);
450 if (vogl_is_swap_buffers_entrypoint(entrypoint_id) || vogl_is_draw_entrypoint(entrypoint_id) || vogl_is_make_current_entrypoint(entrypoint_id))
452 vogl_error_printf("Failed finding state snapshot in keyframe file!\n");
456 switch (entrypoint_id)
458 case VOGL_ENTRYPOINT_glInternalTraceCommandRAD:
460 GLuint cmd = keyframe_trace_packet.get_param_value<GLuint>(0);
461 GLuint size = keyframe_trace_packet.get_param_value<GLuint>(1); VOGL_NOTE_UNUSED(size);
463 if (cmd == cITCRKeyValueMap)
465 key_value_map &kvm = keyframe_trace_packet.get_key_value_map();
467 dynamic_string cmd_type(kvm.get_string("command_type"));
468 if (cmd_type == "state_snapshot")
470 dynamic_string id(kvm.get_string("binary_id"));
473 vogl_error_printf("%s: Missing binary_id field in glInternalTraceCommandRAD key_valye_map command type: \"%s\"\n", VOGL_FUNCTION_NAME, cmd_type.get_ptr());
477 uint8_vec snapshot_data;
479 timed_scope ts("get_multi_blob_manager().get");
480 if (!pTrace_reader->get_multi_blob_manager().get(id, snapshot_data) || (snapshot_data.is_empty()))
482 vogl_error_printf("%s: Failed reading snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
487 vogl_message_printf("%s: Deserializing state snapshot \"%s\", %u bytes\n", VOGL_FUNCTION_NAME, id.get_ptr(), snapshot_data.size());
491 timed_scope ts("doc.binary_deserialize");
492 if (!doc.binary_deserialize(snapshot_data) || (!doc.get_root()))
494 vogl_error_printf("%s: Failed deserializing JSON snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
499 pSnapshot = vogl_new(vogl_gl_state_snapshot);
501 timed_scope ts("pSnapshot->deserialize");
502 if (!pSnapshot->deserialize(*doc.get_root(), pTrace_reader->get_multi_blob_manager(), &trace_gl_ctypes))
504 vogl_delete(pSnapshot);
507 vogl_error_printf("%s: Failed deserializing snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
511 found_snapshot = true;
520 } while (!found_snapshot);
526 bool VoglEditor::open_trace_file(dynamic_string filename)
528 QCursor origCursor = this->cursor();
529 this->setCursor(Qt::WaitCursor);
531 vogl_loose_file_blob_manager file_blob_manager;
532 dynamic_string keyframe_trace_path(file_utils::get_pathname(filename.get_ptr()));
533 file_blob_manager.init(cBMFReadable, keyframe_trace_path.get_ptr());
535 dynamic_string actual_keyframe_filename;
537 vogl_trace_file_reader* tmpReader = vogl_open_trace_file(filename, actual_keyframe_filename, NULL);
539 if (tmpReader == NULL)
541 m_statusLabel->setText("Failed to open: ");
542 m_statusLabel->setText(m_statusLabel->text().append(filename.c_str()));
543 this->setCursor(origCursor);
548 m_statusLabel->clear();
551 // now that we know the new trace file can be opened,
552 // close the old one, and update the trace reader
554 m_pTraceReader = tmpReader;
556 m_pApicallTreeModel = new vogleditor_QApiCallTreeModel(m_pTraceReader);
557 ui->treeView->setModel(m_pApicallTreeModel);
559 if (ui->treeView->selectionModel() != NULL)
561 //connect(ui->treeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(on_treeView_selectionChanged(const QItemSelection&, const QItemSelection&)));
562 connect(ui->treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(on_treeView_currentChanged(const QModelIndex &, const QModelIndex &)));
565 if (m_pApicallTreeModel->hasChildren())
567 ui->treeView->setExpanded(m_pApicallTreeModel->index(0,0), true);
568 ui->treeView->setCurrentIndex(m_pApicallTreeModel->index(0,0));
571 int flagsColumnWidth = 30;
572 ui->treeView->header()->setMinimumSectionSize(flagsColumnWidth);
573 ui->treeView->header()->moveSection(VOGL_ACTC_FLAGS, 0);
574 ui->treeView->setColumnWidth(VOGL_ACTC_FLAGS, flagsColumnWidth);
576 int width = ui->treeView->width() - flagsColumnWidth - 30; // subtract a little extra for the scrollbar width
577 ui->treeView->setColumnWidth(VOGL_ACTC_APICALL, width * 0.7);
578 ui->treeView->setColumnWidth(VOGL_ACTC_INDEX, width * 0.15);
579 ui->treeView->setColumnWidth(VOGL_ACTC_DURATION, width * 0.15);
581 ui->searchTextBox->setEnabled(true);
582 ui->searchPrevButton->setEnabled(true);
583 ui->searchNextButton->setEnabled(true);
585 ui->action_Close->setEnabled(true);
586 ui->actionExport_API_Calls->setEnabled(true);
588 ui->prevSnapshotButton->setEnabled(true);
589 ui->nextSnapshotButton->setEnabled(true);
590 ui->prevDrawcallButton->setEnabled(true);
591 ui->nextDrawcallButton->setEnabled(true);
593 m_backtraceToJsonMap.clear();
594 m_backtraceDoc.clear();
596 // Extract backtrace map and machine info from trace archive
597 if (m_pTraceReader->get_archive_blob_manager().is_initialized())
600 uint8_vec backtrace_data;
601 bool bBacktraceVisible = false;
602 if (m_pTraceReader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME))
604 //$ TODO mikesart: read MAP_SYMS data here when symbols have been resolved.
605 if (m_pTraceReader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME, backtrace_data))
607 json_node* pRoot = m_backtraceDoc.get_root();
608 if (m_backtraceDoc.deserialize((const char*)backtrace_data.get_ptr(), backtrace_data.size()))
610 bBacktraceVisible = pRoot->size() > 0;
611 for (uint i = 0; i < pRoot->size(); i++)
613 json_node* pChild = pRoot->get_child(i);
615 VOGL_ASSERT("Backtrace node does not have an 'index' child" && pChild != NULL && pChild->get_value_as_uint32("index", index));
616 if (pChild != NULL && pChild->get_value_as_uint32("index", index))
618 m_backtraceToJsonMap.insert(index, pChild);
625 QList<int> backtraceSplitterSizes = ui->splitter_3->sizes();
626 int backtraceSplitterTotalSize = backtraceSplitterSizes[0] + backtraceSplitterSizes[1];
627 QList<int> newBacktraceSplitterSizes;
628 if (!bBacktraceVisible)
630 newBacktraceSplitterSizes.append(backtraceSplitterTotalSize);
631 newBacktraceSplitterSizes.append(0);
632 ui->splitter_3->setSizes(newBacktraceSplitterSizes);
636 newBacktraceSplitterSizes << (backtraceSplitterTotalSize * 0.75)
637 << (backtraceSplitterTotalSize * 0.25);
638 ui->splitter_3->setSizes(newBacktraceSplitterSizes);
642 displayMachineInfo();
645 m_openFilename = filename.c_str();
647 setWindowTitle(m_openFilename + " - " + g_PROJECT_NAME);
649 ui->tabWidget->setCurrentWidget(ui->framebufferTab);
652 m_pPlayButton->setEnabled(true);
653 m_pPauseButton->setEnabled(false);
654 m_pTrimButton->setEnabled(false);
655 m_pStopButton->setEnabled(false);
658 m_pTimelineModel = new vogleditor_apiCallTimelineModel(m_pApicallTreeModel->root());
659 m_timeline->setModel(m_pTimelineModel);
660 m_timeline->repaint();
662 this->setCursor(origCursor);
666 void VoglEditor::displayMachineInfoHelper(QString prefix, const QString& sectionKeyStr, const vogl::json_value& value, QString& rMachineInfoStr)
668 if (value.is_array())
670 const json_node* pNode = value.get_node_ptr();
671 for (uint element = 0; element < pNode->size(); element++)
673 dynamic_string elementStr = pNode->get_value(element).as_string();
675 elementStr = elementStr.replace("\n", "\n\t");
677 rMachineInfoStr += "\t";
678 rMachineInfoStr += elementStr.get_ptr();
679 rMachineInfoStr += "\n";
682 rMachineInfoStr += "\n";
684 else if (value.is_node())
686 // Check if this is the modoule list.
687 bool is_module_list = (sectionKeyStr == "module_list");
688 const json_node* pNode = value.get_node_ptr();
690 for (uint i = 0; i < pNode->size(); i++)
692 dynamic_string key = pNode->get_key(i);
693 const json_value &value2 = pNode->get_value(i);
695 rMachineInfoStr += prefix;
696 // If it's the module list, then the key is the filename and we want to display that last.
698 rMachineInfoStr += key.c_str();
700 if (value2.is_array())
702 const json_node* pNode2 = value2.get_node_ptr();
704 // If this it module_list, then we get these items: base address, address size, uuid
705 // Check in btrace_get_machine_info() to see what's written there.
706 for (uint element = 0; element < pNode2->size(); element++)
708 const json_value &json_val = pNode2->get_value(element);
710 if (json_val.is_string())
712 dynamic_string str = pNode2->get_value(element).as_string();
713 rMachineInfoStr += str.c_str();
718 buf.format("%" PRIx64, json_val.as_uint64());
719 rMachineInfoStr += buf.c_str();
722 rMachineInfoStr += "\t";
727 rMachineInfoStr += ": ";
728 rMachineInfoStr += value2.as_string_ptr();
731 // Display the filename if this is the module_list.
733 rMachineInfoStr += key.c_str();
734 rMachineInfoStr += "\n";
737 rMachineInfoStr += "\n";
739 else if (value.is_string())
741 rMachineInfoStr += value.as_string_ptr();
745 rMachineInfoStr += value.as_string_ptr();
749 void VoglEditor::displayMachineInfo()
751 VOGL_ASSERT(m_pTraceReader != NULL);
753 bool bMachineInfoVisible = false;
754 if (m_pTraceReader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME))
756 uint8_vec machine_info_data;
757 if (m_pTraceReader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME, machine_info_data))
759 bMachineInfoVisible = true;
761 json_node *pRoot = doc.get_root();
762 if (doc.deserialize((const char*)machine_info_data.get_ptr(), machine_info_data.size()))
765 for (uint i = 0; i < pRoot->size(); i++)
767 dynamic_string sectionKeyStr = pRoot->get_key(i);
768 text += pRoot->get_key(i).c_str();
771 QString keyStr = sectionKeyStr.c_str();
772 displayMachineInfoHelper("\t", keyStr, pRoot->get_value(i), text);
775 ui->machineInfoText->setText(text);
780 if (bMachineInfoVisible)
782 if (ui->tabWidget->indexOf(ui->machineInfoTab) == -1)
785 ui->tabWidget->insertTab(0, ui->machineInfoTab, "Machine Info");
789 VOGLEDITOR_ENABLE_TAB(ui->machineInfoTab);
794 ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->machineInfoTab));
798 void VoglEditor::reset_tracefile_ui()
800 ui->action_Close->setEnabled(false);
801 ui->actionExport_API_Calls->setEnabled(false);
803 ui->prevSnapshotButton->setEnabled(false);
804 ui->nextSnapshotButton->setEnabled(false);
805 ui->prevDrawcallButton->setEnabled(false);
806 ui->nextDrawcallButton->setEnabled(false);
807 ui->searchTextBox->clear();
808 ui->searchTextBox->setEnabled(false);
809 ui->searchPrevButton->setEnabled(false);
810 ui->searchNextButton->setEnabled(false);
812 m_statusLabel->clear();
813 m_pPlayButton->setEnabled(false);
814 m_pPauseButton->setEnabled(false);
815 m_pTrimButton->setEnabled(false);
816 m_pStopButton->setEnabled(false);
818 VOGLEDITOR_DISABLE_TAB(ui->machineInfoTab);
823 void VoglEditor::reset_snapshot_ui()
825 m_currentSnapshot = NULL;
827 m_framebufferExplorer->clear();
828 m_textureExplorer->clear();
829 m_renderbufferExplorer->clear();
830 m_programExplorer->clear();
831 m_shaderExplorer->clear();
833 ui->stateTreeView->setModel(NULL);
835 QWidget* pCurrentTab = ui->tabWidget->currentWidget();
837 VOGLEDITOR_DISABLE_TAB(ui->stateTab);
838 VOGLEDITOR_DISABLE_TAB(ui->framebufferTab);
839 VOGLEDITOR_DISABLE_TAB(ui->programTab);
840 VOGLEDITOR_DISABLE_TAB(ui->shaderTab);
841 VOGLEDITOR_DISABLE_TAB(ui->textureTab);
842 VOGLEDITOR_DISABLE_TAB(ui->renderbufferTab);
844 ui->tabWidget->setCurrentWidget(pCurrentTab);
847 /// This helper will most often return a pointer equal to the pCurSnapshot that is passed in, or NULL if the node does not have a snapshot
848 /// and also has no children. The pMostRecentSnapshot parameter will be updated to point to the desired snapshot.
849 /// This function does not follow a traditional DFS search because we need to find the desired snapshot then return the one before it.
850 /// An alternative approach would be to keep a stack of the found snapshots, or even to build up that stack / list as the user
851 /// generates new snapshots.
852 vogleditor_gl_state_snapshot* VoglEditor::findMostRecentSnapshot_helper(vogleditor_apiCallTreeItem* pItem, vogleditor_gl_state_snapshot*& pMostRecentSnapshot, const vogleditor_gl_state_snapshot* pCurSnapshot)
854 // check if this item has a snapshot shot
855 if (pItem->has_snapshot())
857 vogleditor_gl_state_snapshot* pTmp = pItem->get_snapshot();
858 if (pTmp == pCurSnapshot)
860 // if we've reached the item with the current snapshot, we want to return the previous snapshot.
865 // update most recent snapshot
866 pMostRecentSnapshot = pTmp;
870 for (int i = 0; i < pItem->childCount(); i++)
872 vogleditor_gl_state_snapshot* pTmp = findMostRecentSnapshot_helper(pItem->child(i), pMostRecentSnapshot, pCurSnapshot);
875 if (pTmp == pCurSnapshot)
877 // if we've reached the item with the current snapshot, we want to return the previous snapshot.
882 // update most recent snapshot
883 pMostRecentSnapshot = pTmp;
891 /// This function exists just to simplify the interaction with the helper, so that there no confusion between
892 /// whether the returned value, or passed in reference parameter should be used as the most recent snapshot.
893 /// It will either return NULL if there is no recent snapshot (which should only happen for the very first snapshot
894 /// in a trace), or a pointer to a valid snapshot.
895 vogleditor_gl_state_snapshot* VoglEditor::findMostRecentSnapshot(vogleditor_apiCallTreeItem* pItem, const vogleditor_gl_state_snapshot* pCurSnapshot)
897 vogleditor_gl_state_snapshot* pMostRecentSnapshot = NULL;
898 findMostRecentSnapshot_helper(pItem, pMostRecentSnapshot, pCurSnapshot);
899 return pMostRecentSnapshot;
902 void VoglEditor::update_ui_for_snapshot(vogleditor_gl_state_snapshot* pStateSnapshot)
904 if (pStateSnapshot == NULL)
910 if (pStateSnapshot->is_valid() == false)
916 if (m_currentSnapshot == pStateSnapshot)
918 // no need to update if it is the same snapshot
922 m_currentSnapshot = pStateSnapshot;
924 if (ui->stateTreeView->model() != NULL)
926 if (static_cast<vogleditor_QStateTreeModel*>(ui->stateTreeView->model())->get_snapshot() == m_currentSnapshot)
928 // displaying the same snapshot, return
933 QCursor origCursor = this->cursor();
934 this->setCursor(Qt::WaitCursor);
937 vogleditor_QStateTreeModel* pStateModel = new vogleditor_QStateTreeModel(NULL);
939 vogleditor_QApiCallTreeModel* pTreeModel = static_cast<vogleditor_QApiCallTreeModel*>(ui->treeView->model());
940 vogleditor_gl_state_snapshot* pBaseSnapshot = findMostRecentSnapshot(pTreeModel->root(), m_currentSnapshot);
941 pStateModel->set_diff_base_snapshot(pBaseSnapshot);
943 pStateModel->set_snapshot(pStateSnapshot);
945 ui->stateTreeView->setModel(pStateModel);
946 ui->stateTreeView->expandToDepth(1);
947 ui->stateTreeView->setColumnWidth(0, ui->stateTreeView->width() * 0.5);
949 VOGLEDITOR_ENABLE_TAB(ui->stateTab);
951 if (pStateSnapshot->get_contexts().size() > 0)
953 vogl_trace_ptr_value curContextHandle = pStateSnapshot->get_cur_trace_context();
954 if (curContextHandle != 0)
956 vogl_context_snapshot* pContext = pStateSnapshot->get_context(curContextHandle);
959 vogl_gl_object_state_ptr_vec textureObjects;
960 pContext->get_all_objects_of_category(cGLSTTexture, textureObjects);
961 m_textureExplorer->set_texture_objects(textureObjects);
963 GLuint curActiveTextureUnit = pContext->get_general_state().get_value<GLuint>(GL_ACTIVE_TEXTURE);
964 if (curActiveTextureUnit >= GL_TEXTURE0 && curActiveTextureUnit < (GL_TEXTURE0 + pContext->get_context_info().get_max_texture_image_units()))
966 GLuint cur2DBinding = pContext->get_general_state().get_value<GLuint>(GL_TEXTURE_2D_BINDING_EXT, curActiveTextureUnit - GL_TEXTURE0);
967 displayTexture(cur2DBinding, false);
971 vogl_gl_object_state_ptr_vec renderbufferObjects;
972 pContext->get_all_objects_of_category(cGLSTRenderbuffer, renderbufferObjects);
973 m_renderbufferExplorer->set_texture_objects(renderbufferObjects);
974 if (renderbufferObjects.size() > 0) { VOGLEDITOR_ENABLE_TAB(ui->renderbufferTab); }
977 vogl_gl_object_state_ptr_vec framebufferObjects;
978 pContext->get_all_objects_of_category(cGLSTFramebuffer, framebufferObjects);
979 m_framebufferExplorer->set_framebuffer_objects(framebufferObjects, *pContext, pStateSnapshot->get_default_framebuffer());
980 GLuint64 curDrawFramebuffer = pContext->get_general_state().get_value<GLuint64>(GL_DRAW_FRAMEBUFFER_BINDING);
981 displayFramebuffer(curDrawFramebuffer, false);
984 vogl_gl_object_state_ptr_vec programObjects;
985 pContext->get_all_objects_of_category(cGLSTProgram, programObjects);
986 m_programExplorer->set_program_objects(programObjects);
987 GLuint64 curProgram = pContext->get_general_state().get_value<GLuint64>(GL_CURRENT_PROGRAM);
988 m_programExplorer->set_active_program(curProgram);
989 if (programObjects.size() > 0) { VOGLEDITOR_ENABLE_TAB(ui->programTab); }
992 vogl_gl_object_state_ptr_vec shaderObjects;
993 pContext->get_all_objects_of_category(cGLSTShader, shaderObjects);
994 m_shaderExplorer->set_shader_objects(shaderObjects);
997 for (vogl_gl_object_state_ptr_vec::iterator iter = programObjects.begin(); iter != programObjects.end(); iter++)
999 if ((*iter)->get_snapshot_handle() == curProgram)
1001 vogl_program_state* pProgramState = static_cast<vogl_program_state*>(*iter);
1002 if (pProgramState->get_attached_shaders().size() > 0)
1004 uint curShader = pProgramState->get_attached_shaders()[0];
1005 m_shaderExplorer->set_active_shader(curShader);
1011 if (shaderObjects.size() > 0) { VOGLEDITOR_ENABLE_TAB(ui->shaderTab); }
1015 this->setCursor(origCursor);
1018 void VoglEditor::on_stateTreeView_clicked(const QModelIndex &index)
1020 vogleditor_stateTreeItem* pStateItem = static_cast<vogleditor_stateTreeItem*>(index.internalPointer());
1021 if (pStateItem == NULL)
1026 switch(pStateItem->getStateType())
1028 case vogleditor_stateTreeItem::cTEXTURE:
1030 vogleditor_stateTreeTextureItem* pTextureItem = static_cast<vogleditor_stateTreeTextureItem*>(pStateItem);
1031 if (pTextureItem == NULL)
1036 displayTexture(pTextureItem->get_texture_state()->get_snapshot_handle(), true);
1040 case vogleditor_stateTreeItem::cPROGRAM:
1042 vogleditor_stateTreeProgramItem* pProgramItem = static_cast<vogleditor_stateTreeProgramItem*>(pStateItem);
1043 if (pProgramItem == NULL)
1048 displayProgram(pProgramItem->get_current_state()->get_snapshot_handle(), true);
1052 case vogleditor_stateTreeItem::cSHADER:
1054 vogleditor_stateTreeShaderItem* pShaderItem = static_cast<vogleditor_stateTreeShaderItem*>(pStateItem);
1055 if (pShaderItem == NULL)
1060 displayShader(pShaderItem->get_current_state()->get_snapshot_handle(), true);
1064 case vogleditor_stateTreeItem::cFRAMEBUFFER:
1066 vogleditor_stateTreeFramebufferItem* pFramebufferItem = static_cast<vogleditor_stateTreeFramebufferItem*>(pStateItem);
1067 if (pFramebufferItem == NULL)
1072 displayFramebuffer(pFramebufferItem->get_framebuffer_state()->get_snapshot_handle(), true);
1076 case vogleditor_stateTreeItem::cDEFAULT:
1083 bool VoglEditor::displayShader(GLuint64 shaderHandle, bool bBringTabToFront)
1085 bool bDisplayed = false;
1086 if (m_shaderExplorer->set_active_shader(shaderHandle))
1088 if (bBringTabToFront)
1090 ui->tabWidget->setCurrentWidget(ui->shaderTab);
1097 void VoglEditor::displayProgram(GLuint64 programHandle, bool bBringTabToFront)
1099 if (m_programExplorer->set_active_program(programHandle))
1101 if (bBringTabToFront)
1103 ui->tabWidget->setCurrentWidget(ui->programTab);
1108 void VoglEditor::displayFramebuffer(GLuint64 framebufferHandle, bool bBringTabToFront)
1110 bool bDisplayedFBO = m_framebufferExplorer->set_active_framebuffer(framebufferHandle);
1114 VOGLEDITOR_ENABLE_TAB(ui->framebufferTab);
1115 if (bBringTabToFront)
1117 ui->tabWidget->setCurrentWidget(ui->framebufferTab);
1122 bool VoglEditor::displayTexture(GLuint64 textureHandle, bool bBringTabToFront)
1124 bool bDisplayedTexture = m_textureExplorer->set_active_texture(textureHandle);
1126 if (bDisplayedTexture)
1128 VOGLEDITOR_ENABLE_TAB(ui->textureTab);
1129 if (bBringTabToFront)
1131 ui->tabWidget->setCurrentWidget(ui->textureTab);
1135 return bDisplayedTexture;
1138 void VoglEditor::on_treeView_currentChanged(const QModelIndex & current, const QModelIndex & previous)
1140 VOGL_NOTE_UNUSED(previous);
1141 onApiCallSelected(current, false);
1144 void VoglEditor::on_treeView_clicked(const QModelIndex &index)
1146 onApiCallSelected(index, true);
1149 void VoglEditor::onApiCallSelected(const QModelIndex &index, bool bAllowStateSnapshot)
1151 vogleditor_apiCallTreeItem* pCallTreeItem = static_cast<vogleditor_apiCallTreeItem*>(index.internalPointer());
1152 if (pCallTreeItem == NULL)
1157 vogleditor_frameItem* pFrameItem = pCallTreeItem->frameItem();
1158 vogleditor_apiCallItem* pApiCallItem = pCallTreeItem->apiCallItem();
1160 if (bAllowStateSnapshot && pCallTreeItem == m_pCurrentCallTreeItem)
1162 // we can only get snapshots for specific API calls
1163 if (pApiCallItem != NULL && pApiCallItem->needs_snapshot())
1165 // get the snapshot after the current api call
1166 vogleditor_gl_state_snapshot* pNewSnapshot = NULL;
1167 QCursor origCursor = cursor();
1168 setCursor(Qt::WaitCursor);
1169 m_traceReplayer.replay(m_pTraceReader, m_pApicallTreeModel->root(), &pNewSnapshot, pApiCallItem->globalCallIndex(), false);
1170 setCursor(origCursor);
1171 pCallTreeItem->set_snapshot(pNewSnapshot);
1175 update_ui_for_snapshot(pCallTreeItem->get_snapshot());
1177 if (pApiCallItem != NULL && m_pCurrentCallTreeItem != pCallTreeItem)
1179 if (m_backtraceToJsonMap.size() > 0)
1182 json_node* pBacktraceNode = m_backtraceToJsonMap[(uint)pApiCallItem->backtraceHashIndex()];
1183 if (pBacktraceNode != NULL)
1185 json_node* pAddrs = pBacktraceNode->find_child_array("addrs");
1186 json_node* pSyms = pBacktraceNode->find_child_array("syms");
1188 for (uint i = 0; i < pAddrs->size(); i++)
1190 tmp += pAddrs->get_value(i).as_string_ptr();
1194 tmp += pSyms->get_value(i).as_string_ptr();
1199 ui->backtraceText->setText(tmp);
1203 if (pApiCallItem != NULL)
1205 m_timeline->setCurrentApiCall(pApiCallItem->globalCallIndex());
1208 if (pFrameItem != NULL)
1210 m_timeline->setCurrentFrame(pFrameItem->frameNumber());
1213 m_timeline->repaint();
1215 m_pCurrentCallTreeItem = pCallTreeItem;
1218 void VoglEditor::selectApicallModelIndex(QModelIndex index, bool scrollTo, bool select)
1220 // make sure the index is visible
1221 QModelIndex parentIndex = index.parent();
1222 while (parentIndex.isValid())
1224 if (ui->treeView->isExpanded(parentIndex) == false)
1226 ui->treeView->expand(parentIndex);
1228 parentIndex = parentIndex.parent();
1231 // scroll to the index
1234 ui->treeView->scrollTo(index);
1240 ui->treeView->setCurrentIndex(index);
1243 if (m_searchApicallResults.size() > 0 && !ui->searchTextBox->text().isEmpty())
1245 QItemSelectionModel* pSelection = ui->treeView->selectionModel();
1246 for (int i = 0; i < m_searchApicallResults.size(); i++)
1248 pSelection->select(m_searchApicallResults[i], QItemSelectionModel::Select | QItemSelectionModel::Rows);
1250 ui->treeView->setSelectionModel(pSelection);
1254 void VoglEditor::on_searchTextBox_textChanged(const QString &searchText)
1256 QModelIndex curSearchIndex = ui->treeView->currentIndex();
1257 if (curSearchIndex.isValid() == false)
1262 // store original background color of the search text box so that it can be turned to red and later restored.
1263 static const QColor sOriginalTextBoxBackground = ui->searchTextBox->palette().base().color();
1265 // clear previous items
1266 QItemSelectionModel* pSelection = ui->treeView->selectionModel();
1267 if (pSelection != NULL)
1269 for (int i = 0; i < m_searchApicallResults.size(); i++)
1271 pSelection->select(m_searchApicallResults[i], QItemSelectionModel::Clear | QItemSelectionModel::Rows);
1273 ui->treeView->setSelectionModel(pSelection);
1277 m_searchApicallResults.clear();
1278 if (m_pApicallTreeModel != NULL)
1280 m_searchApicallResults = m_pApicallTreeModel->find_search_matches(searchText);
1283 // if there are matches, restore the textbox background to its original color
1284 if (m_searchApicallResults.size() > 0)
1286 QPalette palette(ui->searchTextBox->palette());
1287 palette.setColor(QPalette::Base, sOriginalTextBoxBackground);
1288 ui->searchTextBox->setPalette(palette);
1292 if (!searchText.isEmpty())
1294 if (m_searchApicallResults.size() > 0)
1296 // scroll to the first result, but don't select it
1297 selectApicallModelIndex(m_searchApicallResults[0], true, false);
1301 // no items were found, so set the textbox background to red
1302 QPalette palette(ui->searchTextBox->palette());
1303 palette.setColor(QPalette::Base, Qt::red);
1304 ui->searchTextBox->setPalette(palette);
1309 void VoglEditor::on_searchNextButton_clicked()
1311 if (m_pApicallTreeModel != NULL)
1313 QModelIndex index = m_pApicallTreeModel->find_next_search_result(m_pCurrentCallTreeItem, ui->searchTextBox->text());
1314 selectApicallModelIndex(index, true, true);
1318 void VoglEditor::on_searchPrevButton_clicked()
1320 if (m_pApicallTreeModel != NULL)
1322 QModelIndex index = m_pApicallTreeModel->find_prev_search_result(m_pCurrentCallTreeItem, ui->searchTextBox->text());
1323 selectApicallModelIndex(index, true, true);
1327 void VoglEditor::on_prevSnapshotButton_clicked()
1329 if (m_pApicallTreeModel != NULL)
1331 vogleditor_apiCallTreeItem* pPrevItemWithSnapshot = m_pApicallTreeModel->find_prev_snapshot(m_pCurrentCallTreeItem);
1332 selectApicallModelIndex(m_pApicallTreeModel->indexOf(pPrevItemWithSnapshot), true, true);
1333 ui->treeView->setFocus();
1337 void VoglEditor::on_nextSnapshotButton_clicked()
1339 if (m_pApicallTreeModel != NULL)
1341 vogleditor_apiCallTreeItem* pNextItemWithSnapshot = m_pApicallTreeModel->find_next_snapshot(m_pCurrentCallTreeItem);
1342 selectApicallModelIndex(m_pApicallTreeModel->indexOf(pNextItemWithSnapshot), true, true);
1343 ui->treeView->setFocus();
1347 void VoglEditor::on_prevDrawcallButton_clicked()
1349 if (m_pApicallTreeModel != NULL)
1351 vogleditor_apiCallTreeItem* pPrevItem = m_pApicallTreeModel->find_prev_drawcall(m_pCurrentCallTreeItem);
1352 selectApicallModelIndex(m_pApicallTreeModel->indexOf(pPrevItem), true, true);
1353 ui->treeView->setFocus();
1357 void VoglEditor::on_nextDrawcallButton_clicked()
1359 if (m_pApicallTreeModel != NULL)
1361 vogleditor_apiCallTreeItem* pNextItem = m_pApicallTreeModel->find_next_drawcall(m_pCurrentCallTreeItem);
1362 selectApicallModelIndex(m_pApicallTreeModel->indexOf(pNextItem), true, true);
1363 ui->treeView->setFocus();
1368 void VoglEditor::on_program_edited(vogl_program_state* pNewProgramState)
1370 VOGL_NOTE_UNUSED(pNewProgramState);
1372 m_currentSnapshot->set_edited(true);
1374 // update all the snapshot flags
1375 bool bFoundEditedSnapshot = false;
1376 recursive_update_snapshot_flags(m_pApicallTreeModel->root(), bFoundEditedSnapshot);
1378 // give the tree view focus so that it redraws. This is something of a hack, we don't really want to be changing around which control has focus,
1379 // but right now I don't see it being a major issue. It may be an issue later on depending on how we implement more state editing (ie, if arrow
1380 // keys are used to cycle through options in a drop-down, and the tree view gets focus, the arrow keys would then start changing the selected
1381 // API call instead of cycling through state options).
1382 ui->treeView->setFocus();
1385 // if an edited snapshot has already been found, mark the node (and all children) as dirty.
1386 void VoglEditor::recursive_update_snapshot_flags(vogleditor_apiCallTreeItem* pItem, bool& bFoundEditedSnapshot)
1388 // check if this item has a snapshot shot
1389 if (pItem->has_snapshot())
1391 if (!bFoundEditedSnapshot)
1393 if (pItem->get_snapshot()->is_edited())
1395 bFoundEditedSnapshot = true;
1399 pItem->get_snapshot()->set_outdated(false);
1404 pItem->get_snapshot()->set_outdated(true);
1408 for (int i = 0; i < pItem->childCount(); i++)
1410 recursive_update_snapshot_flags(pItem->child(i), bFoundEditedSnapshot);
1414 #undef VOGLEDITOR_DISABLE_TAB
1415 #undef VOGLEDITOR_ENABLE_TAB