]> git.cworth.org Git - vogl/blob - src/vogleditor/vogleditor_qapicalltreemodel.cpp
e34cc5b531611290f6f33ca173cc4c298d9600ee
[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    for ( ; ; )
76    {
77       vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
78
79       if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
80       {
81          vogl_error_printf("Failed reading from trace file!\n");
82          break;
83       }
84
85       if (read_status == vogl_trace_file_reader::cEOF)
86       {
87          vogl_printf("At trace file EOF on swap %" PRIu64 "\n", total_swaps);
88          break;
89       }
90
91       const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf(); VOGL_NOTE_UNUSED(packet_buf);
92
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;
95
96       if (pTrace_reader->get_packet_type() == cTSPTGLEntrypoint)
97       {
98          vogl_trace_packet* pTrace_packet = vogl_new(vogl_trace_packet, &m_trace_ctypes);
99
100          if (!pTrace_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 = pTrace_packet->get_param_value<GLuint>(0);
115             GLuint size = pTrace_packet->get_param_value<GLuint>(1); VOGL_NOTE_UNUSED(size);
116
117             if (cmd == cITCRKeyValueMap)
118             {
119                key_value_map &kvm = pTrace_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 < pTrace_packet->total_params(); param_index++)
176          {
177             if (param_index != 0)
178                funcCall.append(", ");
179
180             paramStr.clear();
181             pTrace_packet->pretty_print_param(paramStr, param_index, false);
182
183             funcCall.append(paramStr.c_str());
184          }
185          funcCall.append(" )");
186
187          if (pTrace_packet->has_return_value())
188          {
189             funcCall.append(" = ");
190             paramStr.clear();
191             pTrace_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 = vogl_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 = vogl_new(vogleditor_apiCallItem, pCurFrame, pTrace_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 = vogl_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 start is NULL, then search will begin from top, otherwise it will begin from the start item and search onwards
458     if (start != NULL)
459     {
460         if (iter.findNext(start) == false)
461         {
462             // the object wasn't found in the list, so just return the same item
463             return start;
464         }
465     }
466
467     // now the iterator is pointing to the desired start object in the list,
468     // continually check the next item and find one with a snapshot
469     vogleditor_apiCallTreeItem* pFound = start;
470     while (iter.hasNext())
471     {
472         if (iter.peekNext()->has_snapshot())
473         {
474             pFound = iter.peekNext();
475             break;
476         }
477
478         iter.next();
479     }
480
481     return pFound;
482 }
483
484
485 vogleditor_apiCallTreeItem *vogleditor_QApiCallTreeModel::find_prev_drawcall(vogleditor_apiCallTreeItem* start)
486 {
487     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
488
489     if (iter.findNext(start) == false)
490     {
491         // the object wasn't found in the list, so just return the same item
492         return start;
493     }
494
495     // need to back up past the current item
496     iter.previous();
497
498     // now the iterator is pointing to the desired start object in the list,
499     // continually check the prev item and find one with a snapshot
500     vogleditor_apiCallTreeItem* pFound = start;
501     while (iter.hasPrevious())
502     {
503         vogleditor_apiCallTreeItem* pItem = iter.peekPrevious();
504         if (pItem->apiCallItem() != NULL)
505         {
506             gl_entrypoint_id_t entrypointId = static_cast<gl_entrypoint_id_t>(pItem->apiCallItem()->getGLPacket()->m_entrypoint_id);
507             if (vogl_is_draw_entrypoint(entrypointId) ||
508                 vogl_is_clear_entrypoint(entrypointId))
509             {
510                 pFound = iter.peekPrevious();
511                 break;
512             }
513         }
514
515         iter.previous();
516     }
517
518     return pFound;
519 }
520
521 vogleditor_apiCallTreeItem *vogleditor_QApiCallTreeModel::find_next_drawcall(vogleditor_apiCallTreeItem* start)
522 {
523     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
524
525     if (iter.findNext(start) == false)
526     {
527         // the object wasn't found in the list, so just return the same item
528         return start;
529     }
530
531     // now the iterator is pointing to the desired start object in the list,
532     // continually check the next item and find one with a snapshot
533     vogleditor_apiCallTreeItem* pFound = start;
534     while (iter.hasNext())
535     {
536         vogleditor_apiCallTreeItem* pItem = iter.peekNext();
537         if (pItem->apiCallItem() != NULL)
538         {
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             {
543                 pFound = iter.peekNext();
544                 break;
545             }
546         }
547
548         iter.next();
549     }
550
551     return pFound;
552 }
553
554 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_call_number(uint64_t callNumber)
555 {
556     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
557
558     vogleditor_apiCallTreeItem* pFound = NULL;
559     while (iter.hasNext())
560     {
561         vogleditor_apiCallTreeItem* pItem = iter.peekNext();
562         if (pItem->apiCallItem() != NULL)
563         {
564             if (pItem->apiCallItem()->globalCallIndex() == callNumber)
565             {
566                 pFound = iter.peekNext();
567                 break;
568             }
569         }
570
571         iter.next();
572     }
573
574     return pFound;
575 }
576
577 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_frame_number(uint64_t frameNumber)
578 {
579     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
580
581     vogleditor_apiCallTreeItem* pFound = NULL;
582     while (iter.hasNext())
583     {
584         vogleditor_apiCallTreeItem* pItem = iter.peekNext();
585         if (pItem->frameItem() != NULL)
586         {
587             if (pItem->frameItem()->frameNumber() == frameNumber)
588             {
589                 pFound = iter.peekNext();
590                 break;
591             }
592         }
593
594         iter.next();
595     }
596
597     return pFound;
598 }