]> git.cworth.org Git - vogl/blob - src/vogleditor/vogleditor_qapicalltreemodel.cpp
UI: Fix issue #20: Search in the editor really slow
[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* pItem = static_cast<vogleditor_apiCallTreeItem*>(index.internalPointer());
316
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)
319     {
320         if (!m_searchString.isEmpty())
321         {
322             QVariant data = pItem->columnData(VOGL_ACTC_APICALL, Qt::DisplayRole);
323             QString string = data.toString();
324             if (string.contains(m_searchString, Qt::CaseInsensitive))
325             {
326                 return QColor(Qt::yellow);
327             }
328         }
329     }
330
331     return pItem->columnData(index.column(), role);
332 }
333
334 Qt::ItemFlags vogleditor_QApiCallTreeModel::flags(const QModelIndex &index) const
335 {
336     if (!index.isValid())
337         return 0;
338
339     return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
340 }
341
342 QVariant vogleditor_QApiCallTreeModel::headerData(int section, Qt::Orientation orientation,
343                                int role) const
344 {
345     if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
346         return m_rootItem->columnData(section, role);
347
348     return QVariant();
349 }
350
351 void vogleditor_QApiCallTreeModel::set_highlight_search_string(const QString searchString)
352 {
353     m_searchString = searchString;
354 }
355
356 QModelIndex vogleditor_QApiCallTreeModel::find_prev_search_result(vogleditor_apiCallTreeItem* start, const QString searchText)
357 {
358     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
359
360     if (start != NULL)
361     {
362         if (iter.findNext(start) == false)
363         {
364             // the object wasn't found in the list, so return a default (invalid) item
365             return QModelIndex();
366         }
367
368         // need to back up past the current item
369         iter.previous();
370     }
371     else
372     {
373         // set the iterator to the back so that searching starts from the end of the list
374         iter.toBack();
375     }
376
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())
381     {
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))
386         {
387             pFound = pItem;
388             break;
389         }
390
391         iter.previous();
392     }
393
394     return indexOf(pFound);
395 }
396
397 QModelIndex vogleditor_QApiCallTreeModel::find_next_search_result(vogleditor_apiCallTreeItem* start, const QString searchText)
398 {
399     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
400
401     if (start != NULL)
402     {
403         if (iter.findNext(start) == false)
404         {
405             // the object wasn't found in the list, so return a default (invalid) item
406             return QModelIndex();
407         }
408     }
409
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())
414     {
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))
419         {
420             pFound = pItem;
421             break;
422         }
423
424         iter.next();
425     }
426
427     return indexOf(pFound);
428 }
429
430 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_prev_snapshot(vogleditor_apiCallTreeItem* start)
431 {
432     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
433
434     if (start != NULL)
435     {
436         if (iter.findNext(start) == false)
437         {
438             // the object wasn't found in the list
439             return NULL;
440         }
441
442         // need to back up past the current item
443         iter.previous();
444     }
445     else
446     {
447         // set the iterator to the back so that searching starts from the end of the list
448         iter.toBack();
449     }
450
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())
455     {
456         if (iter.peekPrevious()->has_snapshot())
457         {
458             pFound = iter.peekPrevious();
459             break;
460         }
461
462         iter.previous();
463     }
464
465     return pFound;
466 }
467
468 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_next_snapshot(vogleditor_apiCallTreeItem* start)
469 {
470     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
471
472     // if start is NULL, then search will begin from top, otherwise it will begin from the start item and search onwards
473     if (start != NULL)
474     {
475         if (iter.findNext(start) == false)
476         {
477             // the object wasn't found in the list
478             return NULL;
479         }
480     }
481
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())
486     {
487         if (iter.peekNext()->has_snapshot())
488         {
489             pFound = iter.peekNext();
490             break;
491         }
492
493         iter.next();
494     }
495
496     return pFound;
497 }
498
499
500 vogleditor_apiCallTreeItem *vogleditor_QApiCallTreeModel::find_prev_drawcall(vogleditor_apiCallTreeItem* start)
501 {
502     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
503
504     if (start != NULL)
505     {
506         if (iter.findNext(start) == false)
507         {
508             // the object wasn't found in the list
509             return NULL;
510         }
511
512         // need to back up past the current item
513         iter.previous();
514     }
515     else
516     {
517         // set the iterator to the back so that searching starts from the end of the list
518         iter.toBack();
519     }
520
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())
525     {
526         vogleditor_apiCallTreeItem* pItem = iter.peekPrevious();
527         if (pItem->apiCallItem() != NULL)
528         {
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))
532             {
533                 pFound = iter.peekPrevious();
534                 break;
535             }
536         }
537
538         iter.previous();
539     }
540
541     return pFound;
542 }
543
544 vogleditor_apiCallTreeItem *vogleditor_QApiCallTreeModel::find_next_drawcall(vogleditor_apiCallTreeItem* start)
545 {
546     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
547
548     if (iter.findNext(start) == false)
549     {
550         // the object wasn't found in the list
551         return NULL;
552     }
553
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())
558     {
559         vogleditor_apiCallTreeItem* pItem = iter.peekNext();
560         if (pItem->apiCallItem() != NULL)
561         {
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))
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_call_number(uint64_t callNumber)
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->apiCallItem() != NULL)
586         {
587             if (pItem->apiCallItem()->globalCallIndex() == callNumber)
588             {
589                 pFound = iter.peekNext();
590                 break;
591             }
592         }
593
594         iter.next();
595     }
596
597     return pFound;
598 }
599
600 vogleditor_apiCallTreeItem* vogleditor_QApiCallTreeModel::find_frame_number(uint64_t frameNumber)
601 {
602     QLinkedListIterator<vogleditor_apiCallTreeItem*> iter(m_itemList);
603
604     vogleditor_apiCallTreeItem* pFound = NULL;
605     while (iter.hasNext())
606     {
607         vogleditor_apiCallTreeItem* pItem = iter.peekNext();
608         if (pItem->frameItem() != NULL)
609         {
610             if (pItem->frameItem()->frameNumber() == frameNumber)
611             {
612                 pFound = iter.peekNext();
613                 break;
614             }
615         }
616
617         iter.next();
618     }
619
620     return pFound;
621 }