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 "vogl_trace_file_writer.h"
49 #include "vogleditor_qstatetreemodel.h"
50 #include "vogleditor_statetreetextureitem.h"
51 #include "vogleditor_statetreeprogramitem.h"
52 #include "vogleditor_statetreeshaderitem.h"
53 #include "vogleditor_statetreeframebufferitem.h"
54 #include "vogleditor_qtextureexplorer.h"
56 #define VOGLEDITOR_DISABLE_TAB(tab) ui->tabWidget->setTabEnabled(ui->tabWidget->indexOf(tab), false);
57 #define VOGLEDITOR_ENABLE_TAB(tab) ui->tabWidget->setTabEnabled(ui->tabWidget->indexOf(tab), true);
59 //----------------------------------------------------------------------------------------------------------------------
61 //----------------------------------------------------------------------------------------------------------------------
62 static void *g_actual_libgl_module_handle;
63 static QString g_PROJECT_NAME = "Vogl Editor";
65 //----------------------------------------------------------------------------------------------------------------------
66 // vogl_get_proc_address_helper
67 //----------------------------------------------------------------------------------------------------------------------
68 static vogl_void_func_ptr_t vogl_get_proc_address_helper(const char *pName)
72 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;
74 if ((!pFunc) && (GL_ENTRYPOINT(glXGetProcAddress)))
75 pFunc = reinterpret_cast<vogl_void_func_ptr_t>( GL_ENTRYPOINT(glXGetProcAddress)(reinterpret_cast<const GLubyte*>(pName)) );
81 //----------------------------------------------------------------------------------------------------------------------
83 //----------------------------------------------------------------------------------------------------------------------
88 g_actual_libgl_module_handle = dlopen("libGL.so.1", RTLD_LAZY);
89 if (!g_actual_libgl_module_handle)
91 vogl_error_printf("%s: Failed loading libGL.so.1!\n", VOGL_FUNCTION_NAME);
95 GL_ENTRYPOINT(glXGetProcAddress) = reinterpret_cast<glXGetProcAddress_func_ptr_t>(dlsym(g_actual_libgl_module_handle, "glXGetProcAddress"));
96 if (!GL_ENTRYPOINT(glXGetProcAddress))
98 vogl_error_printf("%s: Failed getting address of glXGetProcAddress() from libGL.so.1!\n", VOGL_FUNCTION_NAME);
105 VoglEditor::VoglEditor(QWidget *parent) :
107 ui(new Ui::VoglEditor),
109 m_framebufferExplorer(NULL),
110 m_textureExplorer(NULL),
111 m_renderbufferExplorer(NULL),
112 m_programExplorer(NULL),
113 m_shaderExplorer(NULL),
115 m_currentSnapshot(NULL),
116 m_pCurrentCallTreeItem(NULL),
118 m_pPauseButton(NULL),
121 m_pTraceReader(NULL),
122 m_pApicallTreeModel(NULL)
128 vogl_init_actual_gl_entrypoints(vogl_get_proc_address_helper);
131 m_statusLabel = new QLabel(ui->statusBar);
132 m_statusLabel->setBaseSize(150, 12);
133 ui->statusBar->addWidget(m_statusLabel, 1);
135 // cache the original background color of the search text box
136 m_searchTextboxBackgroundColor = ui->searchTextBox->palette().base().color();
138 // setup framebuffer tab
139 QGridLayout* framebufferTab_layout = new QGridLayout;
140 m_framebufferExplorer = new vogleditor_QFramebufferExplorer(ui->framebufferTab);
141 framebufferTab_layout->addWidget(m_framebufferExplorer, 0, 0);
142 ui->framebufferTab->setLayout(framebufferTab_layout);
145 QGridLayout* textureTab_layout = new QGridLayout;
146 m_textureExplorer = new vogleditor_QTextureExplorer(ui->textureTab);
147 textureTab_layout->addWidget(m_textureExplorer, 0, 0);
148 ui->textureTab->setLayout(textureTab_layout);
150 // setup renderbuffer tab
151 QGridLayout* rbTab_layout = new QGridLayout;
152 m_renderbufferExplorer = new vogleditor_QTextureExplorer(ui->renderbufferTab);
153 rbTab_layout->addWidget(m_renderbufferExplorer, 0, 0);
154 ui->renderbufferTab->setLayout(rbTab_layout);
157 QGridLayout* programTab_layout = new QGridLayout;
158 m_programExplorer = new vogleditor_QProgramExplorer(ui->programTab);
159 programTab_layout->addWidget(m_programExplorer, 0, 0);
160 ui->programTab->setLayout(programTab_layout);
163 QGridLayout* shaderTab_layout = new QGridLayout;
164 m_shaderExplorer = new vogleditor_QShaderExplorer(ui->shaderTab);
165 shaderTab_layout->addWidget(m_shaderExplorer, 0, 0);
166 ui->shaderTab->setLayout(shaderTab_layout);
169 m_timeline = new vogleditor_QTimelineView();
170 m_timeline->setMinimumHeight(100);
171 ui->verticalLayout->addWidget(m_timeline);
172 ui->verticalLayout->removeWidget(ui->timelineViewPlaceholder);
174 // add buttons to toolbar
175 m_pPlayButton = new QToolButton(ui->mainToolBar);
176 m_pPlayButton->setText("Play trace");
177 m_pPlayButton->setEnabled(false);
179 m_pPauseButton = new QToolButton(ui->mainToolBar);
180 m_pPauseButton->setText("Pause");
181 m_pPauseButton->setEnabled(false);
183 m_pTrimButton = new QToolButton(ui->mainToolBar);
184 m_pTrimButton->setText("Trim");
185 m_pTrimButton->setEnabled(false);
187 m_pStopButton = new QToolButton(ui->mainToolBar);
188 m_pStopButton->setText("Stop");
189 m_pStopButton->setEnabled(false);
191 // Temporarily hide the other buttons (until asyncronous playback is supported)
192 m_pPauseButton->setVisible(false);
193 m_pTrimButton->setVisible(false);
194 m_pStopButton->setVisible(false);
196 ui->mainToolBar->addWidget(m_pPlayButton);
197 ui->mainToolBar->addWidget(m_pPauseButton);
198 ui->mainToolBar->addWidget(m_pTrimButton);
199 ui->mainToolBar->addWidget(m_pStopButton);
201 connect(m_pPlayButton, SIGNAL(clicked()), this, SLOT(playCurrentTraceFile()));
202 connect(m_pPauseButton, SIGNAL(clicked()), this, SLOT(pauseCurrentTraceFile()));
203 connect(m_pTrimButton, SIGNAL(clicked()), this, SLOT(trimCurrentTraceFile()));
204 connect(m_pStopButton, SIGNAL(clicked()), this, SLOT(stopCurrentTraceFile()));
206 connect(m_programExplorer, SIGNAL(program_edited(vogl_program_state*)), this, SLOT(on_program_edited(vogl_program_state*)));
208 reset_tracefile_ui();
211 VoglEditor::~VoglEditor()
216 if (m_textureExplorer != NULL)
218 delete m_textureExplorer;
219 m_textureExplorer = NULL;
222 if (m_renderbufferExplorer != NULL)
224 delete m_renderbufferExplorer;
225 m_renderbufferExplorer = NULL;
228 if (m_programExplorer != NULL)
230 delete m_programExplorer;
231 m_programExplorer = NULL;
234 if (m_shaderExplorer != NULL)
236 delete m_shaderExplorer;
237 m_shaderExplorer = NULL;
241 void VoglEditor::playCurrentTraceFile()
243 QCursor origCursor = cursor();
244 setCursor(Qt::WaitCursor);
247 m_pPlayButton->setEnabled(false);
248 m_pPauseButton->setEnabled(true);
249 m_pTrimButton->setEnabled(true);
250 m_pStopButton->setEnabled(true);
251 m_statusLabel->clear();
253 if (m_traceReplayer.replay(m_pTraceReader, m_pApicallTreeModel->root(), NULL, 0, true))
255 // replay was successful
256 m_pPlayButton->setEnabled(true);
257 m_pPauseButton->setEnabled(false);
258 m_pTrimButton->setEnabled(false);
259 m_pStopButton->setEnabled(false);
263 m_statusLabel->setText("Failed to replay the trace.");
266 setCursor(origCursor);
269 void VoglEditor::pauseCurrentTraceFile()
271 if (m_traceReplayer.pause())
274 m_pPlayButton->setEnabled(true);
275 m_pPauseButton->setEnabled(false);
276 m_pTrimButton->setEnabled(true);
277 m_pStopButton->setEnabled(true);
278 m_statusLabel->clear();
282 m_statusLabel->setText("Failed to pause the trace replay.");
286 void VoglEditor::trimCurrentTraceFile()
288 if (m_traceReplayer.trim())
290 m_statusLabel->clear();
294 m_statusLabel->setText("Failed to trim the trace replay.");
298 void VoglEditor::stopCurrentTraceFile()
300 if (m_traceReplayer.stop())
303 m_pPlayButton->setEnabled(true);
304 m_pPauseButton->setEnabled(false);
305 m_pTrimButton->setEnabled(false);
306 m_pStopButton->setEnabled(false);
307 m_statusLabel->clear();
311 m_statusLabel->setText("Failed to stop the trace replay.");
315 void VoglEditor::on_actionE_xit_triggered()
320 void VoglEditor::on_action_Open_triggered()
322 QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), QString(),
323 tr("GLI Binary Files (*.bin);;JSON Files (*.json)"));
325 if (!fileName.isEmpty()) {
326 vogl::dynamic_string filename;
327 filename.set(fileName.toStdString().c_str());
329 if (open_trace_file(filename) == false) {
330 QMessageBox::critical(this, tr("Error"), tr("Could not open file"));
336 void VoglEditor::on_action_Close_triggered()
341 void VoglEditor::close_trace_file()
343 if (m_pTraceReader != NULL)
345 m_pTraceReader->close();
346 vogl_delete(m_pTraceReader);
347 m_pTraceReader = NULL;
349 setWindowTitle(g_PROJECT_NAME);
351 m_openFilename.clear();
352 m_backtraceToJsonMap.clear();
353 m_backtraceDoc.clear();
355 reset_tracefile_ui();
357 ui->treeView->setModel(NULL);
358 ui->machineInfoText->clear();
359 ui->backtraceText->clear();
360 m_timeline->setModel(NULL);
361 m_timeline->repaint();
363 if (m_pTimelineModel != NULL)
365 delete m_pTimelineModel;
366 m_pTimelineModel = NULL;
371 void VoglEditor::write_child_api_calls(vogleditor_apiCallTreeItem* pItem, FILE* pFile)
373 QString string = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole).toString();
374 vogl_fwrite(string.toStdString().c_str(), 1, string.size(), pFile);
375 vogl_fwrite("\r\n", 1, 2, pFile);
377 for (int i = 0; i < pItem->childCount(); i++)
379 write_child_api_calls(pItem->child(i), pFile);
383 void VoglEditor::on_actionExport_API_Calls_triggered()
385 QString suggestedName = m_openFilename;
387 int lastIndex = suggestedName.lastIndexOf('-');
390 suggestedName = suggestedName.remove(lastIndex, suggestedName.size() - lastIndex);
392 suggestedName += "-ApiCalls.txt";
394 QString fileName = QFileDialog::getSaveFileName(this, tr("Export API Calls"), suggestedName, tr("Text (*.txt)"));
396 if (!fileName.isEmpty())
398 vogl::dynamic_string filename;
399 filename.set(fileName.toStdString().c_str());
401 FILE* pFile = vogl_fopen(filename.c_str(), "w");
402 vogleditor_QApiCallTreeModel* pModel = static_cast<vogleditor_QApiCallTreeModel*>(ui->treeView->model());
403 vogleditor_apiCallTreeItem* pRoot = pModel->root();
404 for (int i = 0; i < pRoot->childCount(); i++)
406 write_child_api_calls(pRoot->child(i), pFile);
412 static const unsigned int VOGLEDITOR_SESSION_FILE_FORMAT_VERSION_1 = 1;
413 static const unsigned int VOGLEDITOR_SESSION_FILE_FORMAT_VERSION = VOGLEDITOR_SESSION_FILE_FORMAT_VERSION_1;
415 bool VoglEditor::load_session_from_disk(QString sessionFile)
418 json_document sessionDoc;
419 if (!sessionDoc.deserialize_file(sessionFile.toStdString().c_str()))
424 // look for expected metadata
425 json_node* pMetadata = sessionDoc.get_root()->find_child_object("metadata");
426 if (pMetadata == NULL)
431 const json_value& rFormatVersion = pMetadata->find_value("session_file_format_version");
432 if (!rFormatVersion.is_valid())
437 if (rFormatVersion.as_uint32() != VOGLEDITOR_SESSION_FILE_FORMAT_VERSION_1)
442 // load base trace file
443 json_node* pBaseTraceFile = sessionDoc.get_root()->find_child_object("base_trace_file");
444 if (pBaseTraceFile == NULL)
449 const json_value& rBaseTraceFilePath = pBaseTraceFile->find_value("rel_path");
450 const json_value& rBaseTraceFileUuid = pBaseTraceFile->find_value("uuid");
452 if (!rBaseTraceFilePath.is_valid() || !rBaseTraceFileUuid.is_valid())
457 dynamic_string sessionPathName;
458 dynamic_string sessionFileName;
459 file_utils::split_path(sessionFile.toStdString().c_str(), sessionPathName, sessionFileName);
461 dynamic_string traceFilePath = sessionPathName;
462 traceFilePath.append(rBaseTraceFilePath.as_string());
464 if (!open_trace_file(traceFilePath))
469 // TODO: verify UUID of the loaded trace file
471 // load session data if it is available
472 json_node* pSessionData = sessionDoc.get_root()->find_child_object("session_data");
473 if (pSessionData != NULL)
475 const json_value& rSessionPath = pSessionData->find_value("rel_path");
476 if (!rSessionPath.is_valid())
481 dynamic_string sessionDataPath = sessionPathName;
482 sessionDataPath.append(rSessionPath.as_string());
484 vogl_loose_file_blob_manager file_blob_manager;
485 file_blob_manager.init(cBMFReadWrite, sessionDataPath.c_str());
486 vogl_blob_manager* pBlob_manager = static_cast<vogl_blob_manager*>(&file_blob_manager);
489 const json_node* pSnapshots = pSessionData->find_child_array("snapshots");
490 for (unsigned int i = 0; i < pSnapshots->size(); i++)
492 const json_node* pSnapshotNode = pSnapshots->get_value_as_object(i);
494 const json_value& uuid = pSnapshotNode->find_value("uuid");
495 const json_value& isValid = pSnapshotNode->find_value("is_valid");
496 const json_value& isEdited = pSnapshotNode->find_value("is_edited");
497 const json_value& isOutdated = pSnapshotNode->find_value("is_outdated");
498 const json_value& frameNumber = pSnapshotNode->find_value("frame_number");
499 const json_value& callIndex = pSnapshotNode->find_value("call_index");
500 const json_value& path = pSnapshotNode->find_value("rel_path");
502 // make sure expected nodes are valid
503 if (!isValid.is_valid() || !isEdited.is_valid() || !isOutdated.is_valid())
508 vogl_gl_state_snapshot* pSnapshot = NULL;
510 if (path.is_valid() && isValid.as_bool() && uuid.is_valid())
512 dynamic_string snapshotPath = sessionDataPath;
513 snapshotPath.append(path.as_string());
516 json_document snapshotDoc;
517 if (!snapshotDoc.deserialize_file(snapshotPath.c_str()))
522 // attempt to verify the snapshot file
523 json_node* pSnapshotRoot = snapshotDoc.get_root();
524 if (pSnapshotRoot == NULL)
526 vogl_warning_printf("Invalid snapshot file at %s.", path.as_string_ptr());
530 const json_value& snapshotUuid = pSnapshotRoot->find_value("uuid");
531 if (!snapshotUuid.is_valid())
533 vogl_warning_printf("Invalid 'uuid' in snapshot file at %s.", path.as_string_ptr());
537 if (snapshotUuid.as_string() != uuid.as_string())
539 vogl_warning_printf("Mismatching 'uuid' between snapshot file at %s and that stored in the session file at %s.", path.as_string_ptr(), sessionFile.toStdString().c_str());
543 vogl_ctypes trace_gl_ctypes(m_pTraceReader->get_sof_packet().m_pointer_sizes);
544 pSnapshot = vogl_new(vogl_gl_state_snapshot);
545 if (!pSnapshot->deserialize(*snapshotDoc.get_root(), *pBlob_manager, &trace_gl_ctypes))
547 vogl_delete(pSnapshot);
549 vogl_warning_printf("Unable to deserialize the snapshot with uuid %s.", uuid.as_string_ptr());
554 vogleditor_gl_state_snapshot* pContainer = vogl_new(vogleditor_gl_state_snapshot, pSnapshot);
555 pContainer->set_edited(isEdited.as_bool());
556 pContainer->set_outdated(isOutdated.as_bool());
558 if (callIndex.is_valid())
560 // the snapshot is associated with an api call
561 vogleditor_apiCallTreeItem* pItem = m_pApicallTreeModel->find_call_number(callIndex.as_uint64());
564 pItem->set_snapshot(pContainer);
568 vogl_warning_printf("Unable to find API call index %" PRIu64 " to load the snapshot into.", callIndex.as_uint64());
569 if (pSnapshot != NULL) { vogl_delete(pSnapshot); pSnapshot = NULL; }
570 if (pContainer != NULL) { vogl_delete(pContainer); pContainer = NULL; }
573 else if (frameNumber.is_valid())
575 // the snapshot is associated with a frame.
576 // frame snapshots have the additional requirement that the snapshot itself MUST exist since
577 // we only save a frame snapshot if it is the inital frame and it has been edited.
578 // If we allow NULL snapshots, that we could accidently remove the initial snapshot that was loaded with the trace file.
579 if (pSnapshot != NULL)
581 vogleditor_apiCallTreeItem* pItem = m_pApicallTreeModel->find_frame_number(frameNumber.as_uint64());
584 pItem->set_snapshot(pContainer);
588 vogl_warning_printf("Unable to find frame number %" PRIu64 " to load the snapshot into.", frameNumber.as_uint64());
589 if (pSnapshot != NULL) { vogl_delete(pSnapshot); pSnapshot = NULL; }
590 if (pContainer != NULL) { vogl_delete(pContainer); pContainer = NULL; }
596 vogl_warning_printf("Session file contains invalid call or frame number for snapshot with uuid %s", uuid.as_string_ptr());
597 if (pSnapshot != NULL) { vogl_delete(pSnapshot); pSnapshot = NULL; }
598 if (pContainer != NULL) { vogl_delete(pContainer); pContainer = NULL; }
607 * Below is a summary of the information that needs to be saved out in a session's json file so that we can reload the session and be fully-featured.
608 * Note that not all of this information is currently supported (either by VoglEditor or the save/load functionality).
610 * sample data structure for version 1:
613 "session_file_format_version" : "0x1" <- would need to be updated when organization of existing data is changed
615 "base_trace_file" : {
616 "path" : "../traces/trimmed4.bin",
617 "uuid" : [ 2761638124, 1361789091, 2623121922, 1789156619 ]
620 "path" : "/home/peterl/voglproj/vogl_build/traces/trimmed4-vogleditor-sessiondata/",
623 "uuid" : "B346B680801ED2F5144E421DEA5EFDCC",
626 "is_outdated" : false,
630 "uuid" : "BC261B884088DBEADF376A03A489F2B9",
633 "is_outdated" : false,
634 "call_index" : 881069,
635 "path" : "/home/peterl/voglproj/vogl_build/traces/trimmed4-vogleditor-sessiondata/snapshot_call_881069.json"
638 "uuid" : "176DE3DEAA437B871FE122C84D5432E3",
641 "is_outdated" : false,
642 "call_index" : 881075,
643 "path" : "/home/peterl/voglproj/vogl_build/traces/trimmed4-vogleditor-sessiondata/snapshot_call_881075.json"
648 "is_outdated" : true,
649 "call_index" : 881080
655 bool VoglEditor::save_session_to_disk(QString sessionFile)
657 dynamic_string sessionPathName;
658 dynamic_string sessionFileName;
659 file_utils::split_path(sessionFile.toStdString().c_str(), sessionPathName, sessionFileName);
661 // modify the session file name to make a sessiondata folder
662 QString sessionDataFolder(sessionFileName.c_str());
663 int lastIndex = sessionDataFolder.lastIndexOf('.');
666 sessionDataFolder = sessionDataFolder.remove(lastIndex, sessionDataFolder.size() - lastIndex);
668 sessionDataFolder += "-sessiondata/";
670 dynamic_string sessionDataPath = sessionPathName;
671 sessionDataPath.append(sessionDataFolder.toStdString().c_str());
672 file_utils::create_directories(sessionDataPath, false);
674 vogl_loose_file_blob_manager file_blob_manager;
675 file_blob_manager.init(cBMFReadWrite, sessionDataPath.c_str());
676 vogl_blob_manager* pBlob_manager = static_cast<vogl_blob_manager*>(&file_blob_manager);
678 QCursor origCursor = this->cursor();
679 setCursor(Qt::WaitCursor);
681 json_document sessionDoc;
682 json_node& metadata = sessionDoc.get_root()->add_object("metadata");
683 metadata.add_key_value("session_file_format_version", to_hex_string(VOGLEDITOR_SESSION_FILE_FORMAT_VERSION));
685 // find relative path from session file to trace file
687 QString absoluteTracePath = relativeAppDir.absoluteFilePath(m_openFilename.toStdString().c_str());
688 QDir absoluteSessionFileDir(sessionPathName.c_str());
689 QString tracePathRelativeToSessionFile = absoluteSessionFileDir.relativeFilePath(absoluteTracePath);
691 json_node& baseTraceFile = sessionDoc.get_root()->add_object("base_trace_file");
692 baseTraceFile.add_key_value("rel_path", tracePathRelativeToSessionFile.toStdString().c_str());
693 json_node &uuid_array = baseTraceFile.add_array("uuid");
694 for (uint i = 0; i < VOGL_ARRAY_SIZE(m_pTraceReader->get_sof_packet().m_uuid); i++)
696 uuid_array.add_value(m_pTraceReader->get_sof_packet().m_uuid[i]);
699 json_node& sessionDataNode = sessionDoc.get_root()->add_object("session_data");
700 sessionDataNode.add_key_value("rel_path", sessionDataFolder.toStdString().c_str());
701 json_node& snapshotArray = sessionDataNode.add_array("snapshots");
703 vogleditor_apiCallTreeItem* pItem = m_pApicallTreeModel->find_next_snapshot(NULL);
704 vogleditor_apiCallTreeItem* pLastItem = NULL;
705 bool bSavedSuccessfully = true;
706 while (pItem != pLastItem && pItem != NULL)
708 dynamic_string filename;
710 json_node& snapshotNode = snapshotArray.add_object();
711 if (pItem->get_snapshot()->get_snapshot() != NULL)
713 dynamic_string strUUID;
714 snapshotNode.add_key_value("uuid", pItem->get_snapshot()->get_snapshot()->get_uuid().get_string(strUUID));
716 snapshotNode.add_key_value("is_valid", pItem->get_snapshot()->is_valid());
717 snapshotNode.add_key_value("is_edited", pItem->get_snapshot()->is_edited());
718 snapshotNode.add_key_value("is_outdated", pItem->get_snapshot()->is_outdated());
720 if (pItem->apiCallItem() != NULL)
722 uint64_t callIndex = pItem->apiCallItem()->globalCallIndex();
723 snapshotNode.add_key_value("call_index", callIndex);
724 if (pItem->get_snapshot()->get_snapshot() != NULL)
726 filename = filename.format("snapshot_call_%" PRIu64 ".json", callIndex);
727 snapshotNode.add_key_value("rel_path", filename);
728 dynamic_string filepath = sessionDataPath;
729 filepath.append(filename);
730 if (!save_snapshot_to_disk(pItem->get_snapshot()->get_snapshot(), filepath, pBlob_manager))
732 bSavedSuccessfully = false;
737 else if (pItem->frameItem() != NULL)
739 // the first frame of a trim will have a snapshot.
740 // this should only be saved out if the snapshot has been edited
741 uint64_t frameNumber = pItem->frameItem()->frameNumber();
742 snapshotNode.add_key_value("frame_number", frameNumber);
743 if (pItem->get_snapshot()->is_edited())
745 filename = filename.format("snapshot_frame_%" PRIu64 ".json", frameNumber);
746 snapshotNode.add_key_value("rel_path", filename);
747 dynamic_string filepath = sessionDataPath;
748 filepath.append(filename);
749 if (!save_snapshot_to_disk(pItem->get_snapshot()->get_snapshot(), filepath, pBlob_manager))
751 bSavedSuccessfully = false;
758 pItem = m_pApicallTreeModel->find_next_snapshot(pLastItem);
761 if (bSavedSuccessfully)
763 bSavedSuccessfully = sessionDoc.serialize_to_file(sessionFile.toStdString().c_str());
766 setCursor(origCursor);
768 return bSavedSuccessfully;
771 bool VoglEditor::save_snapshot_to_disk(vogl_gl_state_snapshot *pSnapshot, dynamic_string filename, vogl_blob_manager *pBlob_manager)
773 if (pSnapshot == NULL)
780 vogl_ctypes trace_gl_ctypes(m_pTraceReader->get_sof_packet().m_pointer_sizes);
782 if (!pSnapshot->serialize(*doc.get_root(), *pBlob_manager, &trace_gl_ctypes))
784 vogl_error_printf("Failed serializing state snapshot document!\n");
787 else if (!doc.serialize_to_file(filename.get_ptr(), true))
789 vogl_error_printf("Failed writing state snapshot to file \"%s\"!\n", filename.get_ptr());
794 vogl_printf("Successfully wrote JSON snapshot to file \"%s\"\n", filename.get_ptr());
800 //----------------------------------------------------------------------------------------------------------------------
801 // read_state_snapshot_from_trace
802 //----------------------------------------------------------------------------------------------------------------------
803 vogl_gl_state_snapshot* VoglEditor::read_state_snapshot_from_trace(vogl_trace_file_reader* pTrace_reader)
805 vogl_ctypes trace_gl_ctypes(pTrace_reader->get_sof_packet().m_pointer_sizes);
807 vogl_trace_packet keyframe_trace_packet(&trace_gl_ctypes);
809 pTrace_reader->seek_to_frame(0);
811 vogl_gl_state_snapshot *pSnapshot = NULL;
812 bool found_snapshot = false;
815 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
817 if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
819 vogl_error_printf("%s: Failed reading from keyframe trace file!\n", VOGL_FUNCTION_NAME);
823 if ((read_status == vogl_trace_file_reader::cEOF) || (pTrace_reader->get_packet_type() == cTSPTEOF))
825 vogl_error_printf("%s: Failed finding state snapshot in keyframe file!\n", VOGL_FUNCTION_NAME);
829 if (pTrace_reader->get_packet_type() != cTSPTGLEntrypoint)
832 if (!keyframe_trace_packet.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
834 vogl_error_printf("%s: Failed parsing GL entrypoint packet in keyframe file\n", VOGL_FUNCTION_NAME);
838 const vogl_trace_gl_entrypoint_packet *pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
839 gl_entrypoint_id_t entrypoint_id = static_cast<gl_entrypoint_id_t>(pGL_packet->m_entrypoint_id);
841 if (vogl_is_swap_buffers_entrypoint(entrypoint_id) || vogl_is_draw_entrypoint(entrypoint_id) || vogl_is_make_current_entrypoint(entrypoint_id))
843 vogl_error_printf("Failed finding state snapshot in keyframe file!\n");
847 switch (entrypoint_id)
849 case VOGL_ENTRYPOINT_glInternalTraceCommandRAD:
851 GLuint cmd = keyframe_trace_packet.get_param_value<GLuint>(0);
852 GLuint size = keyframe_trace_packet.get_param_value<GLuint>(1); VOGL_NOTE_UNUSED(size);
854 if (cmd == cITCRKeyValueMap)
856 key_value_map &kvm = keyframe_trace_packet.get_key_value_map();
858 dynamic_string cmd_type(kvm.get_string("command_type"));
859 if (cmd_type == "state_snapshot")
861 dynamic_string id(kvm.get_string("binary_id"));
864 vogl_error_printf("%s: Missing binary_id field in glInternalTraceCommandRAD key_valye_map command type: \"%s\"\n", VOGL_FUNCTION_NAME, cmd_type.get_ptr());
868 uint8_vec snapshot_data;
870 timed_scope ts("get_multi_blob_manager().get");
871 if (!pTrace_reader->get_multi_blob_manager().get(id, snapshot_data) || (snapshot_data.is_empty()))
873 vogl_error_printf("%s: Failed reading snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
878 vogl_message_printf("%s: Deserializing state snapshot \"%s\", %u bytes\n", VOGL_FUNCTION_NAME, id.get_ptr(), snapshot_data.size());
882 timed_scope ts("doc.binary_deserialize");
883 if (!doc.binary_deserialize(snapshot_data) || (!doc.get_root()))
885 vogl_error_printf("%s: Failed deserializing JSON snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
890 pSnapshot = vogl_new(vogl_gl_state_snapshot);
892 timed_scope ts("pSnapshot->deserialize");
893 if (!pSnapshot->deserialize(*doc.get_root(), pTrace_reader->get_multi_blob_manager(), &trace_gl_ctypes))
895 vogl_delete(pSnapshot);
898 vogl_error_printf("%s: Failed deserializing snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
902 found_snapshot = true;
911 } while (!found_snapshot);
917 bool VoglEditor::open_trace_file(dynamic_string filename)
919 QCursor origCursor = this->cursor();
920 this->setCursor(Qt::WaitCursor);
922 vogl_loose_file_blob_manager file_blob_manager;
923 dynamic_string keyframe_trace_path(file_utils::get_pathname(filename.get_ptr()));
924 file_blob_manager.init(cBMFReadable, keyframe_trace_path.get_ptr());
926 dynamic_string actual_keyframe_filename;
928 vogl_trace_file_reader* tmpReader = vogl_open_trace_file(filename, actual_keyframe_filename, NULL);
930 if (tmpReader == NULL)
932 m_statusLabel->setText("Failed to open: ");
933 m_statusLabel->setText(m_statusLabel->text().append(filename.c_str()));
934 this->setCursor(origCursor);
939 m_statusLabel->clear();
942 // now that we know the new trace file can be opened,
943 // close the old one, and update the trace reader
945 m_pTraceReader = tmpReader;
947 vogl_ctypes trace_ctypes;
948 trace_ctypes.init(m_pTraceReader->get_sof_packet().m_pointer_sizes);
950 m_pApicallTreeModel = new vogleditor_QApiCallTreeModel(m_pTraceReader);
951 ui->treeView->setModel(m_pApicallTreeModel);
953 if (ui->treeView->selectionModel() != NULL)
955 //connect(ui->treeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(on_treeView_selectionChanged(const QItemSelection&, const QItemSelection&)));
956 connect(ui->treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(on_treeView_currentChanged(const QModelIndex &, const QModelIndex &)));
959 if (m_pApicallTreeModel->hasChildren())
961 ui->treeView->setExpanded(m_pApicallTreeModel->index(0,0), true);
962 ui->treeView->setCurrentIndex(m_pApicallTreeModel->index(0,0));
965 int flagsColumnWidth = 30;
966 ui->treeView->header()->setMinimumSectionSize(flagsColumnWidth);
967 ui->treeView->header()->moveSection(VOGL_ACTC_FLAGS, 0);
968 ui->treeView->setColumnWidth(VOGL_ACTC_FLAGS, flagsColumnWidth);
970 int width = ui->treeView->width() - flagsColumnWidth - 30; // subtract a little extra for the scrollbar width
971 ui->treeView->setColumnWidth(VOGL_ACTC_APICALL, width * 0.7);
972 ui->treeView->setColumnWidth(VOGL_ACTC_INDEX, width * 0.15);
973 ui->treeView->setColumnWidth(VOGL_ACTC_DURATION, width * 0.15);
975 ui->searchTextBox->setEnabled(true);
976 ui->searchPrevButton->setEnabled(true);
977 ui->searchNextButton->setEnabled(true);
979 ui->action_Close->setEnabled(true);
980 ui->actionSave_Session->setEnabled(true);
981 ui->actionExport_API_Calls->setEnabled(true);
983 ui->prevSnapshotButton->setEnabled(true);
984 ui->nextSnapshotButton->setEnabled(true);
985 ui->prevDrawcallButton->setEnabled(true);
986 ui->nextDrawcallButton->setEnabled(true);
988 m_backtraceToJsonMap.clear();
989 m_backtraceDoc.clear();
991 // Extract backtrace map and machine info from trace archive
992 if (m_pTraceReader->get_archive_blob_manager().is_initialized())
995 uint8_vec backtrace_data;
996 bool bBacktraceVisible = false;
997 if (m_pTraceReader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME))
999 //$ TODO mikesart: read MAP_SYMS data here when symbols have been resolved.
1000 if (m_pTraceReader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME, backtrace_data))
1002 json_node* pRoot = m_backtraceDoc.get_root();
1003 if (m_backtraceDoc.deserialize((const char*)backtrace_data.get_ptr(), backtrace_data.size()))
1005 bBacktraceVisible = pRoot->size() > 0;
1006 for (uint i = 0; i < pRoot->size(); i++)
1008 json_node* pChild = pRoot->get_child(i);
1010 VOGL_ASSERT("Backtrace node does not have an 'index' child" && pChild != NULL && pChild->get_value_as_uint32("index", index));
1011 if (pChild != NULL && pChild->get_value_as_uint32("index", index))
1013 m_backtraceToJsonMap.insert(index, pChild);
1020 QList<int> backtraceSplitterSizes = ui->splitter_3->sizes();
1021 int backtraceSplitterTotalSize = backtraceSplitterSizes[0] + backtraceSplitterSizes[1];
1022 QList<int> newBacktraceSplitterSizes;
1023 if (!bBacktraceVisible)
1025 newBacktraceSplitterSizes.append(backtraceSplitterTotalSize);
1026 newBacktraceSplitterSizes.append(0);
1027 ui->splitter_3->setSizes(newBacktraceSplitterSizes);
1031 newBacktraceSplitterSizes << (backtraceSplitterTotalSize * 0.75)
1032 << (backtraceSplitterTotalSize * 0.25);
1033 ui->splitter_3->setSizes(newBacktraceSplitterSizes);
1037 displayMachineInfo();
1040 m_openFilename = filename.c_str();
1042 setWindowTitle(m_openFilename + " - " + g_PROJECT_NAME);
1044 ui->tabWidget->setCurrentWidget(ui->framebufferTab);
1047 m_pPlayButton->setEnabled(true);
1048 m_pPauseButton->setEnabled(false);
1049 m_pTrimButton->setEnabled(false);
1050 m_pStopButton->setEnabled(false);
1053 m_pTimelineModel = new vogleditor_apiCallTimelineModel(m_pApicallTreeModel->root());
1054 m_timeline->setModel(m_pTimelineModel);
1055 m_timeline->repaint();
1057 this->setCursor(origCursor);
1061 void VoglEditor::displayMachineInfoHelper(QString prefix, const QString& sectionKeyStr, const vogl::json_value& value, QString& rMachineInfoStr)
1063 if (value.is_array())
1065 const json_node* pNode = value.get_node_ptr();
1066 for (uint element = 0; element < pNode->size(); element++)
1068 dynamic_string elementStr = pNode->get_value(element).as_string();
1070 elementStr = elementStr.replace("\n", "\n\t");
1072 rMachineInfoStr += "\t";
1073 rMachineInfoStr += elementStr.get_ptr();
1074 rMachineInfoStr += "\n";
1077 rMachineInfoStr += "\n";
1079 else if (value.is_node())
1081 // Check if this is the modoule list.
1082 bool is_module_list = (sectionKeyStr == "module_list");
1083 const json_node* pNode = value.get_node_ptr();
1085 for (uint i = 0; i < pNode->size(); i++)
1087 dynamic_string key = pNode->get_key(i);
1088 const json_value &value2 = pNode->get_value(i);
1090 rMachineInfoStr += prefix;
1091 // If it's the module list, then the key is the filename and we want to display that last.
1092 if (!is_module_list)
1093 rMachineInfoStr += key.c_str();
1095 if (value2.is_array())
1097 const json_node* pNode2 = value2.get_node_ptr();
1099 // If this it module_list, then we get these items: base address, address size, uuid
1100 // Check in btrace_get_machine_info() to see what's written there.
1101 for (uint element = 0; element < pNode2->size(); element++)
1103 const json_value &json_val = pNode2->get_value(element);
1105 if (json_val.is_string())
1107 dynamic_string str = pNode2->get_value(element).as_string();
1108 rMachineInfoStr += str.c_str();
1113 buf.format("%" PRIx64, json_val.as_uint64());
1114 rMachineInfoStr += buf.c_str();
1117 rMachineInfoStr += "\t";
1122 rMachineInfoStr += ": ";
1123 rMachineInfoStr += value2.as_string_ptr();
1126 // Display the filename if this is the module_list.
1128 rMachineInfoStr += key.c_str();
1129 rMachineInfoStr += "\n";
1132 rMachineInfoStr += "\n";
1134 else if (value.is_string())
1136 rMachineInfoStr += value.as_string_ptr();
1140 rMachineInfoStr += value.as_string_ptr();
1144 void VoglEditor::displayMachineInfo()
1146 VOGL_ASSERT(m_pTraceReader != NULL);
1148 bool bMachineInfoVisible = false;
1149 if (m_pTraceReader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME))
1151 uint8_vec machine_info_data;
1152 if (m_pTraceReader->get_archive_blob_manager().get(VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME, machine_info_data))
1154 bMachineInfoVisible = true;
1156 json_node *pRoot = doc.get_root();
1157 if (doc.deserialize((const char*)machine_info_data.get_ptr(), machine_info_data.size()))
1160 for (uint i = 0; i < pRoot->size(); i++)
1162 dynamic_string sectionKeyStr = pRoot->get_key(i);
1163 text += pRoot->get_key(i).c_str();
1166 QString keyStr = sectionKeyStr.c_str();
1167 displayMachineInfoHelper("\t", keyStr, pRoot->get_value(i), text);
1170 ui->machineInfoText->setText(text);
1175 if (bMachineInfoVisible)
1177 if (ui->tabWidget->indexOf(ui->machineInfoTab) == -1)
1180 ui->tabWidget->insertTab(0, ui->machineInfoTab, "Machine Info");
1184 VOGLEDITOR_ENABLE_TAB(ui->machineInfoTab);
1189 ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->machineInfoTab));
1193 void VoglEditor::reset_tracefile_ui()
1195 ui->action_Close->setEnabled(false);
1196 ui->actionExport_API_Calls->setEnabled(false);
1197 ui->actionSave_Session->setEnabled(false);
1199 ui->prevSnapshotButton->setEnabled(false);
1200 ui->nextSnapshotButton->setEnabled(false);
1201 ui->prevDrawcallButton->setEnabled(false);
1202 ui->nextDrawcallButton->setEnabled(false);
1203 ui->searchTextBox->clear();
1204 ui->searchTextBox->setEnabled(false);
1205 ui->searchPrevButton->setEnabled(false);
1206 ui->searchNextButton->setEnabled(false);
1208 m_statusLabel->clear();
1209 m_pPlayButton->setEnabled(false);
1210 m_pPauseButton->setEnabled(false);
1211 m_pTrimButton->setEnabled(false);
1212 m_pStopButton->setEnabled(false);
1214 VOGLEDITOR_DISABLE_TAB(ui->machineInfoTab);
1216 reset_snapshot_ui();
1219 void VoglEditor::reset_snapshot_ui()
1221 m_currentSnapshot = NULL;
1223 m_framebufferExplorer->clear();
1224 m_textureExplorer->clear();
1225 m_renderbufferExplorer->clear();
1226 m_programExplorer->clear();
1227 m_shaderExplorer->clear();
1229 ui->stateTreeView->setModel(NULL);
1231 QWidget* pCurrentTab = ui->tabWidget->currentWidget();
1233 VOGLEDITOR_DISABLE_TAB(ui->stateTab);
1234 VOGLEDITOR_DISABLE_TAB(ui->framebufferTab);
1235 VOGLEDITOR_DISABLE_TAB(ui->programTab);
1236 VOGLEDITOR_DISABLE_TAB(ui->shaderTab);
1237 VOGLEDITOR_DISABLE_TAB(ui->textureTab);
1238 VOGLEDITOR_DISABLE_TAB(ui->renderbufferTab);
1240 ui->tabWidget->setCurrentWidget(pCurrentTab);
1243 /// 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
1244 /// and also has no children. The pMostRecentSnapshot parameter will be updated to point to the desired snapshot.
1245 /// This function does not follow a traditional DFS search because we need to find the desired snapshot then return the one before it.
1246 /// An alternative approach would be to keep a stack of the found snapshots, or even to build up that stack / list as the user
1247 /// generates new snapshots.
1248 vogleditor_gl_state_snapshot* VoglEditor::findMostRecentSnapshot_helper(vogleditor_apiCallTreeItem* pItem, vogleditor_gl_state_snapshot*& pMostRecentSnapshot, const vogleditor_gl_state_snapshot* pCurSnapshot)
1250 // check if this item has a snapshot shot
1251 if (pItem->has_snapshot() && pItem->get_snapshot()->is_valid())
1253 vogleditor_gl_state_snapshot* pTmp = pItem->get_snapshot();
1254 if (pTmp == pCurSnapshot)
1256 // if we've reached the item with the current snapshot, we want to return the previous snapshot.
1261 // update most recent snapshot
1262 pMostRecentSnapshot = pTmp;
1266 for (int i = 0; i < pItem->childCount(); i++)
1268 vogleditor_gl_state_snapshot* pTmp = findMostRecentSnapshot_helper(pItem->child(i), pMostRecentSnapshot, pCurSnapshot);
1271 if (pTmp == pCurSnapshot)
1273 // if we've reached the item with the current snapshot, we want to return the previous snapshot.
1278 // update most recent snapshot
1279 pMostRecentSnapshot = pTmp;
1287 /// This function exists just to simplify the interaction with the helper, so that there no confusion between
1288 /// whether the returned value, or passed in reference parameter should be used as the most recent snapshot.
1289 /// It will either return NULL if there is no recent snapshot (which should only happen for the very first snapshot
1290 /// in a trace), or a pointer to a valid snapshot.
1291 vogleditor_gl_state_snapshot* VoglEditor::findMostRecentSnapshot(vogleditor_apiCallTreeItem* pItem, const vogleditor_gl_state_snapshot* pCurSnapshot)
1293 vogleditor_gl_state_snapshot* pMostRecentSnapshot = NULL;
1294 findMostRecentSnapshot_helper(pItem, pMostRecentSnapshot, pCurSnapshot);
1295 return pMostRecentSnapshot;
1298 void VoglEditor::update_ui_for_snapshot(vogleditor_gl_state_snapshot* pStateSnapshot)
1300 if (pStateSnapshot == NULL)
1302 reset_snapshot_ui();
1306 if (pStateSnapshot->is_valid() == false)
1308 reset_snapshot_ui();
1312 if (m_currentSnapshot == pStateSnapshot)
1314 // no need to update if it is the same snapshot
1318 m_currentSnapshot = pStateSnapshot;
1320 if (ui->stateTreeView->model() != NULL)
1322 if (static_cast<vogleditor_QStateTreeModel*>(ui->stateTreeView->model())->get_snapshot() == m_currentSnapshot)
1324 // displaying the same snapshot, return
1329 QCursor origCursor = this->cursor();
1330 this->setCursor(Qt::WaitCursor);
1333 vogleditor_QStateTreeModel* pStateModel = new vogleditor_QStateTreeModel(NULL);
1335 vogleditor_gl_state_snapshot* pBaseSnapshot = findMostRecentSnapshot(m_pApicallTreeModel->root(), m_currentSnapshot);
1336 pStateModel->set_diff_base_snapshot(pBaseSnapshot);
1338 pStateModel->set_snapshot(pStateSnapshot);
1340 ui->stateTreeView->setModel(pStateModel);
1341 ui->stateTreeView->expandToDepth(1);
1342 ui->stateTreeView->setColumnWidth(0, ui->stateTreeView->width() * 0.5);
1344 VOGLEDITOR_ENABLE_TAB(ui->stateTab);
1346 if (pStateSnapshot->get_contexts().size() > 0)
1348 vogl_trace_ptr_value curContextHandle = pStateSnapshot->get_cur_trace_context();
1349 if (curContextHandle != 0)
1351 vogl_context_snapshot* pContext = pStateSnapshot->get_context(curContextHandle);
1354 vogl_gl_object_state_ptr_vec textureObjects;
1355 pContext->get_all_objects_of_category(cGLSTTexture, textureObjects);
1356 m_textureExplorer->set_texture_objects(textureObjects);
1358 GLuint curActiveTextureUnit = pContext->get_general_state().get_value<GLuint>(GL_ACTIVE_TEXTURE);
1359 if (curActiveTextureUnit >= GL_TEXTURE0 && curActiveTextureUnit < (GL_TEXTURE0 + pContext->get_context_info().get_max_texture_image_units()))
1361 GLuint cur2DBinding = pContext->get_general_state().get_value<GLuint>(GL_TEXTURE_2D_BINDING_EXT, curActiveTextureUnit - GL_TEXTURE0);
1362 displayTexture(cur2DBinding, false);
1366 vogl_gl_object_state_ptr_vec renderbufferObjects;
1367 pContext->get_all_objects_of_category(cGLSTRenderbuffer, renderbufferObjects);
1368 m_renderbufferExplorer->set_texture_objects(renderbufferObjects);
1369 if (renderbufferObjects.size() > 0) { VOGLEDITOR_ENABLE_TAB(ui->renderbufferTab); }
1372 vogl_gl_object_state_ptr_vec framebufferObjects;
1373 pContext->get_all_objects_of_category(cGLSTFramebuffer, framebufferObjects);
1374 m_framebufferExplorer->set_framebuffer_objects(framebufferObjects, *pContext, pStateSnapshot->get_default_framebuffer());
1375 GLuint64 curDrawFramebuffer = pContext->get_general_state().get_value<GLuint64>(GL_DRAW_FRAMEBUFFER_BINDING);
1376 displayFramebuffer(curDrawFramebuffer, false);
1379 vogl_gl_object_state_ptr_vec programObjects;
1380 pContext->get_all_objects_of_category(cGLSTProgram, programObjects);
1381 m_programExplorer->set_program_objects(programObjects);
1382 GLuint64 curProgram = pContext->get_general_state().get_value<GLuint64>(GL_CURRENT_PROGRAM);
1383 m_programExplorer->set_active_program(curProgram);
1384 if (programObjects.size() > 0) { VOGLEDITOR_ENABLE_TAB(ui->programTab); }
1387 vogl_gl_object_state_ptr_vec shaderObjects;
1388 pContext->get_all_objects_of_category(cGLSTShader, shaderObjects);
1389 m_shaderExplorer->set_shader_objects(shaderObjects);
1390 if (curProgram != 0)
1392 for (vogl_gl_object_state_ptr_vec::iterator iter = programObjects.begin(); iter != programObjects.end(); iter++)
1394 if ((*iter)->get_snapshot_handle() == curProgram)
1396 vogl_program_state* pProgramState = static_cast<vogl_program_state*>(*iter);
1397 if (pProgramState->get_attached_shaders().size() > 0)
1399 uint curShader = pProgramState->get_attached_shaders()[0];
1400 m_shaderExplorer->set_active_shader(curShader);
1406 if (shaderObjects.size() > 0) { VOGLEDITOR_ENABLE_TAB(ui->shaderTab); }
1410 this->setCursor(origCursor);
1413 void VoglEditor::on_stateTreeView_clicked(const QModelIndex &index)
1415 vogleditor_stateTreeItem* pStateItem = static_cast<vogleditor_stateTreeItem*>(index.internalPointer());
1416 if (pStateItem == NULL)
1421 switch(pStateItem->getStateType())
1423 case vogleditor_stateTreeItem::cTEXTURE:
1425 vogleditor_stateTreeTextureItem* pTextureItem = static_cast<vogleditor_stateTreeTextureItem*>(pStateItem);
1426 if (pTextureItem == NULL)
1431 displayTexture(pTextureItem->get_texture_state()->get_snapshot_handle(), true);
1435 case vogleditor_stateTreeItem::cPROGRAM:
1437 vogleditor_stateTreeProgramItem* pProgramItem = static_cast<vogleditor_stateTreeProgramItem*>(pStateItem);
1438 if (pProgramItem == NULL)
1443 displayProgram(pProgramItem->get_current_state()->get_snapshot_handle(), true);
1447 case vogleditor_stateTreeItem::cSHADER:
1449 vogleditor_stateTreeShaderItem* pShaderItem = static_cast<vogleditor_stateTreeShaderItem*>(pStateItem);
1450 if (pShaderItem == NULL)
1455 displayShader(pShaderItem->get_current_state()->get_snapshot_handle(), true);
1459 case vogleditor_stateTreeItem::cFRAMEBUFFER:
1461 vogleditor_stateTreeFramebufferItem* pFramebufferItem = static_cast<vogleditor_stateTreeFramebufferItem*>(pStateItem);
1462 if (pFramebufferItem == NULL)
1467 displayFramebuffer(pFramebufferItem->get_framebuffer_state()->get_snapshot_handle(), true);
1471 case vogleditor_stateTreeItem::cDEFAULT:
1478 bool VoglEditor::displayShader(GLuint64 shaderHandle, bool bBringTabToFront)
1480 bool bDisplayed = false;
1481 if (m_shaderExplorer->set_active_shader(shaderHandle))
1483 if (bBringTabToFront)
1485 ui->tabWidget->setCurrentWidget(ui->shaderTab);
1492 void VoglEditor::displayProgram(GLuint64 programHandle, bool bBringTabToFront)
1494 if (m_programExplorer->set_active_program(programHandle))
1496 if (bBringTabToFront)
1498 ui->tabWidget->setCurrentWidget(ui->programTab);
1503 void VoglEditor::displayFramebuffer(GLuint64 framebufferHandle, bool bBringTabToFront)
1505 bool bDisplayedFBO = m_framebufferExplorer->set_active_framebuffer(framebufferHandle);
1509 VOGLEDITOR_ENABLE_TAB(ui->framebufferTab);
1510 if (bBringTabToFront)
1512 ui->tabWidget->setCurrentWidget(ui->framebufferTab);
1517 bool VoglEditor::displayTexture(GLuint64 textureHandle, bool bBringTabToFront)
1519 bool bDisplayedTexture = m_textureExplorer->set_active_texture(textureHandle);
1521 if (bDisplayedTexture)
1523 VOGLEDITOR_ENABLE_TAB(ui->textureTab);
1524 if (bBringTabToFront)
1526 ui->tabWidget->setCurrentWidget(ui->textureTab);
1530 return bDisplayedTexture;
1533 void VoglEditor::on_treeView_currentChanged(const QModelIndex & current, const QModelIndex & previous)
1535 VOGL_NOTE_UNUSED(previous);
1536 onApiCallSelected(current, false);
1539 void VoglEditor::on_treeView_clicked(const QModelIndex &index)
1541 onApiCallSelected(index, true);
1544 void VoglEditor::onApiCallSelected(const QModelIndex &index, bool bAllowStateSnapshot)
1546 vogleditor_apiCallTreeItem* pCallTreeItem = static_cast<vogleditor_apiCallTreeItem*>(index.internalPointer());
1547 if (pCallTreeItem == NULL)
1552 vogleditor_frameItem* pFrameItem = pCallTreeItem->frameItem();
1553 vogleditor_apiCallItem* pApiCallItem = pCallTreeItem->apiCallItem();
1555 if (bAllowStateSnapshot && pCallTreeItem == m_pCurrentCallTreeItem)
1557 // we can only get snapshots for specific API calls
1558 if (pApiCallItem != NULL && pApiCallItem->needs_snapshot())
1560 // get the snapshot after the current api call
1561 vogleditor_gl_state_snapshot* pNewSnapshot = NULL;
1562 QCursor origCursor = cursor();
1563 setCursor(Qt::WaitCursor);
1564 m_traceReplayer.replay(m_pTraceReader, m_pApicallTreeModel->root(), &pNewSnapshot, pApiCallItem->globalCallIndex(), false);
1565 setCursor(origCursor);
1566 pCallTreeItem->set_snapshot(pNewSnapshot);
1570 update_ui_for_snapshot(pCallTreeItem->get_snapshot());
1572 if (pApiCallItem != NULL && m_pCurrentCallTreeItem != pCallTreeItem)
1574 if (m_backtraceToJsonMap.size() > 0)
1577 json_node* pBacktraceNode = m_backtraceToJsonMap[(uint)pApiCallItem->backtraceHashIndex()];
1578 if (pBacktraceNode != NULL)
1580 json_node* pAddrs = pBacktraceNode->find_child_array("addrs");
1581 json_node* pSyms = pBacktraceNode->find_child_array("syms");
1583 for (uint i = 0; i < pAddrs->size(); i++)
1585 tmp += pAddrs->get_value(i).as_string_ptr();
1589 tmp += pSyms->get_value(i).as_string_ptr();
1594 ui->backtraceText->setText(tmp);
1598 if (pApiCallItem != NULL)
1600 m_timeline->setCurrentApiCall(pApiCallItem->globalCallIndex());
1603 if (pFrameItem != NULL)
1605 m_timeline->setCurrentFrame(pFrameItem->frameNumber());
1608 m_timeline->repaint();
1610 m_pCurrentCallTreeItem = pCallTreeItem;
1613 void VoglEditor::selectApicallModelIndex(QModelIndex index, bool scrollTo, bool select)
1615 // make sure the index is visible
1616 QModelIndex parentIndex = index.parent();
1617 while (parentIndex.isValid())
1619 if (ui->treeView->isExpanded(parentIndex) == false)
1621 ui->treeView->expand(parentIndex);
1623 parentIndex = parentIndex.parent();
1626 // scroll to the index
1629 ui->treeView->scrollTo(index);
1635 ui->treeView->setCurrentIndex(index);
1639 void VoglEditor::on_searchTextBox_textChanged(const QString &searchText)
1641 QPalette palette(ui->searchTextBox->palette());
1642 palette.setColor(QPalette::Base, m_searchTextboxBackgroundColor);
1643 ui->searchTextBox->setPalette(palette);
1645 if (m_pApicallTreeModel != NULL)
1647 m_pApicallTreeModel->set_highlight_search_string(searchText);
1650 // need to briefly give the treeview focus so that it properly redraws and highlights the matching rows
1651 // then return focus to the search textbox so that typed keys are not lost
1652 ui->treeView->setFocus();
1653 ui->searchTextBox->setFocus();
1656 void VoglEditor::on_searchNextButton_clicked()
1658 if (m_pApicallTreeModel != NULL)
1660 QModelIndex index = m_pApicallTreeModel->find_next_search_result(m_pCurrentCallTreeItem, ui->searchTextBox->text());
1661 if (index.isValid())
1663 selectApicallModelIndex(index, true, true);
1664 ui->treeView->setFocus();
1669 void VoglEditor::on_searchPrevButton_clicked()
1671 if (m_pApicallTreeModel != NULL)
1673 QModelIndex index = m_pApicallTreeModel->find_prev_search_result(m_pCurrentCallTreeItem, ui->searchTextBox->text());
1674 if (index.isValid())
1676 selectApicallModelIndex(index, true, true);
1677 ui->treeView->setFocus();
1682 void VoglEditor::on_prevSnapshotButton_clicked()
1684 if (m_pApicallTreeModel != NULL)
1686 vogleditor_apiCallTreeItem* pPrevItemWithSnapshot = m_pApicallTreeModel->find_prev_snapshot(m_pCurrentCallTreeItem);
1687 if (pPrevItemWithSnapshot != NULL)
1689 selectApicallModelIndex(m_pApicallTreeModel->indexOf(pPrevItemWithSnapshot), true, true);
1690 ui->treeView->setFocus();
1695 void VoglEditor::on_nextSnapshotButton_clicked()
1697 if (m_pApicallTreeModel != NULL)
1699 vogleditor_apiCallTreeItem* pNextItemWithSnapshot = m_pApicallTreeModel->find_next_snapshot(m_pCurrentCallTreeItem);
1700 if (pNextItemWithSnapshot != NULL)
1702 selectApicallModelIndex(m_pApicallTreeModel->indexOf(pNextItemWithSnapshot), true, true);
1703 ui->treeView->setFocus();
1708 void VoglEditor::on_prevDrawcallButton_clicked()
1710 if (m_pApicallTreeModel != NULL)
1712 vogleditor_apiCallTreeItem* pPrevItem = m_pApicallTreeModel->find_prev_drawcall(m_pCurrentCallTreeItem);
1713 if (pPrevItem != NULL)
1715 selectApicallModelIndex(m_pApicallTreeModel->indexOf(pPrevItem), true, true);
1716 ui->treeView->setFocus();
1721 void VoglEditor::on_nextDrawcallButton_clicked()
1723 if (m_pApicallTreeModel != NULL)
1725 vogleditor_apiCallTreeItem* pNextItem = m_pApicallTreeModel->find_next_drawcall(m_pCurrentCallTreeItem);
1726 if (pNextItem != NULL)
1728 selectApicallModelIndex(m_pApicallTreeModel->indexOf(pNextItem), true, true);
1729 ui->treeView->setFocus();
1734 void VoglEditor::on_program_edited(vogl_program_state* pNewProgramState)
1736 VOGL_NOTE_UNUSED(pNewProgramState);
1738 m_currentSnapshot->set_edited(true);
1740 // update all the snapshot flags
1741 bool bFoundEditedSnapshot = false;
1742 recursive_update_snapshot_flags(m_pApicallTreeModel->root(), bFoundEditedSnapshot);
1744 // 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,
1745 // 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
1746 // 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
1747 // API call instead of cycling through state options).
1748 ui->treeView->setFocus();
1751 // if an edited snapshot has already been found, mark the node (and all children) as dirty.
1752 void VoglEditor::recursive_update_snapshot_flags(vogleditor_apiCallTreeItem* pItem, bool& bFoundEditedSnapshot)
1754 // check if this item has a snapshot shot
1755 if (pItem->has_snapshot())
1757 if (!bFoundEditedSnapshot)
1759 if (pItem->get_snapshot()->is_edited())
1761 bFoundEditedSnapshot = true;
1765 pItem->get_snapshot()->set_outdated(false);
1770 pItem->get_snapshot()->set_outdated(true);
1774 for (int i = 0; i < pItem->childCount(); i++)
1776 recursive_update_snapshot_flags(pItem->child(i), bFoundEditedSnapshot);
1780 #undef VOGLEDITOR_DISABLE_TAB
1781 #undef VOGLEDITOR_ENABLE_TAB
1783 void VoglEditor::on_actionSave_Session_triggered()
1785 QString baseName = m_openFilename;
1787 int lastIndex = baseName.lastIndexOf('.');
1788 if (lastIndex != -1)
1790 baseName = baseName.remove(lastIndex, baseName.size() - lastIndex);
1793 QString suggestedName = baseName + "-vogleditor.json";
1795 QString sessionFilename = QFileDialog::getSaveFileName(this, tr("Save Debug Session"), suggestedName, tr("JSON (*.json)"));
1797 if (!save_session_to_disk(sessionFilename))
1799 m_statusLabel->setText("ERROR: Failed to save session");
1803 void VoglEditor::on_actionOpen_Session_triggered()
1805 QString sessionFilename = QFileDialog::getOpenFileName(this, tr("Load Debug Session"), QString(), tr("JSON (*.json)"));
1807 QCursor origCursor = this->cursor();
1808 setCursor(Qt::WaitCursor);
1810 if (!load_session_from_disk(sessionFilename))
1812 m_statusLabel->setText("ERROR: Failed to load session");
1815 setCursor(origCursor);
1818 void VoglEditor::on_searchTextBox_returnPressed()
1820 if (m_pApicallTreeModel != NULL)
1822 QModelIndex index = m_pApicallTreeModel->find_next_search_result(m_pCurrentCallTreeItem, ui->searchTextBox->text());
1823 if (index.isValid())
1825 // a valid item was found, scroll to it and select it
1826 selectApicallModelIndex(index, true, true);
1830 // no items were found, so set the textbox background to red (it will get cleared to the original color if the user edits the search text)
1831 QPalette palette(ui->searchTextBox->palette());
1832 palette.setColor(QPalette::Base, Qt::red);
1833 ui->searchTextBox->setPalette(palette);