bool vogleditor_traceReplayer::applying_snapshot_and_process_resize(const vogl_gl_state_snapshot* pSnapshot)
{
    vogl_gl_replayer::status_t status = m_pTraceReplayer->begin_applying_snapshot(pSnapshot, false);

    bool bStatus = true;
    while (status == vogl_gl_replayer::cStatusResizeWindow)
    {
        vogleditor_output_message("Waiting for replay window to resize.");

        // Pump X events in case the window is resizing
        if (process_x_events())
        {
            status = m_pTraceReplayer->process_pending_window_resize();
        }
        else
        {
            bStatus = false;
            break;
        }
    }

    if (bStatus && status != vogl_gl_replayer::cStatusOK)
    {
        vogleditor_output_error("Replay unable to apply snapshot");
        bStatus = false;
    }

    return bStatus;
}
bool vogleditor_QApiCallTreeModel::init(vogl_trace_file_reader* pTrace_reader)
{
   vogleditor_apiCallTreeItem* parent = m_rootItem;
   const vogl_trace_stream_start_of_file_packet &sof_packet = pTrace_reader->get_sof_packet();
   VOGL_NOTE_UNUSED(sof_packet);

   uint64_t total_swaps = 0;
   bool found_eof_packet = false;

   // make a PendingFrame node to hold the api calls
   // this will remain in the pending state until the first
   // api call is seen, then it will be made the CurFrame and
   // appended to the parent
   vogleditor_frameItem* pCurFrame = NULL;
   vogleditor_apiCallTreeItem* pCurParent = parent;

   // Make a PendingSnapshot that may or may not be populated when reading the trace.
   // This snapshot will be assigned to the next API call that occurs.
   vogleditor_gl_state_snapshot* pPendingSnapshot = NULL;

   m_trace_ctypes.init(pTrace_reader->get_sof_packet().m_pointer_sizes);

   for ( ; ; )
   {
      vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();

      if ((read_status != vogl_trace_file_reader::c*K) && (read_status != vogl_trace_file_reader::cEOF))
      {
         vogleditor_output_error("Failed reading from trace file!");
         return false;
      }

      if (read_status == vogl_trace_file_reader::cEOF)
      {
         vogl_printf("At trace file EOF on swap %" PRIu64 "\n", total_swaps);
         return false;
      }

      const vogl::vector<uint8_t> &packet_buf = pTrace_reader->get_packet_buf(); VOGL_NOTE_UNUSED(packet_buf);

      const vogl_trace_stream_packet_base &base_packet = pTrace_reader->get_base_packet(); VOGL_NOTE_UNUSED(base_packet);
      const vogl_trace_gl_entrypoint_packet *pGL_packet = NULL;

      if (pTrace_reader->get_packet_type() == cTSPTGLEntrypoint)
      {
         vogl_trace_packet* pTrace_packet = vogl_new(vogl_trace_packet, &m_trace_ctypes);

         if (!pTrace_packet->deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
         {
             vogleditor_output_error("Failed parsing GL entrypoint packet.");
             return false;
         }

         if (!pTrace_packet->check())
         {
             vogleditor_output_error("GL entrypoint packet failed consistency check. Please make sure the trace was made with the most recent version of VOGL.");
             return false;
         }

         pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
         gl_entrypoint_id_t entrypoint_id = static_cast<gl_entrypoint_id_t>(pGL_packet->m_entrypoint_id);

         if (entrypoint_id == VOGL_ENTRYPOINT_glInternalTraceCommandRAD)
         {
            // Check if this is a state snapshot.
            // This is entirely optional since the client is designed to dynamically get new snapshots
            // if they don't exist.
            GLuint cmd = pTrace_packet->get_param_value<GLuint>(0);
            GLuint size = pTrace_packet->get_param_value<GLuint>(1); VOGL_NOTE_UNUSED(size);

            if (cmd == cITCRKeyValueMap)
            {
               key_value_map &kvm = pTrace_packet->get_key_value_map();

               dynamic_string cmd_type(kvm.get_string("command_type"));
               if (cmd_type == "state_snapshot")
               {
                  dynamic_string id(kvm.get_string("binary_id"));
                  if (id.is_empty())
                  {
                     vogl_warning_printf("%s: Missing binary_id field in glInternalTraceCommandRAD key_value_map command type: \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, cmd_type.get_ptr());
                     continue;
                  }

                  uint8_vec snapshot_data;
                  {
                     timed_scope ts("get_multi_blob_manager().get");
                     if (!pTrace_reader->get_multi_blob_manager().get(id, snapshot_data) || (snapshot_data.is_empty()))
                     {
                        vogl_warning_printf("%s: Failed reading snapshot blob data \"%s\"!\n", VOGL_FUNCTION_INFO_CSTR, id.get_ptr());
                        continue;
                     }
                  }

                  json_document doc;
                  {
                     timed_scope ts("doc.binary_deserialize");
                     if (!doc.binary_deserialize(snapshot_data) || (!doc.get_root()))
                     {
                        vogl_warning_printf("%s: Failed deserializing JSON snapshot blob data \"%s\"!\n", VOGL_FUNCTION_INFO_CSTR, id.get_ptr());
                        continue;
                     }
                  }

                  vogl_gl_state_snapshot* pGLSnapshot = vogl_new(vogl_gl_state_snapshot);
                  pPendingSnapshot = vogl_new(vogleditor_gl_state_snapshot, pGLSnapshot);

                  timed_scope ts("pPendingSnapshot->deserialize");
                  if (!pPendingSnapshot->get_snapshot()->deserialize(*doc.get_root(), pTrace_reader->get_multi_blob_manager(), &m_trace_ctypes))
                  {
                     vogl_delete(pPendingSnapshot);
                     pPendingSnapshot = NULL;

                     vogl_warning_printf("%s: Failed deserializing snapshot blob data \"%s\"!\n", VOGL_FUNCTION_INFO_CSTR, id.get_ptr());
                  }
               }
            }

            continue;
         }

         const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[entrypoint_id];

         QString funcCall = entrypoint_desc.m_pName;

         // format parameters
         funcCall.append("( ");
         dynamic_string paramStr;
         for (uint param_index = 0; param_index < pTrace_packet->total_params(); param_index++)
         {
            if (param_index != 0)
               funcCall.append(", ");

            paramStr.clear();
            pTrace_packet->pretty_print_param(paramStr, param_index, false);

            funcCall.append(paramStr.c_str());
         }
         funcCall.append(" )");

         if (pTrace_packet->has_return_value())
         {
            funcCall.append(" = ");
            paramStr.clear();
            pTrace_packet->pretty_print_return_value(paramStr, false);
            funcCall.append(paramStr.c_str());
         }

         // if we don't have a current frame, make a new frame node
         // and append it to the parent
         if (pCurFrame == NULL)
         {
            pCurFrame = vogl_new(vogleditor_frameItem, total_swaps);
            vogleditor_apiCallTreeItem* pNewFrameNode = vogl_new(vogleditor_apiCallTreeItem, pCurFrame, pCurParent);
            pCurParent->appendChild(pNewFrameNode);
            m_itemList.append(pNewFrameNode);

            if (pPendingSnapshot != NULL)
            {
               pCurFrame->set_snapshot(pPendingSnapshot);
               pPendingSnapshot = NULL;
            }

            // update current parent
            pCurParent = pNewFrameNode;
         }

         // make item and node for the api call
         vogleditor_apiCallItem* pCallItem = vogl_new(vogleditor_apiCallItem, pCurFrame, pTrace_packet, *pGL_packet);
         pCurFrame->appendCall(pCallItem);

         if (pPendingSnapshot != NULL)
         {
            pCallItem->set_snapshot(pPendingSnapshot);
            pPendingSnapshot = NULL;
         }

         vogleditor_apiCallTreeItem* item = vogl_new(vogleditor_apiCallTreeItem, funcCall, pCallItem, pCurParent);
         pCurParent->appendChild(item);
         m_itemList.append(item);

         if (vogl_is_swap_buffers_entrypoint(entrypoint_id))
         {
            total_swaps++;

            // reset the CurParent back to the original parent so that the next frame will be at the root level
            pCurParent = parent;

            // reset the CurFrame so that a new frame node will be created on the next api call
            pCurFrame = NULL;
         }
         else if (vogl_is_start_nested_entrypoint(entrypoint_id))
         {
             // Nest logically paired blocks of gl calls including terminating
             // nest call
             pCurParent = item;
         }
         else if (vogl_is_end_nested_entrypoint(entrypoint_id))
         {
             // move the parent back one level of the hierarchy, to its own parent
             // (but not past Frame parent [e.g., unpaired "end" operation])
             if (pCurParent->parent() != parent)
                 pCurParent = pCurParent->parent();
         }
      }

      if (pTrace_reader->get_packet_type() == cTSPTEOF)
      {
          found_eof_packet = true;
          vogl_printf("Found trace file EOF packet on swap %" PRIu64 "\n", total_swaps);
          break;
      }
   }

   return found_eof_packet;
}
vogleditor_tracereplayer_result vogleditor_traceReplayer::replay(vogl_trace_file_reader* m_pTraceReader, vogleditor_apiCallTreeItem* pRootItem, vogleditor_gl_state_snapshot** ppNewSnapshot, uint64_t apiCallNumber, bool endlessMode)
{
   // reset to beginnning of trace file.
   m_pTraceReader->seek_to_frame(0);

   int initial_window_width = 1280;
   int initial_window_height = 1024;

   if (!m_window.open(initial_window_width, initial_window_height))
   {
      vogleditor_output_error("Failed opening GL replayer window!");
      return VOGLEDITOR_TRR_ERROR;
   }

   uint replayer_flags = cGLReplayerForceDebugContexts;
   if (!m_pTraceReplayer->init(replayer_flags, &m_window, m_pTraceReader->get_sof_packet(), m_pTraceReader->get_multi_blob_manager()))
   {
      vogleditor_output_error("Failed initializing GL replayer!");
      m_window.close();
      return VOGLEDITOR_TRR_ERROR;
   }

   XSelectInput(m_window.get_display(), m_window.get_xwindow(),
                EnterWindowMask | LeaveWindowMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | StructureNotifyMask | KeymapStateMask);

   m_wmDeleteMessage = XInternAtom(m_window.get_display(), "WM_DELETE_WINDOW", False);
   XSetWMProtocols(m_window.get_display(), m_window.get_xwindow(), &m_wmDeleteMessage, 1);

   timer tm;
   tm.start();

   vogleditor_tracereplayer_result result = VOGLEDITOR_TRR_SUCCESS;

   for ( ; ; )
   {
      if (process_x_events() == false)
      {
          result = VOGLEDITOR_TRR_USER_EXIT;
          break;
      }

      if (pRootItem->childCount() > 0)
      {
          vogleditor_apiCallTreeItem* pFirstFrame = pRootItem->child(0);

          bool bStatus = true;
          // if the first snapshot has not been edited, then restore it here, otherwise it will get restored in the recursive call below.
          if (pFirstFrame->has_snapshot() && !pFirstFrame->get_snapshot()->is_edited())
          {
              bStatus = applying_snapshot_and_process_resize(pFirstFrame->get_snapshot()->get_snapshot());
          }

          if (bStatus)
          {
              // replay each API call.
              result = recursive_replay_apicallTreeItem(pRootItem, ppNewSnapshot, apiCallNumber);

              if (result == VOGLEDITOR_TRR_ERROR)
              {
                  QString msg = QString("Replay ending abruptly at frame index %1, global api call %2").arg(m_pTraceReplayer->get_frame_index()).arg(m_pTraceReplayer->get_last_processed_call_counter());
                  vogleditor_output_error(msg.toStdString().c_str());
                  break;
              }
              else if (result == VOGLEDITOR_TRR_SNAPSHOT_SUCCESS)
              {
                  break;
              }
              else if (result == VOGLEDITOR_TRR_USER_EXIT)
              {
                  vogleditor_output_message("Replay stopped");
                  break;
              }
              else
              {
                  QString msg = QString("At trace EOF, frame index %1").arg(m_pTraceReplayer->get_frame_index());
                  vogleditor_output_message(msg.toStdString().c_str());
                  if (!endlessMode)
                  {
                      break;
                  }
              }
          }
          else
          {
              break;
          }
      }
   }

   m_pTraceReplayer->deinit();
   m_window.close();
   return result;
}
vogleditor_tracereplayer_result vogleditor_traceReplayer::recursive_replay_apicallTreeItem(vogleditor_apiCallTreeItem* pItem, vogleditor_gl_state_snapshot** ppNewSnapshot, uint64_t apiCallNumber)
{
    vogleditor_tracereplayer_result result = VOGLEDITOR_TRR_SUCCESS;
    vogleditor_apiCallItem* pApiCall = pItem->apiCallItem();
    if (pApiCall != NULL)
    {
        vogl_trace_packet* pTrace_packet = pApiCall->getTracePacket();

        vogl_gl_replayer::status_t status = vogl_gl_replayer::cStatusOK;

        // See if a window resize or snapshot is pending. If a window resize is pending we must delay a while and pump X events until the window is resized.
        while (m_pTraceReplayer->get_has_pending_window_resize() || m_pTraceReplayer->get_pending_apply_snapshot())
        {
            // Pump X events in case the window is resizing
            if (process_x_events())
            {
                status = m_pTraceReplayer->process_pending_window_resize();
                if (status != vogl_gl_replayer::cStatusResizeWindow)
                    break;
            }
            else
            {
                // most likely the window wants to close, so let's return
                return VOGLEDITOR_TRR_USER_EXIT;
            }
        }

        // replay the trace packet
        if (status == vogl_gl_replayer::cStatusOK)
            status = m_pTraceReplayer->process_next_packet(*pTrace_packet);

        // if that was successful, check to see if a state snapshot is needed
        if ((status != vogl_gl_replayer::cStatusHardFailure) && (status != vogl_gl_replayer::cStatusAtEOF))
        {
            if (ppNewSnapshot != NULL)
            {
               // get the snapshot after the selected api call
               if ((!*ppNewSnapshot) && (m_pTraceReplayer->get_last_processed_call_counter() == static_cast<int64_t>(apiCallNumber)))
               {
                  dynamic_string info;
                  vogleditor_output_message(info.format("Taking snapshot on API call # %" PRIu64 "...", apiCallNumber).c_str());

                  vogl_gl_state_snapshot* pNewSnapshot = m_pTraceReplayer->snapshot_state();
                  if (pNewSnapshot == NULL)
                  {
                      result = VOGLEDITOR_TRR_ERROR;
                      vogleditor_output_error("... snapshot failed!");
                  }
                  else
                  {
                      result = VOGLEDITOR_TRR_SNAPSHOT_SUCCESS;
                      vogleditor_output_message("... snapshot succeeded!\n");
                      *ppNewSnapshot = vogl_new(vogleditor_gl_state_snapshot, pNewSnapshot);
                      if (*ppNewSnapshot == NULL)
                      {
                         result = VOGLEDITOR_TRR_ERROR;
                         vogleditor_output_error("Allocating memory for snapshot container failed!");
                         vogl_delete(pNewSnapshot);
                      }
                  }
               }
            }
        }
        else
        {
            // replaying the trace packet failed, set as error
            result = VOGLEDITOR_TRR_ERROR;
            dynamic_string info;
            vogleditor_output_error(info.format("Unable to replay gl entrypoint at call %" PRIu64, pTrace_packet->get_call_counter()).c_str());
        }
    }

    if (result == VOGLEDITOR_TRR_SUCCESS && pItem->has_snapshot() && pItem->get_snapshot()->is_edited() && pItem->get_snapshot()->is_valid())
    {
        if(applying_snapshot_and_process_resize(pItem->get_snapshot()->get_snapshot()))
        {
            result = VOGLEDITOR_TRR_SUCCESS;
        }
    }

    if (result == VOGLEDITOR_TRR_SUCCESS)
    {
        for (int i = 0; i < pItem->childCount(); i++)
        {
            result = recursive_replay_apicallTreeItem(pItem->child(i), ppNewSnapshot, apiCallNumber);

            if (result != VOGLEDITOR_TRR_SUCCESS)
                break;

            // Pump X events in case the window is resizing
            if (process_x_events() == false)
            {
                // most likely the window wants to close, so let's return
                return VOGLEDITOR_TRR_USER_EXIT;
            }
        }
    }

    return result;
}