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 "vogleditor_qapicalltreemodel.h"
28 #include "vogl_common.h"
29 #include "vogl_trace_file_reader.h"
30 #include "vogl_trace_packet.h"
31 #include "vogl_trace_stream_types.h"
32 #include "vogleditor_gl_state_snapshot.h"
34 vogleditor_QApiCallTreeModel::vogleditor_QApiCallTreeModel(vogl_trace_file_reader* reader, QObject *parent)
35 : QAbstractItemModel(parent)
37 m_rootItem = vogl_new(vogleditor_apiCallTreeItem, this);
38 setupModelData(reader, m_rootItem);
41 vogleditor_QApiCallTreeModel::~vogleditor_QApiCallTreeModel()
43 if (m_rootItem != NULL)
45 vogl_delete(m_rootItem);
53 void vogleditor_QApiCallTreeModel::setupModelData(vogl_trace_file_reader* pTrace_reader, vogleditor_apiCallTreeItem *parent)
55 const vogl_trace_stream_start_of_file_packet &sof_packet = pTrace_reader->get_sof_packet();
56 VOGL_NOTE_UNUSED(sof_packet);
58 uint64_t total_swaps = 0;
59 // TODO probably need to handle eof_packet
60 //bool found_eof_packet = false;
62 // make a PendingFrame node to hold the api calls
63 // this will remain in the pending state until the first
64 // api call is seen, then it will be made the CurFrame and
65 // appended to the parent
66 vogleditor_frameItem* pCurFrame = NULL;
67 vogleditor_apiCallTreeItem* pCurParent = parent;
69 // Make a PendingSnapshot that may or may not be populated when reading the trace.
70 // This snapshot will be assigned to the next API call that occurs.
71 vogleditor_gl_state_snapshot* pPendingSnapshot = NULL;
73 m_trace_ctypes.init(pTrace_reader->get_sof_packet().m_pointer_sizes);
77 vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
79 if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
81 vogl_error_printf("Failed reading from trace file!\n");
85 if (read_status == vogl_trace_file_reader::cEOF)
87 vogl_printf("At trace file EOF on swap %" PRIu64 "\n", total_swaps);
91 const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf(); VOGL_NOTE_UNUSED(packet_buf);
93 const vogl_trace_stream_packet_base &base_packet = pTrace_reader->get_base_packet(); VOGL_NOTE_UNUSED(base_packet);
94 const vogl_trace_gl_entrypoint_packet *pGL_packet = NULL;
96 if (pTrace_reader->get_packet_type() == cTSPTGLEntrypoint)
98 vogl_trace_packet* pTrace_packet = vogl_new(vogl_trace_packet, &m_trace_ctypes);
100 if (!pTrace_packet->deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
102 console::error("%s: Failed parsing GL entrypoint packet\n", VOGL_FUNCTION_NAME);
106 pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
107 gl_entrypoint_id_t entrypoint_id = static_cast<gl_entrypoint_id_t>(pGL_packet->m_entrypoint_id);
109 if (entrypoint_id == VOGL_ENTRYPOINT_glInternalTraceCommandRAD)
111 // Check if this is a state snapshot.
112 // This is entirely optional since the client is designed to dynamically get new snapshots
113 // if they don't exist.
114 GLuint cmd = pTrace_packet->get_param_value<GLuint>(0);
115 GLuint size = pTrace_packet->get_param_value<GLuint>(1); VOGL_NOTE_UNUSED(size);
117 if (cmd == cITCRKeyValueMap)
119 key_value_map &kvm = pTrace_packet->get_key_value_map();
121 dynamic_string cmd_type(kvm.get_string("command_type"));
122 if (cmd_type == "state_snapshot")
124 dynamic_string id(kvm.get_string("binary_id"));
127 vogl_warning_printf("%s: Missing binary_id field in glInternalTraceCommandRAD key_valye_map command type: \"%s\"\n", VOGL_FUNCTION_NAME, cmd_type.get_ptr());
131 uint8_vec snapshot_data;
133 timed_scope ts("get_multi_blob_manager().get");
134 if (!pTrace_reader->get_multi_blob_manager().get(id, snapshot_data) || (snapshot_data.is_empty()))
136 vogl_warning_printf("%s: Failed reading snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
143 timed_scope ts("doc.binary_deserialize");
144 if (!doc.binary_deserialize(snapshot_data) || (!doc.get_root()))
146 vogl_warning_printf("%s: Failed deserializing JSON snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
151 vogl_gl_state_snapshot* pGLSnapshot = vogl_new(vogl_gl_state_snapshot);
152 pPendingSnapshot = vogl_new(vogleditor_gl_state_snapshot, pGLSnapshot);
154 timed_scope ts("pPendingSnapshot->deserialize");
155 if (!pPendingSnapshot->get_snapshot()->deserialize(*doc.get_root(), pTrace_reader->get_multi_blob_manager(), &m_trace_ctypes))
157 vogl_delete(pPendingSnapshot);
158 pPendingSnapshot = NULL;
160 vogl_warning_printf("%s: Failed deserializing snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
168 const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[entrypoint_id];
170 QString funcCall = entrypoint_desc.m_pName;
173 funcCall.append("( ");
174 dynamic_string paramStr;
175 for (uint param_index = 0; param_index < pTrace_packet->total_params(); param_index++)
177 if (param_index != 0)
178 funcCall.append(", ");
181 pTrace_packet->pretty_print_param(paramStr, param_index, false);
183 funcCall.append(paramStr.c_str());
185 funcCall.append(" )");
187 if (pTrace_packet->has_return_value())
189 funcCall.append(" = ");
191 pTrace_packet->pretty_print_return_value(paramStr, false);
192 funcCall.append(paramStr.c_str());
195 // if we don't have a current frame, make a new frame node
196 // and append it to the parent
197 if (pCurFrame == NULL)
199 pCurFrame = vogl_new(vogleditor_frameItem, total_swaps);
200 vogleditor_apiCallTreeItem* pNewFrameNode = vogl_new(vogleditor_apiCallTreeItem, pCurFrame, pCurParent);
201 pCurParent->appendChild(pNewFrameNode);
202 m_itemList.append(pNewFrameNode);
204 if (pPendingSnapshot != NULL)
206 pCurFrame->set_snapshot(pPendingSnapshot);
207 pPendingSnapshot = NULL;
210 // update current parent
211 pCurParent = pNewFrameNode;
214 // make item and node for the api call
215 vogleditor_apiCallItem* pCallItem = vogl_new(vogleditor_apiCallItem, pCurFrame, pTrace_packet, *pGL_packet);
216 pCurFrame->appendCall(pCallItem);
218 if (pPendingSnapshot != NULL)
220 pCallItem->set_snapshot(pPendingSnapshot);
221 pPendingSnapshot = NULL;
224 vogleditor_apiCallTreeItem* item = vogl_new(vogleditor_apiCallTreeItem, funcCall, pCallItem, pCurParent);
225 pCurParent->appendChild(item);
226 m_itemList.append(item);
228 if (vogl_is_swap_buffers_entrypoint(entrypoint_id))
232 // reset the CurParent back to the original parent so that the next frame will be at the root level
235 // reset the CurFrame so that a new frame node will be created on the next api call
238 else if (entrypoint_id == VOGL_ENTRYPOINT_glBegin)
240 // items in the glBegin/glEnd block will be nested, including the glEnd
243 else if (entrypoint_id == VOGL_ENTRYPOINT_glEnd)
245 // move the parent back one level of the hierarchy, to its own parent
246 pCurParent = pCurParent->parent();
250 if (pTrace_reader->get_packet_type() == cTSPTEOF)
252 //found_eof_packet = true;
253 vogl_printf("Found trace file EOF packet on swap %" PRIu64 "\n", total_swaps);
259 QModelIndex vogleditor_QApiCallTreeModel::index(int row, int column, const QModelIndex &parent) const
261 if (!hasIndex(row, column, parent))
262 return QModelIndex();
264 vogleditor_apiCallTreeItem *parentItem;
266 if (!parent.isValid())
267 parentItem = m_rootItem;
269 parentItem = static_cast<vogleditor_apiCallTreeItem*>(parent.internalPointer());
271 vogleditor_apiCallTreeItem *childItem = parentItem->child(row);
273 return createIndex(row, column, childItem);
275 return QModelIndex();
278 QModelIndex vogleditor_QApiCallTreeModel::indexOf(const vogleditor_apiCallTreeItem* pItem) const
281 return createIndex(pItem->row(), VOGL_ACTC_APICALL, (void*)pItem);
283 return QModelIndex();
286 QModelIndex vogleditor_QApiCallTreeModel::parent(const QModelIndex &index) const
288 if (!index.isValid())
289 return QModelIndex();
291 vogleditor_apiCallTreeItem* childItem = static_cast<vogleditor_apiCallTreeItem*>(index.internalPointer());
292 vogleditor_apiCallTreeItem* parentItem = childItem->parent();
294 if (parentItem == m_rootItem)
295 return QModelIndex();
297 return createIndex(parentItem->row(), VOGL_ACTC_APICALL, parentItem);
300 int vogleditor_QApiCallTreeModel::rowCount(const QModelIndex &parent) const
302 vogleditor_apiCallTreeItem* parentItem;
303 if (parent.column() > 0)
306 if (!parent.isValid())
307 parentItem = m_rootItem;
309 parentItem = static_cast<vogleditor_apiCallTreeItem*>(parent.internalPointer());
311 return parentItem->childCount();
314 int vogleditor_QApiCallTreeModel::columnCount(const QModelIndex &parent) const
316 VOGL_NOTE_UNUSED(parent);
317 return VOGL_MAX_ACTC;
320 QVariant vogleditor_QApiCallTreeModel::data(const QModelIndex &index, int role) const
322 if (!index.isValid())
325 vogleditor_apiCallTreeItem* pItem = static_cast<vogleditor_apiCallTreeItem*>(index.internalPointer());
327 // highlight the API call cell if it has a substring which matches the searchString
328 if (role == Qt::BackgroundRole && index.column() == VOGL_ACTC_APICALL)
330 if (!m_searchString.isEmpty())
332 QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
333 QString string = data.toString();
334 if (string.contains(m_searchString, Qt::CaseInsensitive))
336 return QColor(Qt::yellow);
341 return pItem->columnData(index.column(), role);
344 Qt::ItemFlags vogleditor_QApiCallTreeModel::flags(const QModelIndex &index) const
346 if (!index.isValid())
349 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
352 QVariant vogleditor_QApiCallTreeModel::headerData(int section, Qt::Orientation orientation,
355 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
356 return m_rootItem->columnData(section, role);
361 void vogleditor_QApiCallTreeModel::set_highlight_search_string(const QString searchString)
363 m_searchString = searchString;
366 QModelIndex vogleditor_QApiCallTreeModel::find_prev_search_result(vogleditor_apiCallTreeItem* start, const QString searchText)
368 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
372 if (iter.findNext(start) == false)
374 // the object wasn't found in the list, so return a default (invalid) item
375 return QModelIndex();
378 // need to back up past the current item
383 // set the iterator to the back so that searching starts from the end of the list
387 // now the iterator is pointing to the desired start object in the list,
388 // continually check the prev item and find one with a snapshot
389 vogleditor_apiCallTreeItem* pFound = NULL;
390 while (iter.hasPrevious())
392 vogleditor_apiCallTreeItem* pItem = iter.peekPrevious();
393 QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
394 QString string = data.toString();
395 if (string.contains(searchText, Qt::CaseInsensitive))
404 return indexOf(pFound);
407 QModelIndex vogleditor_QApiCallTreeModel::find_next_search_result(vogleditor_apiCallTreeItem* start, const QString searchText)
409 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
413 if (iter.findNext(start) == false)
415 // the object wasn't found in the list, so return a default (invalid) item
416 return QModelIndex();
420 // now the iterator is pointing to the desired start object in the list,
421 // continually check the next item and find one with a snapshot
422 vogleditor_apiCallTreeItem* pFound = NULL;
423 while (iter.hasNext())
425 vogleditor_apiCallTreeItem* pItem = iter.peekNext();
426 QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
427 QString string = data.toString();
428 if (string.contains(searchText, Qt::CaseInsensitive))
437 return indexOf(pFound);
440 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_prev_snapshot(vogleditor_apiCallTreeItem* start)
442 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
446 if (iter.findNext(start) == false)
448 // the object wasn't found in the list
452 // need to back up past the current item
457 // set the iterator to the back so that searching starts from the end of the list
461 // now the iterator is pointing to the desired start object in the list,
462 // continually check the prev item and find one with a snapshot
463 vogleditor_apiCallTreeItem* pFound = NULL;
464 while (iter.hasPrevious())
466 if (iter.peekPrevious()->has_snapshot())
468 pFound = iter.peekPrevious();
478 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_next_snapshot(vogleditor_apiCallTreeItem* start)
480 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
482 // if start is NULL, then search will begin from top, otherwise it will begin from the start item and search onwards
485 if (iter.findNext(start) == false)
487 // the object wasn't found in the list
492 // now the iterator is pointing to the desired start object in the list,
493 // continually check the next item and find one with a snapshot
494 vogleditor_apiCallTreeItem* pFound = NULL;
495 while (iter.hasNext())
497 if (iter.peekNext()->has_snapshot())
499 pFound = iter.peekNext();
510 vogleditor_apiCallTreeItem *vogleditor_QApiCallTreeModel::find_prev_drawcall(vogleditor_apiCallTreeItem* start)
512 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
516 if (iter.findNext(start) == false)
518 // the object wasn't found in the list
522 // need to back up past the current item
527 // set the iterator to the back so that searching starts from the end of the list
531 // now the iterator is pointing to the desired start object in the list,
532 // continually check the prev item and find one with a snapshot
533 vogleditor_apiCallTreeItem* pFound = NULL;
534 while (iter.hasPrevious())
536 vogleditor_apiCallTreeItem* pItem = iter.peekPrevious();
537 if (pItem->apiCallItem() != NULL)
539 gl_entrypoint_id_t entrypointId = static_cast<gl_entrypoint_id_t>(pItem->apiCallItem()->getGLPacket()->m_entrypoint_id);
540 if (vogl_is_draw_entrypoint(entrypointId) ||
541 vogl_is_clear_entrypoint(entrypointId) ||
542 (entrypointId == VOGL_ENTRYPOINT_glBitmap) ||
543 (entrypointId == VOGL_ENTRYPOINT_glEnd))
545 pFound = iter.peekPrevious();
556 vogleditor_apiCallTreeItem *vogleditor_QApiCallTreeModel::find_next_drawcall(vogleditor_apiCallTreeItem* start)
558 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
560 if (iter.findNext(start) == false)
562 // the object wasn't found in the list
566 // now the iterator is pointing to the desired start object in the list,
567 // continually check the next item and find one with a snapshot
568 vogleditor_apiCallTreeItem* pFound = NULL;
569 while (iter.hasNext())
571 vogleditor_apiCallTreeItem* pItem = iter.peekNext();
572 if (pItem->apiCallItem() != NULL)
574 gl_entrypoint_id_t entrypointId = static_cast<gl_entrypoint_id_t>(pItem->apiCallItem()->getGLPacket()->m_entrypoint_id);
575 if (vogl_is_draw_entrypoint(entrypointId) ||
576 vogl_is_clear_entrypoint(entrypointId) ||
577 (entrypointId == VOGL_ENTRYPOINT_glBitmap) ||
578 (entrypointId == VOGL_ENTRYPOINT_glEnd))
580 pFound = iter.peekNext();
591 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_call_number(uint64_t callNumber)
593 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
595 vogleditor_apiCallTreeItem* pFound = NULL;
596 while (iter.hasNext())
598 vogleditor_apiCallTreeItem* pItem = iter.peekNext();
599 if (pItem->apiCallItem() != NULL)
601 if (pItem->apiCallItem()->globalCallIndex() == callNumber)
603 pFound = iter.peekNext();
614 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_frame_number(uint64_t frameNumber)
616 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
618 vogleditor_apiCallTreeItem* pFound = NULL;
619 while (iter.hasNext())
621 vogleditor_apiCallTreeItem* pItem = iter.peekNext();
622 if (pItem->frameItem() != NULL)
624 if (pItem->frameItem()->frameNumber() == frameNumber)
626 pFound = iter.peekNext();