]> git.cworth.org Git - vogl/blob - src/vogleditor/vogleditor_qapicalltreemodel.cpp
c89e1589c24d425f37a88ef11c249ee2a3e44149
[vogl] / src / vogleditor / vogleditor_qapicalltreemodel.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
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:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
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
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 #include "vogleditor_qapicalltreemodel.h"
27
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"
33
34 vogleditor_QApiCallTreeModel::vogleditor_QApiCallTreeModel(vogl_trace_file_reader* reader, QObject *parent)
35     : QAbstractItemModel(parent)
36 {
37     m_rootItem = new vogleditor_apiCallTreeItem(this);
38     setupModelData(reader, m_rootItem);
39 }
40
41 vogleditor_QApiCallTreeModel::~vogleditor_QApiCallTreeModel()
42 {
43    if (m_rootItem != NULL)
44    {
45       delete m_rootItem;
46       m_rootItem = NULL;
47    }
48
49    m_itemList.clear();
50 }
51
52 // TODO: return bool
53 void vogleditor_QApiCallTreeModel::setupModelData(vogl_trace_file_reader* pTrace_reader, vogleditor_apiCallTreeItem *parent)
54 {
55    const vogl_trace_stream_start_of_file_packet &sof_packet = pTrace_reader->get_sof_packet();
56    VOGL_NOTE_UNUSED(sof_packet);
57
58    uint64_t total_swaps = 0;
59    // TODO probably need to handle eof_packet
60    //bool found_eof_packet = false;
61
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;
68
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;
72
73    m_trace_ctypes.init(pTrace_reader->get_sof_packet().m_pointer_sizes);
74
75    vogl_trace_packet trace_packet(&m_trace_ctypes);
76
77    for ( ; ; )
78    {
79       vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
80
81       if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
82       {
83          vogl_error_printf("Failed reading from trace file!\n");
84          break;
85       }
86
87       if (read_status == vogl_trace_file_reader::cEOF)
88       {
89          vogl_printf("At trace file EOF on swap %" PRIu64 "\n", total_swaps);
90          break;
91       }
92
93       const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf(); VOGL_NOTE_UNUSED(packet_buf);
94
95       const vogl_trace_stream_packet_base &base_packet = pTrace_reader->get_base_packet(); VOGL_NOTE_UNUSED(base_packet);
96       const vogl_trace_gl_entrypoint_packet *pGL_packet = NULL;
97
98       if (pTrace_reader->get_packet_type() == cTSPTGLEntrypoint)
99       {
100          if (!trace_packet.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
101          {
102             console::error("%s: Failed parsing GL entrypoint packet\n", VOGL_FUNCTION_NAME);
103             break;
104          }
105
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);
108
109          if (entrypoint_id == VOGL_ENTRYPOINT_glInternalTraceCommandRAD)
110          {
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 = trace_packet.get_param_value<GLuint>(0);
115             GLuint size = trace_packet.get_param_value<GLuint>(1); VOGL_NOTE_UNUSED(size);
116
117             if (cmd == cITCRKeyValueMap)
118             {
119                key_value_map &kvm = trace_packet.get_key_value_map();
120
121                dynamic_string cmd_type(kvm.get_string("command_type"));
122                if (cmd_type == "state_snapshot")
123                {
124                   dynamic_string id(kvm.get_string("binary_id"));
125                   if (id.is_empty())
126                   {
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());
128                      continue;
129                   }
130
131                   uint8_vec snapshot_data;
132                   {
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()))
135                      {
136                         vogl_warning_printf("%s: Failed reading snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
137                         continue;
138                      }
139                   }
140
141                   json_document doc;
142                   {
143                      timed_scope ts("doc.binary_deserialize");
144                      if (!doc.binary_deserialize(snapshot_data) || (!doc.get_root()))
145                      {
146                         vogl_warning_printf("%s: Failed deserializing JSON snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
147                         continue;
148                      }
149                   }
150
151                   vogl_gl_state_snapshot* pGLSnapshot = vogl_new(vogl_gl_state_snapshot);
152                   pPendingSnapshot = vogl_new(vogleditor_gl_state_snapshot, pGLSnapshot);
153
154                   timed_scope ts("pPendingSnapshot->deserialize");
155                   if (!pPendingSnapshot->get_snapshot()->deserialize(*doc.get_root(), pTrace_reader->get_multi_blob_manager(), &m_trace_ctypes))
156                   {
157                      vogl_delete(pPendingSnapshot);
158                      pPendingSnapshot = NULL;
159
160                      vogl_warning_printf("%s: Failed deserializing snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
161                   }
162                }
163             }
164
165             continue;
166          }
167
168          const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[entrypoint_id];
169
170          QString funcCall = entrypoint_desc.m_pName;
171
172          // format parameters
173          funcCall.append("( ");
174          dynamic_string paramStr;
175          for (uint param_index = 0; param_index < trace_packet.total_params(); param_index++)
176          {
177             if (param_index != 0)
178                funcCall.append(", ");
179
180             paramStr.clear();
181             trace_packet.pretty_print_param(paramStr, param_index, false);
182
183             funcCall.append(paramStr.c_str());
184          }
185          funcCall.append(" )");
186
187          if (trace_packet.has_return_value())
188          {
189             funcCall.append(" = ");
190             paramStr.clear();
191             trace_packet.pretty_print_return_value(paramStr, false);
192             funcCall.append(paramStr.c_str());
193          }
194
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)
198          {
199             pCurFrame = new vogleditor_frameItem(total_swaps);
200             vogleditor_apiCallTreeItem* pNewFrameNode = new vogleditor_apiCallTreeItem(pCurFrame, pCurParent);
201             pCurParent->appendChild(pNewFrameNode);
202             m_itemList.append(pNewFrameNode);
203
204             if (pPendingSnapshot != NULL)
205             {
206                pCurFrame->set_snapshot(pPendingSnapshot);
207                pPendingSnapshot = NULL;
208             }
209
210             // update current parent
211             pCurParent = pNewFrameNode;
212          }
213
214          // make item and node for the api call
215          vogleditor_apiCallItem* pCallItem = new vogleditor_apiCallItem(pCurFrame, trace_packet, *pGL_packet);
216          pCurFrame->appendCall(pCallItem);
217
218          if (pPendingSnapshot != NULL)
219          {
220             pCallItem->set_snapshot(pPendingSnapshot);
221             pPendingSnapshot = NULL;
222          }
223
224          vogleditor_apiCallTreeItem* item = new vogleditor_apiCallTreeItem(funcCall, pCallItem, pCurParent);
225          pCurParent->appendChild(item);
226          m_itemList.append(item);
227
228          if (vogl_is_swap_buffers_entrypoint(entrypoint_id))
229          {
230             total_swaps++;
231
232             // reset the CurParent back to the original parent so that the next frame will be at the root level
233             pCurParent = parent;
234
235             // reset the CurFrame so that a new frame node will be created on the next api call
236             pCurFrame = NULL;
237          }
238       }
239
240       if (pTrace_reader->get_packet_type() == cTSPTEOF)
241       {
242          //found_eof_packet = true;
243          vogl_printf("Found trace file EOF packet on swap %" PRIu64 "\n", total_swaps);
244          break;
245       }
246    }
247 }
248
249 QModelIndex vogleditor_QApiCallTreeModel::index(int row, int column, const QModelIndex &parent) const
250 {
251     if (!hasIndex(row, column, parent))
252         return QModelIndex();
253
254     vogleditor_apiCallTreeItem *parentItem;
255
256     if (!parent.isValid())
257         parentItem = m_rootItem;
258     else
259         parentItem = static_cast<vogleditor_apiCallTreeItem*>(parent.internalPointer());
260
261     vogleditor_apiCallTreeItem *childItem = parentItem->child(row);
262     if (childItem)
263         return createIndex(row, column, childItem);
264     else
265         return QModelIndex();
266 }
267
268 QModelIndex vogleditor_QApiCallTreeModel::indexOf(const vogleditor_apiCallTreeItem* pItem) const
269 {
270     if (pItem != NULL)
271         return createIndex(pItem->row(), VOGL_ACTC_APICALL, (void*)pItem);
272     else
273         return QModelIndex();
274 }
275
276 QModelIndex vogleditor_QApiCallTreeModel::parent(const QModelIndex &index) const
277 {
278     if (!index.isValid())
279         return QModelIndex();
280
281     vogleditor_apiCallTreeItem* childItem = static_cast<vogleditor_apiCallTreeItem*>(index.internalPointer());
282     vogleditor_apiCallTreeItem* parentItem = childItem->parent();
283
284     if (parentItem == m_rootItem)
285         return QModelIndex();
286
287     return createIndex(parentItem->row(), VOGL_ACTC_APICALL, parentItem);
288 }
289
290 int vogleditor_QApiCallTreeModel::rowCount(const QModelIndex &parent) const
291 {
292     vogleditor_apiCallTreeItem* parentItem;
293     if (parent.column() > 0)
294         return 0;
295
296     if (!parent.isValid())
297         parentItem = m_rootItem;
298     else
299         parentItem = static_cast<vogleditor_apiCallTreeItem*>(parent.internalPointer());
300
301     return parentItem->childCount();
302 }
303
304 int vogleditor_QApiCallTreeModel::columnCount(const QModelIndex &parent) const
305 {
306    VOGL_NOTE_UNUSED(parent);
307    return VOGL_MAX_ACTC;
308 }
309
310 QVariant vogleditor_QApiCallTreeModel::data(const QModelIndex &index, int role) const
311 {
312     if (!index.isValid())
313         return QVariant();
314
315     vogleditor_apiCallTreeItem *item = static_cast<vogleditor_apiCallTreeItem*>(index.internalPointer());
316
317     return item->columnData(index.column(), role);
318 }
319
320 Qt::ItemFlags vogleditor_QApiCallTreeModel::flags(const QModelIndex &index) const
321 {
322     if (!index.isValid())
323         return 0;
324
325     return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
326 }
327
328 QVariant vogleditor_QApiCallTreeModel::headerData(int section, Qt::Orientation orientation,
329                                int role) const
330 {
331     if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
332         return m_rootItem->columnData(section, role);
333
334     return QVariant();
335 }
336
337 QModelIndexList vogleditor_QApiCallTreeModel::find_search_matches(const QString searchText)
338 {
339     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
340
341     QModelIndexList matches;
342     // iterate through all items and find matching text
343     while (iter.hasNext())
344     {
345         vogleditor_apiCallTreeItem* pItem = iter.peekNext();
346         QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
347         QString string = data.toString();
348         if (string.contains(searchText, Qt::CaseInsensitive))
349         {
350             matches.push_back(indexOf(pItem));
351         }
352
353         iter.next();
354     }
355
356     return matches;
357 }
358
359 QModelIndex vogleditor_QApiCallTreeModel::find_prev_search_result(vogleditor_apiCallTreeItem* start, const QString searchText)
360 {
361     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
362
363     if (iter.findNext(start) == false)
364     {
365         // the object wasn't found in the list, so just return the same item
366         return indexOf(start);
367     }
368
369     // need to back up past the current item
370     iter.previous();
371
372     // now the iterator is pointing to the desired start object in the list,
373     // continually check the prev item and find one with a snapshot
374     vogleditor_apiCallTreeItem* pFound = start;
375     while (iter.hasPrevious())
376     {
377         vogleditor_apiCallTreeItem* pItem = iter.peekPrevious();
378         QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
379         QString string = data.toString();
380         if (string.contains(searchText, Qt::CaseInsensitive))
381         {
382             pFound = pItem;
383             break;
384         }
385
386         iter.previous();
387     }
388
389     return indexOf(pFound);
390 }
391
392 QModelIndex vogleditor_QApiCallTreeModel::find_next_search_result(vogleditor_apiCallTreeItem* start, const QString searchText)
393 {
394
395     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
396
397     if (iter.findNext(start) == false)
398     {
399         // the object wasn't found in the list, so just return the same item
400         return indexOf(start);
401     }
402
403     // now the iterator is pointing to the desired start object in the list,
404     // continually check the next item and find one with a snapshot
405     vogleditor_apiCallTreeItem* pFound = start;
406     while (iter.hasNext())
407     {
408         vogleditor_apiCallTreeItem* pItem = iter.peekNext();
409         QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
410         QString string = data.toString();
411         if (string.contains(searchText, Qt::CaseInsensitive))
412         {
413             pFound = pItem;
414             break;
415         }
416
417         iter.next();
418     }
419
420     return indexOf(pFound);
421 }
422
423 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_prev_snapshot(vogleditor_apiCallTreeItem* start)
424 {
425     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
426
427     if (iter.findNext(start) == false)
428     {
429         // the object wasn't found in the list, so just return the same item
430         return start;
431     }
432
433     // need to back up past the current item
434     iter.previous();
435
436     // now the iterator is pointing to the desired start object in the list,
437     // continually check the prev item and find one with a snapshot
438     vogleditor_apiCallTreeItem* pFound = start;
439     while (iter.hasPrevious())
440     {
441         if (iter.peekPrevious()->has_snapshot())
442         {
443             pFound = iter.peekPrevious();
444             break;
445         }
446
447         iter.previous();
448     }
449
450     return pFound;
451 }
452
453 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_next_snapshot(vogleditor_apiCallTreeItem* start)
454 {
455     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
456
457     if (iter.findNext(start) == false)
458     {
459         // the object wasn't found in the list, so just return the same item
460         return start;
461     }
462
463     // now the iterator is pointing to the desired start object in the list,
464     // continually check the next item and find one with a snapshot
465     vogleditor_apiCallTreeItem* pFound = start;
466     while (iter.hasNext())
467     {
468         if (iter.peekNext()->has_snapshot())
469         {
470             pFound = iter.peekNext();
471             break;
472         }
473
474         iter.next();
475     }
476
477     return pFound;
478 }
479
480
481 vogleditor_apiCallTreeItem *vogleditor_QApiCallTreeModel::find_prev_drawcall(vogleditor_apiCallTreeItem* start)
482 {
483     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
484
485     if (iter.findNext(start) == false)
486     {
487         // the object wasn't found in the list, so just return the same item
488         return start;
489     }
490
491     // need to back up past the current item
492     iter.previous();
493
494     // now the iterator is pointing to the desired start object in the list,
495     // continually check the prev item and find one with a snapshot
496     vogleditor_apiCallTreeItem* pFound = start;
497     while (iter.hasPrevious())
498     {
499         vogleditor_apiCallTreeItem* pItem = iter.peekPrevious();
500         if (pItem->apiCallItem() != NULL)
501         {
502             gl_entrypoint_id_t entrypointId = static_cast<gl_entrypoint_id_t>(pItem->apiCallItem()->getGLPacket()->m_entrypoint_id);
503             if (vogl_is_draw_entrypoint(entrypointId) ||
504                 vogl_is_clear_entrypoint(entrypointId))
505             {
506                 pFound = iter.peekPrevious();
507                 break;
508             }
509         }
510
511         iter.previous();
512     }
513
514     return pFound;
515 }
516
517 vogleditor_apiCallTreeItem *vogleditor_QApiCallTreeModel::find_next_drawcall(vogleditor_apiCallTreeItem* start)
518 {
519     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
520
521     if (iter.findNext(start) == false)
522     {
523         // the object wasn't found in the list, so just return the same item
524         return start;
525     }
526
527     // now the iterator is pointing to the desired start object in the list,
528     // continually check the next item and find one with a snapshot
529     vogleditor_apiCallTreeItem* pFound = start;
530     while (iter.hasNext())
531     {
532         vogleditor_apiCallTreeItem* pItem = iter.peekNext();
533         if (pItem->apiCallItem() != NULL)
534         {
535             gl_entrypoint_id_t entrypointId = static_cast<gl_entrypoint_id_t>(pItem->apiCallItem()->getGLPacket()->m_entrypoint_id);
536             if (vogl_is_draw_entrypoint(entrypointId) ||
537                 vogl_is_clear_entrypoint(entrypointId))
538             {
539                 pFound = iter.peekNext();
540                 break;
541             }
542         }
543
544         iter.next();
545     }
546
547     return pFound;
548 }