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 = new vogleditor_apiCallTreeItem(this);
38 setupModelData(reader, m_rootItem);
41 vogleditor_QApiCallTreeModel::~vogleditor_QApiCallTreeModel()
43 if (m_rootItem != NULL)
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 = 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
240 if (pTrace_reader->get_packet_type() == cTSPTEOF)
242 //found_eof_packet = true;
243 vogl_printf("Found trace file EOF packet on swap %" PRIu64 "\n", total_swaps);
249 QModelIndex vogleditor_QApiCallTreeModel::index(int row, int column, const QModelIndex &parent) const
251 if (!hasIndex(row, column, parent))
252 return QModelIndex();
254 vogleditor_apiCallTreeItem *parentItem;
256 if (!parent.isValid())
257 parentItem = m_rootItem;
259 parentItem = static_cast<vogleditor_apiCallTreeItem*>(parent.internalPointer());
261 vogleditor_apiCallTreeItem *childItem = parentItem->child(row);
263 return createIndex(row, column, childItem);
265 return QModelIndex();
268 QModelIndex vogleditor_QApiCallTreeModel::indexOf(const vogleditor_apiCallTreeItem* pItem) const
271 return createIndex(pItem->row(), VOGL_ACTC_APICALL, (void*)pItem);
273 return QModelIndex();
276 QModelIndex vogleditor_QApiCallTreeModel::parent(const QModelIndex &index) const
278 if (!index.isValid())
279 return QModelIndex();
281 vogleditor_apiCallTreeItem* childItem = static_cast<vogleditor_apiCallTreeItem*>(index.internalPointer());
282 vogleditor_apiCallTreeItem* parentItem = childItem->parent();
284 if (parentItem == m_rootItem)
285 return QModelIndex();
287 return createIndex(parentItem->row(), VOGL_ACTC_APICALL, parentItem);
290 int vogleditor_QApiCallTreeModel::rowCount(const QModelIndex &parent) const
292 vogleditor_apiCallTreeItem* parentItem;
293 if (parent.column() > 0)
296 if (!parent.isValid())
297 parentItem = m_rootItem;
299 parentItem = static_cast<vogleditor_apiCallTreeItem*>(parent.internalPointer());
301 return parentItem->childCount();
304 int vogleditor_QApiCallTreeModel::columnCount(const QModelIndex &parent) const
306 VOGL_NOTE_UNUSED(parent);
307 return VOGL_MAX_ACTC;
310 QVariant vogleditor_QApiCallTreeModel::data(const QModelIndex &index, int role) const
312 if (!index.isValid())
315 vogleditor_apiCallTreeItem* pItem = static_cast<vogleditor_apiCallTreeItem*>(index.internalPointer());
317 // highlight the API call cell if it has a substring which matches the searchString
318 if (role == Qt::BackgroundRole && index.column() == VOGL_ACTC_APICALL)
320 if (!m_searchString.isEmpty())
322 QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
323 QString string = data.toString();
324 if (string.contains(m_searchString, Qt::CaseInsensitive))
326 return QColor(Qt::yellow);
331 return pItem->columnData(index.column(), role);
334 Qt::ItemFlags vogleditor_QApiCallTreeModel::flags(const QModelIndex &index) const
336 if (!index.isValid())
339 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
342 QVariant vogleditor_QApiCallTreeModel::headerData(int section, Qt::Orientation orientation,
345 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
346 return m_rootItem->columnData(section, role);
351 void vogleditor_QApiCallTreeModel::set_highlight_search_string(const QString searchString)
353 m_searchString = searchString;
356 QModelIndex vogleditor_QApiCallTreeModel::find_prev_search_result(vogleditor_apiCallTreeItem* start, const QString searchText)
358 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
362 if (iter.findNext(start) == false)
364 // the object wasn't found in the list, so return a default (invalid) item
365 return QModelIndex();
368 // need to back up past the current item
373 // set the iterator to the back so that searching starts from the end of the list
377 // now the iterator is pointing to the desired start object in the list,
378 // continually check the prev item and find one with a snapshot
379 vogleditor_apiCallTreeItem* pFound = NULL;
380 while (iter.hasPrevious())
382 vogleditor_apiCallTreeItem* pItem = iter.peekPrevious();
383 QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
384 QString string = data.toString();
385 if (string.contains(searchText, Qt::CaseInsensitive))
394 return indexOf(pFound);
397 QModelIndex vogleditor_QApiCallTreeModel::find_next_search_result(vogleditor_apiCallTreeItem* start, const QString searchText)
399 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
403 if (iter.findNext(start) == false)
405 // the object wasn't found in the list, so return a default (invalid) item
406 return QModelIndex();
410 // now the iterator is pointing to the desired start object in the list,
411 // continually check the next item and find one with a snapshot
412 vogleditor_apiCallTreeItem* pFound = NULL;
413 while (iter.hasNext())
415 vogleditor_apiCallTreeItem* pItem = iter.peekNext();
416 QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
417 QString string = data.toString();
418 if (string.contains(searchText, Qt::CaseInsensitive))
427 return indexOf(pFound);
430 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_prev_snapshot(vogleditor_apiCallTreeItem* start)
432 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
436 if (iter.findNext(start) == false)
438 // the object wasn't found in the list
442 // need to back up past the current item
447 // set the iterator to the back so that searching starts from the end of the list
451 // now the iterator is pointing to the desired start object in the list,
452 // continually check the prev item and find one with a snapshot
453 vogleditor_apiCallTreeItem* pFound = NULL;
454 while (iter.hasPrevious())
456 if (iter.peekPrevious()->has_snapshot())
458 pFound = iter.peekPrevious();
468 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_next_snapshot(vogleditor_apiCallTreeItem* start)
470 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
472 // if start is NULL, then search will begin from top, otherwise it will begin from the start item and search onwards
475 if (iter.findNext(start) == false)
477 // the object wasn't found in the list
482 // now the iterator is pointing to the desired start object in the list,
483 // continually check the next item and find one with a snapshot
484 vogleditor_apiCallTreeItem* pFound = NULL;
485 while (iter.hasNext())
487 if (iter.peekNext()->has_snapshot())
489 pFound = iter.peekNext();
500 vogleditor_apiCallTreeItem *vogleditor_QApiCallTreeModel::find_prev_drawcall(vogleditor_apiCallTreeItem* start)
502 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
506 if (iter.findNext(start) == false)
508 // the object wasn't found in the list
512 // need to back up past the current item
517 // set the iterator to the back so that searching starts from the end of the list
521 // now the iterator is pointing to the desired start object in the list,
522 // continually check the prev item and find one with a snapshot
523 vogleditor_apiCallTreeItem* pFound = NULL;
524 while (iter.hasPrevious())
526 vogleditor_apiCallTreeItem* pItem = iter.peekPrevious();
527 if (pItem->apiCallItem() != NULL)
529 gl_entrypoint_id_t entrypointId = static_cast<gl_entrypoint_id_t>(pItem->apiCallItem()->getGLPacket()->m_entrypoint_id);
530 if (vogl_is_draw_entrypoint(entrypointId) ||
531 vogl_is_clear_entrypoint(entrypointId))
533 pFound = iter.peekPrevious();
544 vogleditor_apiCallTreeItem *vogleditor_QApiCallTreeModel::find_next_drawcall(vogleditor_apiCallTreeItem* start)
546 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
548 if (iter.findNext(start) == false)
550 // the object wasn't found in the list
554 // now the iterator is pointing to the desired start object in the list,
555 // continually check the next item and find one with a snapshot
556 vogleditor_apiCallTreeItem* pFound = NULL;
557 while (iter.hasNext())
559 vogleditor_apiCallTreeItem* pItem = iter.peekNext();
560 if (pItem->apiCallItem() != NULL)
562 gl_entrypoint_id_t entrypointId = static_cast<gl_entrypoint_id_t>(pItem->apiCallItem()->getGLPacket()->m_entrypoint_id);
563 if (vogl_is_draw_entrypoint(entrypointId) ||
564 vogl_is_clear_entrypoint(entrypointId))
566 pFound = iter.peekNext();
577 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_call_number(uint64_t callNumber)
579 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
581 vogleditor_apiCallTreeItem* pFound = NULL;
582 while (iter.hasNext())
584 vogleditor_apiCallTreeItem* pItem = iter.peekNext();
585 if (pItem->apiCallItem() != NULL)
587 if (pItem->apiCallItem()->globalCallIndex() == callNumber)
589 pFound = iter.peekNext();
600 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_frame_number(uint64_t frameNumber)
602 QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
604 vogleditor_apiCallTreeItem* pFound = NULL;
605 while (iter.hasNext())
607 vogleditor_apiCallTreeItem* pItem = iter.peekNext();
608 if (pItem->frameItem() != NULL)
610 if (pItem->frameItem()->frameNumber() == frameNumber)
612 pFound = iter.peekNext();