]> git.cworth.org Git - vogl/blob - src/vogleditor/vogleditor_qapicalltreemodel.cpp
UI: Calls within glBegin/glEnd blocks are now nested in the API call tree (including...
[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 = vogl_new(vogleditor_apiCallTreeItem, this);
38     setupModelData(reader, m_rootItem);
39 }
40
41 vogleditor_QApiCallTreeModel::~vogleditor_QApiCallTreeModel()
42 {
43    if (m_rootItem != NULL)
44    {
45       vogl_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 = 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);
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          else if (entrypoint_id == VOGL_ENTRYPOINT_glBegin)
239          {
240              // items in the glBegin/glEnd block will be nested, including the glEnd
241              pCurParent = item;
242          }
243          else if (entrypoint_id == VOGL_ENTRYPOINT_glEnd)
244          {
245              // move the parent back one level of the hierarchy, to its own parent
246              pCurParent = pCurParent->parent();
247          }
248       }
249
250       if (pTrace_reader->get_packet_type() == cTSPTEOF)
251       {
252          //found_eof_packet = true;
253          vogl_printf("Found trace file EOF packet on swap %" PRIu64 "\n", total_swaps);
254          break;
255       }
256    }
257 }
258
259 QModelIndex vogleditor_QApiCallTreeModel::index(int row, int column, const QModelIndex &parent) const
260 {
261     if (!hasIndex(row, column, parent))
262         return QModelIndex();
263
264     vogleditor_apiCallTreeItem *parentItem;
265
266     if (!parent.isValid())
267         parentItem = m_rootItem;
268     else
269         parentItem = static_cast<vogleditor_apiCallTreeItem*>(parent.internalPointer());
270
271     vogleditor_apiCallTreeItem *childItem = parentItem->child(row);
272     if (childItem)
273         return createIndex(row, column, childItem);
274     else
275         return QModelIndex();
276 }
277
278 QModelIndex vogleditor_QApiCallTreeModel::indexOf(const vogleditor_apiCallTreeItem* pItem) const
279 {
280     if (pItem != NULL)
281         return createIndex(pItem->row(), VOGL_ACTC_APICALL, (void*)pItem);
282     else
283         return QModelIndex();
284 }
285
286 QModelIndex vogleditor_QApiCallTreeModel::parent(const QModelIndex &index) const
287 {
288     if (!index.isValid())
289         return QModelIndex();
290
291     vogleditor_apiCallTreeItem* childItem = static_cast<vogleditor_apiCallTreeItem*>(index.internalPointer());
292     vogleditor_apiCallTreeItem* parentItem = childItem->parent();
293
294     if (parentItem == m_rootItem)
295         return QModelIndex();
296
297     return createIndex(parentItem->row(), VOGL_ACTC_APICALL, parentItem);
298 }
299
300 int vogleditor_QApiCallTreeModel::rowCount(const QModelIndex &parent) const
301 {
302     vogleditor_apiCallTreeItem* parentItem;
303     if (parent.column() > 0)
304         return 0;
305
306     if (!parent.isValid())
307         parentItem = m_rootItem;
308     else
309         parentItem = static_cast<vogleditor_apiCallTreeItem*>(parent.internalPointer());
310
311     return parentItem->childCount();
312 }
313
314 int vogleditor_QApiCallTreeModel::columnCount(const QModelIndex &parent) const
315 {
316    VOGL_NOTE_UNUSED(parent);
317    return VOGL_MAX_ACTC;
318 }
319
320 QVariant vogleditor_QApiCallTreeModel::data(const QModelIndex &index, int role) const
321 {
322     if (!index.isValid())
323         return QVariant();
324
325     vogleditor_apiCallTreeItem* pItem = static_cast<vogleditor_apiCallTreeItem*>(index.internalPointer());
326
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)
329     {
330         if (!m_searchString.isEmpty())
331         {
332             QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
333             QString string = data.toString();
334             if (string.contains(m_searchString, Qt::CaseInsensitive))
335             {
336                 return QColor(Qt::yellow);
337             }
338         }
339     }
340
341     return pItem->columnData(index.column(), role);
342 }
343
344 Qt::ItemFlags vogleditor_QApiCallTreeModel::flags(const QModelIndex &index) const
345 {
346     if (!index.isValid())
347         return 0;
348
349     return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
350 }
351
352 QVariant vogleditor_QApiCallTreeModel::headerData(int section, Qt::Orientation orientation,
353                                int role) const
354 {
355     if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
356         return m_rootItem->columnData(section, role);
357
358     return QVariant();
359 }
360
361 void vogleditor_QApiCallTreeModel::set_highlight_search_string(const QString searchString)
362 {
363     m_searchString = searchString;
364 }
365
366 QModelIndex vogleditor_QApiCallTreeModel::find_prev_search_result(vogleditor_apiCallTreeItem* start, const QString searchText)
367 {
368     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
369
370     if (start != NULL)
371     {
372         if (iter.findNext(start) == false)
373         {
374             // the object wasn't found in the list, so return a default (invalid) item
375             return QModelIndex();
376         }
377
378         // need to back up past the current item
379         iter.previous();
380     }
381     else
382     {
383         // set the iterator to the back so that searching starts from the end of the list
384         iter.toBack();
385     }
386
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())
391     {
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))
396         {
397             pFound = pItem;
398             break;
399         }
400
401         iter.previous();
402     }
403
404     return indexOf(pFound);
405 }
406
407 QModelIndex vogleditor_QApiCallTreeModel::find_next_search_result(vogleditor_apiCallTreeItem* start, const QString searchText)
408 {
409     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
410
411     if (start != NULL)
412     {
413         if (iter.findNext(start) == false)
414         {
415             // the object wasn't found in the list, so return a default (invalid) item
416             return QModelIndex();
417         }
418     }
419
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())
424     {
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))
429         {
430             pFound = pItem;
431             break;
432         }
433
434         iter.next();
435     }
436
437     return indexOf(pFound);
438 }
439
440 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_prev_snapshot(vogleditor_apiCallTreeItem* start)
441 {
442     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
443
444     if (start != NULL)
445     {
446         if (iter.findNext(start) == false)
447         {
448             // the object wasn't found in the list
449             return NULL;
450         }
451
452         // need to back up past the current item
453         iter.previous();
454     }
455     else
456     {
457         // set the iterator to the back so that searching starts from the end of the list
458         iter.toBack();
459     }
460
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())
465     {
466         if (iter.peekPrevious()->has_snapshot())
467         {
468             pFound = iter.peekPrevious();
469             break;
470         }
471
472         iter.previous();
473     }
474
475     return pFound;
476 }
477
478 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_next_snapshot(vogleditor_apiCallTreeItem* start)
479 {
480     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
481
482     // if start is NULL, then search will begin from top, otherwise it will begin from the start item and search onwards
483     if (start != NULL)
484     {
485         if (iter.findNext(start) == false)
486         {
487             // the object wasn't found in the list
488             return NULL;
489         }
490     }
491
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())
496     {
497         if (iter.peekNext()->has_snapshot())
498         {
499             pFound = iter.peekNext();
500             break;
501         }
502
503         iter.next();
504     }
505
506     return pFound;
507 }
508
509
510 vogleditor_apiCallTreeItem *vogleditor_QApiCallTreeModel::find_prev_drawcall(vogleditor_apiCallTreeItem* start)
511 {
512     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
513
514     if (start != NULL)
515     {
516         if (iter.findNext(start) == false)
517         {
518             // the object wasn't found in the list
519             return NULL;
520         }
521
522         // need to back up past the current item
523         iter.previous();
524     }
525     else
526     {
527         // set the iterator to the back so that searching starts from the end of the list
528         iter.toBack();
529     }
530
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())
535     {
536         vogleditor_apiCallTreeItem* pItem = iter.peekPrevious();
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                 (entrypointId == VOGL_ENTRYPOINT_glBitmap) ||
543                 (entrypointId == VOGL_ENTRYPOINT_glEnd))
544             {
545                 pFound = iter.peekPrevious();
546                 break;
547             }
548         }
549
550         iter.previous();
551     }
552
553     return pFound;
554 }
555
556 vogleditor_apiCallTreeItem *vogleditor_QApiCallTreeModel::find_next_drawcall(vogleditor_apiCallTreeItem* start)
557 {
558     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
559
560     if (iter.findNext(start) == false)
561     {
562         // the object wasn't found in the list
563         return NULL;
564     }
565
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())
570     {
571         vogleditor_apiCallTreeItem* pItem = iter.peekNext();
572         if (pItem->apiCallItem() != NULL)
573         {
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))
579             {
580                 pFound = iter.peekNext();
581                 break;
582             }
583         }
584
585         iter.next();
586     }
587
588     return pFound;
589 }
590
591 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_call_number(uint64_t callNumber)
592 {
593     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
594
595     vogleditor_apiCallTreeItem* pFound = NULL;
596     while (iter.hasNext())
597     {
598         vogleditor_apiCallTreeItem* pItem = iter.peekNext();
599         if (pItem->apiCallItem() != NULL)
600         {
601             if (pItem->apiCallItem()->globalCallIndex() == callNumber)
602             {
603                 pFound = iter.peekNext();
604                 break;
605             }
606         }
607
608         iter.next();
609     }
610
611     return pFound;
612 }
613
614 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_frame_number(uint64_t frameNumber)
615 {
616     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
617
618     vogleditor_apiCallTreeItem* pFound = NULL;
619     while (iter.hasNext())
620     {
621         vogleditor_apiCallTreeItem* pItem = iter.peekNext();
622         if (pItem->frameItem() != NULL)
623         {
624             if (pItem->frameItem()->frameNumber() == frameNumber)
625             {
626                 pFound = iter.peekNext();
627                 break;
628             }
629         }
630
631         iter.next();
632     }
633
634     return pFound;
635 }