bool vogleditor_traceReplayer::recursive_replay_apicallTreeItem(vogleditor_apiCallTreeItem* pItem, vogleditor_gl_state_snapshot** ppNewSnapshot, uint64_t apiCallNumber)
{
    bool bStatus = true;

    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
            bStatus = process_x_events();
            if (bStatus)
            {
                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 false;
            }
        }

        // 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)))
               {
                  vogl_printf("Taking snapshot on API call # %" PRIu64 "\n", apiCallNumber);

                  vogl_gl_state_snapshot* pNewSnapshot = m_pTraceReplayer->snapshot_state();
                  if (pNewSnapshot == NULL)
                  {
                      vogl_error_printf("Taking new snapshot failed!\n");
                  }
                  else
                  {
                      vogl_printf("Taking snapshot succeeded\n");
                      *ppNewSnapshot = vogl_new(vogleditor_gl_state_snapshot, pNewSnapshot);
                      if (*ppNewSnapshot == NULL)
                      {
                         vogl_error_printf("Allocating memory for snapshot container failed!\n");
                         vogl_delete(pNewSnapshot);
                      }
                  }

                  bStatus = false;
               }
            }
        }
        else
        {
            // replaying the trace packet failed, set as error
            vogl_error_printf("%s: unable to replay gl entrypoint at call %" PRIu64 "\n", VOGL_FUNCTION_NAME, pTrace_packet->get_call_counter());
            bStatus = false;
        }
    }

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

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

            if (!bStatus)
                break;
        }
    }

    return bStatus;
}
bool 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))
   {
      vogl_error_printf("%s: Failed opening GL replayer window!\n", VOGL_FUNCTION_NAME);
      return false;
   }

   uint replayer_flags = cGLReplayerForceDebugContexts;
   if (!m_pTraceReplayer->init(replayer_flags, &m_window, m_pTraceReader->get_sof_packet(), m_pTraceReader->get_multi_blob_manager()))
   {
      vogl_error_printf("%s: Failed initializing GL replayer\n", VOGL_FUNCTION_NAME);
      m_window.close();
      return false;
   }

   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();

   bool bStatus = true;

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

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

          // 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.
              bStatus = recursive_replay_apicallTreeItem(pRootItem, ppNewSnapshot, apiCallNumber);

              if (bStatus == false)
              {
                 vogl_error_printf("%s: Replay ending abruptly at frame index %u, global api call %" PRIu64 "\n", VOGL_FUNCTION_NAME, m_pTraceReplayer->get_frame_index(), m_pTraceReplayer->get_last_processed_call_counter());
                 break;
              }
              else
              {
                 vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_NAME, m_pTraceReplayer->get_frame_index());
                 if (!endlessMode)
                 {
                     break;
                 }
              }
          }
          else
          {
              break;
          }
      }
   }

   m_pTraceReplayer->deinit();
   m_window.close();
   return bStatus;
}
bool vogl_trace_file_writer::close()
{
    VOGL_FUNC_TRACER

    vogl_debug_printf("%s\n", VOGL_FUNCTION_INFO_CSTR);

    if (!m_stream.is_opened())
        return false;

    vogl_message_printf("%s: Flushing trace file %s (this could take some time), %u total frame file offsets\n", VOGL_FUNCTION_INFO_CSTR, m_filename.get_ptr(), m_frame_file_offsets.size());

    bool success = true;

    dynamic_string trace_archive_filename;

    if (!write_eof_packet())
    {
        vogl_error_printf("%s: Failed writing to trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, m_filename.get_ptr());
        success = false;
    }
    else if (m_pTrace_archive.get())
    {
        trace_archive_filename = m_pTrace_archive->get_archive_filename();

        if ((!write_frame_file_offsets_to_archive()) || !m_pTrace_archive->deinit())
        {
            vogl_error_printf("%s: Failed closing trace archive \"%s\"!\n", VOGL_FUNCTION_INFO_CSTR, trace_archive_filename.get_ptr());
            success = false;
        }
        else
        {
            if (!file_utils::get_file_size(trace_archive_filename.get_ptr(), m_sof_packet.m_archive_size))
            {
                vogl_error_printf("%s: Failed determining file size of archive file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, trace_archive_filename.get_ptr());
                success = false;
            }
            else if (m_sof_packet.m_archive_size)
            {
                m_sof_packet.m_archive_offset = m_stream.get_size();

                vogl_message_printf("Copying %" PRIu64 " archive bytes into output trace file\n", m_sof_packet.m_archive_size);

                if (!m_stream.write_file_data(trace_archive_filename.get_ptr()))
                {
                    vogl_error_printf("%s: Failed copying source archive \"%s\" into trace file!\n", VOGL_FUNCTION_INFO_CSTR, trace_archive_filename.get_ptr());
                    success = false;
                }

                m_sof_packet.m_archive_size = m_stream.get_size() - m_sof_packet.m_archive_offset;
            }
        }

        if (success)
        {
            m_sof_packet.finalize();
            VOGL_VERIFY(m_sof_packet.full_validation(sizeof(m_sof_packet)));

            if (!m_stream.seek(0, false) || (m_stream.write(&m_sof_packet, sizeof(m_sof_packet)) != sizeof(m_sof_packet)))
            {
                vogl_error_printf("%s: Failed writing to trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, m_filename.get_ptr());
                success = false;
            }
        }
    }

    close_archive(trace_archive_filename.get_ptr());

    uint64_t total_trace_file_size = m_stream.get_size();

    if (!m_stream.close())
    {
        vogl_error_printf("Failed writing to or closing trace file!\n");
        success = false;
    }

    dynamic_string full_trace_filename(m_filename);
    file_utils::full_path(full_trace_filename);

    if (success)
        vogl_message_printf("%s: Successfully closed trace file %s, total file size: %s\n", VOGL_FUNCTION_INFO_CSTR, full_trace_filename.get_ptr(), uint64_to_string_with_commas(total_trace_file_size).get_ptr());
    else
        vogl_error_printf("%s: Failed closing trace file %s! (Trace will probably not be valid.)\n", VOGL_FUNCTION_INFO_CSTR, full_trace_filename.get_ptr());

    return success;
}
// pTrace_archive may be NULL. Takes ownership of pTrace_archive.
// TODO: Get rid of the demarcation packet, etc. Make the initial sequence of packets more explicit.
bool vogl_trace_file_writer::open(const char *pFilename, vogl_archive_blob_manager *pTrace_archive, bool delete_archive, bool write_demarcation_packet, uint pointer_sizes)
{
    VOGL_FUNC_TRACER

    close();

    if (!pFilename)
        return false;

    m_filename = pFilename;
    if (!m_stream.open(pFilename, cDataStreamWritable | cDataStreamSeekable, false))
    {
        vogl_error_printf("%s: Failed opening trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, pFilename);
        return false;
    }

    vogl_message_printf("%s: Prepping trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, pFilename);

    m_sof_packet.init();
    m_sof_packet.m_pointer_sizes = pointer_sizes;
    m_sof_packet.m_first_packet_offset = sizeof(m_sof_packet);

    md5_hash h(gen_uuid());
    VOGL_ASSUME(sizeof(h) == sizeof(m_sof_packet.m_uuid));
    memcpy(&m_sof_packet.m_uuid, &h, sizeof(h));

    m_sof_packet.finalize();
    VOGL_VERIFY(m_sof_packet.full_validation(sizeof(m_sof_packet)));

    if (m_stream.write(&m_sof_packet, sizeof(m_sof_packet)) != sizeof(m_sof_packet))
    {
        vogl_error_printf("%s: Failed writing to trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, pFilename);
        return false;
    }

    if (pTrace_archive)
    {
        m_pTrace_archive.reset(pTrace_archive);
        m_delete_archive = delete_archive;
    }
    else
    {
        m_pTrace_archive.reset(vogl_new(vogl_archive_blob_manager));
        m_delete_archive = true;

        if (!m_pTrace_archive->init_file_temp(cBMFReadWrite))
        {
            vogl_error_printf("%s: Failed opening temp archive!\n", VOGL_FUNCTION_INFO_CSTR);

            m_pTrace_archive.reset();

            return false;
        }
    }

    // TODO: The trace reader records the first offset right after SOF, I would like to do this after the demarcation packet.
    m_frame_file_offsets.reserve(10000);
    m_frame_file_offsets.resize(0);
    m_frame_file_offsets.push_back(m_stream.get_ofs());

    write_ctypes_packet();

    write_entrypoints_packet();

    if (write_demarcation_packet)
    {
        vogl_write_glInternalTraceCommandRAD(m_stream, m_pCTypes, cITCRDemarcation, 0, NULL);
    }

    vogl_message_printf("%s: Finished opening trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, pFilename);

    return true;
}
Beispiel #5
0
 bool file_utils::does_dir_exist(const char *pDir)
 {
     vogl_error_printf("Unimplemented\n");
     return false;
 }
Beispiel #6
0
    // This helper only works on files with valid file sizes (i.e. it won't work on some files under proc such as /proc/self/status).
    bool file_utils::read_text_file(const char *pPath, dynamic_string_array &lines, uint32_t flags)
    {
        cfile_stream stream;
        if (!stream.open(pPath, cDataStreamReadable))
        {
            if (flags & cRTFPrintErrorMessages)
                vogl_error_printf("Failed opening text file \"%s\" for reading\n", pPath);
            else if (flags & cRTFPrintWarningMessages)
                vogl_warning_printf("Failed opening text file \"%s\" for reading\n", pPath);

            return false;
        }

        int line_count = 0;
        dynamic_string line_str;
        while (stream.get_remaining())
        {
            ++line_count;
            if (!stream.read_line(line_str))
            {
                if (flags & cRTFPrintErrorMessages)
                    vogl_error_printf("Failed reading from text file \"%s\"\n", pPath);
                else if (flags & cRTFPrintWarningMessages)
                    vogl_warning_printf("Failed reading from text file \"%s\"\n", pPath);

                break;
            }

            if (flags & cRTFTrim)
                line_str.trim();

            if (flags & cRTFTrimEnd)
                line_str.trim_end();

            if (flags & cRTFIgnoreEmptyLines)
            {
                if (line_str.is_empty())
                    continue;
            }

            if ((flags & cRTFIgnoreCommentedLines) && (line_str.get_len() >= 2))
            {
                bool found_comment = false;

                for (int i = 0; i < static_cast<int>(line_str.get_len()); ++i)
                {
                    char c = line_str[i];

                    if ((c == ' ') || (c == '\t'))
                        continue;
                    else if ((c == '/') && (line_str[i + 1] == '/'))
                        found_comment = true;

                    break;
                }

                if (found_comment)
                    continue;
            }

            lines.push_back(line_str);
        }

        return true;
    }
Beispiel #7
0
 bool file_utils::disable_read_only(const char *pFilename)
 {
     pFilename;
     vogl_error_printf("Unimplemented\n");
     return false;
 }
bool vogl_default_framebuffer_state::restore(const vogl_context_info &context_info, bool restore_front_buffer) const
{
    VOGL_NOTE_UNUSED(context_info);

    if (!m_valid)
        return false;

    // TODO: Test multisampled default framebuffers
    // TODO: Check to ensure the stored fb is compatible with the current framebuffer
    const GLenum tex_target = (m_fb_attribs.m_samples > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;

    vogl_scoped_state_saver framebuffer_state_saver(cGSTReadBuffer, cGSTDrawBuffer);

    vogl_scoped_binding_state orig_framebuffers(GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER, GL_TEXTURE_2D, GL_TEXTURE_2D_MULTISAMPLE);

    GL_ENTRYPOINT(glBindFramebuffer)(GL_DRAW_FRAMEBUFFER, 0);
    VOGL_CHECK_GL_ERROR;

    for (uint i = 0; i < cDefFramebufferTotal; i++)
    {
        if ((!restore_front_buffer) && ((i == cDefFramebufferFrontLeft) || (i == cDefFramebufferFrontRight)))
            continue;

        if (!m_textures[i].is_valid())
            continue;

        GL_ENTRYPOINT(glDrawBuffer)((i == cDefFramebufferDepthStencil) ? GL_NONE : g_def_framebuffer_enums[i]);
        if (vogl_check_gl_error_internal(true))
            continue;

        GLuint64 tex_handle64 = 0;

        vogl_handle_remapper def_handle_remapper;
        if (!m_textures[i].restore(context_info, def_handle_remapper, tex_handle64))
        {
            vogl_error_printf("%s: Failed restoring texture %u\n", VOGL_METHOD_NAME, i);
            continue;
        }

        GLuint tex_handle = static_cast<GLuint>(tex_handle64);

        // Create FBO
        GLuint fbo_handle = 0;
        GL_ENTRYPOINT(glGenFramebuffers)(1, &fbo_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glBindFramebuffer)(GL_READ_FRAMEBUFFER, fbo_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glFramebufferTexture2D)(GL_READ_FRAMEBUFFER, (i == cDefFramebufferDepthStencil) ? GL_DEPTH_STENCIL_ATTACHMENT : GL_COLOR_ATTACHMENT0, tex_target, tex_handle, 0);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glReadBuffer)((i == cDefFramebufferDepthStencil) ? GL_NONE : GL_COLOR_ATTACHMENT0);
        VOGL_CHECK_GL_ERROR;

        GLenum cur_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_READ_FRAMEBUFFER);
        VOGL_CHECK_GL_ERROR;

        bool status = true;

        if (cur_status == GL_FRAMEBUFFER_COMPLETE)
        {
            GL_ENTRYPOINT(glBlitFramebuffer)(
                0, 0, m_fb_attribs.m_width, m_fb_attribs.m_height,
                0, 0, m_fb_attribs.m_width, m_fb_attribs.m_height,
                (i == cDefFramebufferDepthStencil) ? (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) : GL_COLOR_BUFFER_BIT,
                GL_NEAREST);

            if (vogl_check_gl_error_internal())
            {
                status = false;
            }
        }

        if (!status)
        {
            vogl_warning_printf("%s: Failed blitting framebuffer %u\n", VOGL_METHOD_NAME, i);
        }

        // Delete FBO
        GL_ENTRYPOINT(glBindFramebuffer)(GL_READ_FRAMEBUFFER, 0);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glDeleteFramebuffers)(1, &fbo_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glReadBuffer)(GL_FRONT_LEFT);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glBindTexture)(tex_target, 0);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glDeleteTextures)(1, &tex_handle);
        VOGL_CHECK_GL_ERROR;
    }

    return true;
}
Beispiel #9
0
//----------------------------------------------------------------------------------------------------------------------
// voglsym_main_loop
//----------------------------------------------------------------------------------------------------------------------
static bool
voglsym_main_loop(char *argv[])
{
    VOGL_FUNC_TRACER

    dynamic_string tracefile_arch;
    dynamic_string actual_trace_filename;
    vector<addr_data_t> addr_data_arr;
    vector<btrace_module_info> module_infos;

    dynamic_string trace_filename(g_command_line_params().get_value_as_string_or_empty("", 1));
    if (trace_filename.is_empty())
    {
        vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_NAME);
        return false;
    }

    vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(
        trace_filename,
        actual_trace_filename,
        g_command_line_params().get_value_as_string_or_empty("loose_file_path").get_ptr()));
    if (!pTrace_reader.get())
    {
        vogl_error_printf("%s: File not found, or unable to determine file type of trace file \"%s\"\n", VOGL_FUNCTION_NAME, trace_filename.get_ptr());
        return false;
    }

    bool resolve_symbols = g_command_line_params().get_value_as_bool("resolve_symbols");

    if (resolve_symbols)
        vogl_printf("Resolving symbols in trace file %s\n\n", actual_trace_filename.get_ptr());
    else
        vogl_printf("Reading trace file %s\n\n", actual_trace_filename.get_ptr());

    // compiler_info.json
    dump_compiler_info(pTrace_reader.get(), tracefile_arch);

    if (resolve_symbols)
    {
        if (tracefile_arch.size())
        {
            bool is_64bit = (sizeof(void *) == 8);
            bool trace_file_arch_is_64bits = !tracefile_arch.compare("64bit");

            if (trace_file_arch_is_64bits != is_64bit)
            {
                const char *arch_str = is_64bit ? "64-bit" : "32-bit";
                vogl_error_printf("ERROR: %s is %s, tracefile is %s.\n", argv[0], arch_str, tracefile_arch.c_str());
                vogl_error_printf("ERROR: Same architecture required to resolve symbols.\n");
                return -1;
            }
        }

        if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_SYMS_FILENAME))
        {
            vogl_error_printf("ERROR: Symbols have already resolved for this tracefile.\n");
            return -1;
        }
    }

    // machine_info.json
    dump_machine_info(pTrace_reader.get(), module_infos);

    // backtrace_map_addrs.json
    get_backtrace_map_addrs(pTrace_reader.get(), addr_data_arr);

    // backtrace_map_syms.json
    dump_backtrace_map_syms(pTrace_reader.get());

    // Spew our module information
    if (module_infos.size())
    {
        vogl_header1_printf("%s\n", std::string(78, '*').c_str());
        vogl_header1_printf("%s\n", "Modules");
        vogl_header1_printf("%s\n", std::string(78, '*').c_str());

        for (uint i = 0; i < module_infos.size(); i++)
        {
            char uuid_str[41];
            const btrace_module_info &module_info = module_infos[i];

            btrace_uuid_to_str(uuid_str, module_info.uuid, module_info.uuid_len);

            vogl_printf("0x%" PRIxPTR " (%u) %s %s %s",
                       module_info.base_address, module_info.address_size, uuid_str,
                       module_info.filename, module_info.is_exe ? "(exe)" : "");

            if (resolve_symbols)
            {
                const char *debug_filename = NULL;
                if (btrace_dlopen_add_module(module_info))
                {
                    debug_filename = btrace_get_debug_filename(module_info.filename);
                }

                if (debug_filename)
                {
                    vogl_printf(" [%s]", debug_filename);
                }
            }

            vogl_printf("\n");
        }

        vogl_printf("\n");
    }

    // Spew our backtrace addresses
    if (addr_data_arr.size())
    {
        vogl_header1_printf("%s\n", std::string(78, '*').c_str());
        vogl_header1_printf("%s\n", VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME);
        vogl_header1_printf("%s\n", std::string(78, '*').c_str());

        vogl_printf("index (count): addr0, addr1, ...\n");
        for (uint i = 0; i < addr_data_arr.size(); i++)
        {
            const addr_data_t &addr_data = addr_data_arr[i];

            vogl_printf("0x%x (%u): ", addr_data.index, addr_data.count);
            for (uint j = 0; j < addr_data.addrs.size(); j++)
            {
                vogl_printf("%" PRIxPTR " ", addr_data.addrs[j]);
            }
            vogl_printf("\n");
        }

        vogl_printf("\n");
    }

    // Resolve symbols if we're supposed to.
    //$ TODO: The resolve symbols should be added to the trace file?
    if (resolve_symbols)
    {
        json_document doc;
        json_node *pRoot = doc.get_root();

        pRoot->init_array();

        vogl_header1_printf("%s\n", std::string(78, '*').c_str());
        vogl_header1_printf("%s\n", "Resolving symbols...");
        vogl_header1_printf("%s\n", std::string(78, '*').c_str());

        for (uint i = 0; i < addr_data_arr.size(); i++)
        {
            const addr_data_t &addr_data = addr_data_arr[i];
            json_node &syms_arr = pRoot->add_array();

            for (uint j = 0; j < addr_data.addrs.size(); j++)
            {
                btrace_info trace_info;
                uintptr_t addr = addr_data.addrs[j];
                bool success = btrace_resolve_addr(&trace_info, addr,
                                                   BTRACE_RESOLVE_ADDR_GET_FILENAME | BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC);

                dynamic_string sym;
                if (!success)
                {
                    sym = "?";
                }
                else if (trace_info.function[0] && trace_info.filename[0])
                {
                    // Got function and/or filename.
                    sym.format("%s (%s+0x%" PRIx64 ") at %s:%i",
                               trace_info.function,
                               trace_info.module[0] ? trace_info.module : "?",
                               cast_val_to_uint64(trace_info.offset),
                               trace_info.filename,
                               trace_info.linenumber);
                }
                else if (trace_info.function[0])
                {
                    // Got function, no filename.
                    sym.format("%s (%s+0x%" PRIx64 ")",
                               trace_info.function,
                               trace_info.module[0] ? trace_info.module : "?",
                               cast_val_to_uint64(trace_info.offset));
                }
                else
                {
                    // Only got modulename (no debugging information found).
                    sym.format("(%s+0x%" PRIx64 ")",
                               trace_info.module[0] ? trace_info.module : "?",
                               cast_val_to_uint64(trace_info.offset));
                }

                syms_arr.add_value(sym);
            }
        }

        doc.print(true, 0, 0);
    }

    return true;
}
Beispiel #10
0
 bool file_utils::is_read_only(const char *pFilename)
 {
     VOGL_NOTE_UNUSED(pFilename);
     vogl_error_printf("Unimplemented\n");
     return false;
 }
Beispiel #11
0
bool vogl_sampler_state::snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 handle, GLenum target)
{
    VOGL_FUNC_TRACER

    VOGL_NOTE_UNUSED(remapper);
    VOGL_CHECK_GL_ERROR;
    (void)target;

    clear();

    VOGL_ASSERT(handle <= cUINT32_MAX);

    m_snapshot_handle = static_cast<uint32>(handle);

    bool any_gl_errors = false;

#define GET_INT(pname)                                                            \
    do                                                                            \
    {                                                                             \
        int values[4] = { 0, 0, 0, 0 };                                           \
        GL_ENTRYPOINT(glGetSamplerParameteriv)(m_snapshot_handle, pname, values); \
        if (vogl_check_gl_error())                                                 \
            any_gl_errors = true;                                                 \
        m_params.insert(pname, 0, values, sizeof(values[0]));                     \
    } while (0)
#define GET_FLOAT(pname)                                                          \
    do                                                                            \
    {                                                                             \
        float values[4] = { 0, 0, 0, 0 };                                         \
        GL_ENTRYPOINT(glGetSamplerParameterfv)(m_snapshot_handle, pname, values); \
        if (vogl_check_gl_error())                                                 \
            any_gl_errors = true;                                                 \
        m_params.insert(pname, 0, values, sizeof(values[0]));                     \
    } while (0)

    GET_INT(GL_TEXTURE_MAG_FILTER);
    GET_INT(GL_TEXTURE_MIN_FILTER);
    GET_FLOAT(GL_TEXTURE_MIN_LOD);
    GET_FLOAT(GL_TEXTURE_MAX_LOD);
    GET_INT(GL_TEXTURE_WRAP_S);
    GET_INT(GL_TEXTURE_WRAP_T);
    GET_INT(GL_TEXTURE_WRAP_R);
    GET_FLOAT(GL_TEXTURE_BORDER_COLOR);
    GET_INT(GL_TEXTURE_COMPARE_MODE);
    GET_INT(GL_TEXTURE_COMPARE_FUNC);

    if (context_info.supports_extension("GL_EXT_texture_filter_anisotropic"))
    {
        GET_FLOAT(GL_TEXTURE_MAX_ANISOTROPY_EXT);
    }

    if (context_info.supports_extension("GL_EXT_texture_sRGB_decode"))
    {
        GET_INT(GL_TEXTURE_SRGB_DECODE_EXT);
    }

#undef GET_INT
#undef GET_FLOAT

    if (any_gl_errors)
    {
        clear();

        vogl_error_printf("%s: GL error while enumerating sampler %" PRIu64 "'s' params\n", VOGL_METHOD_NAME, (uint64_t)handle);
        return false;
    }

    m_is_valid = true;

    return true;
}
bool vogl_binary_trace_file_reader::open(const char *pFilename, const char *pLoose_file_path)
{
    VOGL_FUNC_TRACER

    close();

    if (!init_loose_file_blob_manager(pFilename, pLoose_file_path))
        return false;

    if (!m_trace_stream.open(pFilename, cDataStreamReadable | cDataStreamSeekable, true))
    {
        close();
        return false;
    }

    m_trace_file_size = m_trace_stream.get_size();

    if (m_trace_stream.read(&m_sof_packet, sizeof(m_sof_packet)) != sizeof(m_sof_packet))
    {
        close();
        return false;
    }

    if (!m_sof_packet.full_validation(sizeof(m_sof_packet)))
    {
        close();
        return false;
    }

    if (m_sof_packet.m_version < static_cast<uint16>(VOGL_TRACE_FILE_VERSION))
    {
        vogl_error_printf("%s: Trace file version is not supported, found version 0x%04X, expected version 0x%04X!\n", VOGL_FUNCTION_INFO_CSTR, m_sof_packet.m_version, VOGL_TRACE_FILE_VERSION);
        return false;
    }
    else if (m_sof_packet.m_version > static_cast<uint16>(VOGL_TRACE_FILE_VERSION))
    {
        // TODO: Make this an error? Backwards compat?
        vogl_warning_printf("%s: Trace file version is 0x%04X, expected version 0x%04X, this may not work at all!\n", VOGL_FUNCTION_INFO_CSTR, m_sof_packet.m_version, VOGL_TRACE_FILE_VERSION);
    }

    if (m_sof_packet.m_archive_size)
    {
        if (!m_archive_blob_manager.init_file(cBMFReadable | cBMFOpenExisting, pFilename, m_sof_packet.m_archive_offset, m_sof_packet.m_archive_size))
        {
            vogl_error_printf("%s: Failed reading in-trace archive!\n", VOGL_FUNCTION_INFO_CSTR);
            return false;
        }
    }

    m_packet_buf.reserve(512 * 1024);

    m_trace_stream.seek(m_sof_packet.m_first_packet_offset, false);

    if (!read_frame_file_offsets())
    {
        // Keep this in sync with the offset pushed in vogl_init_tracefile()!
        m_frame_file_offsets.push_back(get_cur_file_ofs());
    }

    return true;
}
Beispiel #13
0
//----------------------------------------------------------------------------------------------------------------------
// tool_replay_mode
//----------------------------------------------------------------------------------------------------------------------
static bool tool_replay_mode()
{
    VOGL_FUNC_TRACER

    dynamic_string trace_filename(g_command_line_params().get_value_as_string_or_empty("", 1));
    if (trace_filename.is_empty())
    {
        vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_INFO_CSTR);
        return false;
    }

    dynamic_string actual_trace_filename;
    vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(
                trace_filename,
                actual_trace_filename,
                g_command_line_params().get_value_as_string_or_empty("loose_file_path").get_ptr()));
    if (!pTrace_reader.get())
    {
        vogl_error_printf("%s: File not found, or unable to determine file type of trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, trace_filename.get_ptr());
        return false;
    }

    vogl_printf("Reading trace file %s\n", actual_trace_filename.get_ptr());

    vogl_gl_replayer replayer;
    vogl_replay_window window;

    uint replayer_flags = get_replayer_flags_from_command_line_params();

    // TODO: This will create a window with default attributes, which seems fine for the majority of traces.
    // Unfortunately, some GL call streams *don't* want an alpha channel, or depth, or stencil etc. in the default framebuffer so this may become a problem.
    // Also, this design only supports a single window, which is going to be a problem with multiple window traces.
    if (!window.open(g_command_line_params().get_value_as_int("width", 0, 1024, 1, 65535), g_command_line_params().get_value_as_int("height", 0, 768, 1, 65535), g_command_line_params().get_value_as_int("msaa", 0, 0, 0, 65535)))
    {
        vogl_error_printf("%s: Failed initializing replay window\n", VOGL_FUNCTION_INFO_CSTR);
        return false;
    }

    if (!replayer.init(replayer_flags, &window, pTrace_reader->get_sof_packet(), pTrace_reader->get_multi_blob_manager()))
    {
        vogl_error_printf("%s: Failed initializing GL replayer\n", VOGL_FUNCTION_INFO_CSTR);
        return false;
    }

    // Disable all glGetError() calls in vogl_utils.cpp.
    vogl_disable_gl_get_error();

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

    Atom wmDeleteMessage = XInternAtom(window.get_display(), "WM_DELETE_WINDOW", False);
    XSetWMProtocols(window.get_display(), window.get_xwindow(), &wmDeleteMessage, 1);

    // Bool win_mapped = false;

    vogl_gl_state_snapshot *pSnapshot = NULL;
    int64_t snapshot_loop_start_frame = -1;
    int64_t snapshot_loop_end_frame = -1;

    vogl::hash_map<uint64_t> keys_pressed, keys_down;

    int loop_frame = g_command_line_params().get_value_as_int("loop_frame", 0, -1);
    int loop_len = math::maximum<int>(g_command_line_params().get_value_as_int("loop_len", 0, 1), 1);
    int loop_count = math::maximum<int>(g_command_line_params().get_value_as_int("loop_count", 0, cINT32_MAX), 1);
    bool endless_mode = g_command_line_params().get_value_as_bool("endless");

    timer tm;
    tm.start();

    for (;;)
    {
        tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Main Loop");

        while (X11_Pending(window.get_display()))
        {
            XEvent newEvent;

            // Watch for new X eventsn
            XNextEvent(window.get_display(), &newEvent);

            switch (newEvent.type)
            {
                case KeyPress:
                {
                    KeySym xsym = XLookupKeysym(&newEvent.xkey, 0);

                    //printf("KeyPress 0%04llX %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym);

                    keys_down.insert(xsym);
                    keys_pressed.insert(xsym);

                    break;
                }
                case KeyRelease:
                {
                    KeySym xsym = XLookupKeysym(&newEvent.xkey, 0);

                    //printf("KeyRelease 0x%04llX %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym);

                    keys_down.erase(xsym);

                    break;
                }
                case FocusIn:
                case FocusOut:
                {
                    //printf("FocusIn/FocusOut\n");

                    keys_down.reset();

                    break;
                }
                case MappingNotify:
                {
                    //XRefreshKeyboardMapping(&newEvent);
                    break;
                }
                case UnmapNotify:
                {
                    // printf("UnmapNotify\n");
                    // win_mapped = false;

                    keys_down.reset();

                    break;
                }
                case MapNotify:
                {
                    // printf("MapNotify\n");
                    // win_mapped = true;

                    keys_down.reset();

                    if (!replayer.update_window_dimensions())
                        goto error_exit;

                    break;
                }
                case ConfigureNotify:
                {
                    if (!replayer.update_window_dimensions())
                        goto error_exit;

                    break;
                }
                case DestroyNotify:
                {
                    vogl_message_printf("Exiting\n");
                    goto normal_exit;
                }
                case ClientMessage:
                {
                    if (newEvent.xclient.data.l[0] == (int)wmDeleteMessage)
                    {
                        vogl_message_printf("Exiting\n");
                        goto normal_exit;
                    }

                    break;
                }
                default:
                    break;
            }
        }

        if (replayer.get_at_frame_boundary())
        {
            if ((!pSnapshot) && (loop_frame != -1) && (static_cast<int64_t>(replayer.get_frame_index()) == loop_frame))
            {
                vogl_debug_printf("%s: Capturing replayer state at start of frame %u\n", VOGL_FUNCTION_INFO_CSTR, replayer.get_frame_index());

                pSnapshot = replayer.snapshot_state();

                if (pSnapshot)
                {
                    vogl_printf("Snapshot succeeded\n");

                    snapshot_loop_start_frame = pTrace_reader->get_cur_frame();
                    snapshot_loop_end_frame = pTrace_reader->get_cur_frame() + loop_len;

                    vogl_debug_printf("%s: Loop start: %" PRIi64 " Loop end: %" PRIi64 "\n", VOGL_FUNCTION_INFO_CSTR, snapshot_loop_start_frame, snapshot_loop_end_frame);
                }
                else
                {
                    vogl_error_printf("Snapshot failed!\n");
                    loop_frame = -1;
                }
            }
        }

        vogl_gl_replayer::status_t status = replayer.process_pending_window_resize();
        if (status == vogl_gl_replayer::cStatusOK)
        {
            for (;;)
            {
                status = replayer.process_next_packet(*pTrace_reader);

                if ((status == vogl_gl_replayer::cStatusNextFrame) ||
                    (status == vogl_gl_replayer::cStatusResizeWindow) ||
                    (status == vogl_gl_replayer::cStatusAtEOF) ||
                    (status == vogl_gl_replayer::cStatusHardFailure))
                {
                    break;
                }
            }
        }

        if (status == vogl_gl_replayer::cStatusHardFailure)
            break;

        if (status == vogl_gl_replayer::cStatusAtEOF)
        {
            vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_INFO_CSTR, replayer.get_frame_index());
        }

        if (replayer.get_at_frame_boundary() &&
                pSnapshot && 
                (loop_count > 0) &&
                ((pTrace_reader->get_cur_frame() == snapshot_loop_end_frame) || (status == vogl_gl_replayer::cStatusAtEOF)))
        {
            status = replayer.begin_applying_snapshot(pSnapshot, false);
            if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
                goto error_exit;

            pTrace_reader->seek_to_frame(static_cast<uint>(snapshot_loop_start_frame));

            vogl_debug_printf("%s: Applying snapshot and seeking back to frame %" PRIi64 "\n", VOGL_FUNCTION_INFO_CSTR, snapshot_loop_start_frame);
            loop_count--;
        }
        else
        {
            bool print_progress = (status == vogl_gl_replayer::cStatusAtEOF) ||
                    ((replayer.get_at_frame_boundary()) && ((replayer.get_frame_index() % 100) == 0));
            if (print_progress)
            {
                if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER)
                {
                    vogl_binary_trace_file_reader &binary_trace_reader = *static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get());

                    vogl_printf("Replay now at frame index %u, trace file offet %" PRIu64 ", GL call counter %" PRIu64 ", %3.2f%% percent complete\n",
                               replayer.get_frame_index(),
                               binary_trace_reader.get_cur_file_ofs(),
                               replayer.get_last_parsed_call_counter(),
                               binary_trace_reader.get_trace_file_size() ? (binary_trace_reader.get_cur_file_ofs() * 100.0f) / binary_trace_reader.get_trace_file_size() : 0);
                }
            }

            if (status == vogl_gl_replayer::cStatusAtEOF)
            {
                if (!endless_mode)
                {
                    double time_since_start = tm.get_elapsed_secs();

                    vogl_printf("%u total swaps, %.3f secs, %3.3f avg fps\n", replayer.get_total_swaps(), time_since_start, replayer.get_frame_index() / time_since_start);
                    break;
                }

                vogl_printf("Resetting state and rewinding back to frame 0\n");

                replayer.reset_state();

                if (!pTrace_reader->seek_to_frame(0))
                {
                    vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_INFO_CSTR);
                    goto error_exit;
                }
            }
        }

        telemetry_tick();
    }

normal_exit:
    return true;

error_exit:
    return false;
}
Beispiel #14
0
//----------------------------------------------------------------------------------------------------------------------
// Function vogl_init_gl_entrypoint_descs
//----------------------------------------------------------------------------------------------------------------------
void vogl_init_gl_entrypoint_descs()
{
    gl_entrypoint_param_desc_t *pDst = &g_vogl_entrypoint_param_descs[0][0];

#define DEF_FUNCTION_BEGIN(exported, category, ret, ret_type, num_params, name, args, params)
#define DEF_FUNCTION_INFO(namespace_index, return_spectype, category, version, profile, deprecated, is_whitelisted, is_nullable, whitelisted_for_displaylists, listable)
#define DEF_FUNCTION_BEGIN_PARAMS \
    {                             \
        gl_entrypoint_param_desc_t *pCur = pDst;
#define DEF_FUNCTION_IN_VALUE_PARAM(namespace_index, spectype, type, ctype, name) \
    {                                                                             \
        pCur->m_pSpec_type = #spectype;                                           \
        pCur->m_pName = #name;                                                    \
        pCur->m_ctype = ctype;                                                    \
        pCur->m_class = VOGL_VALUE_PARAM;                                          \
        pCur->m_input = true;                                                     \
        pCur->m_namespace = (vogl_namespace_t)namespace_index;                     \
        ++pCur;                                                                   \
    }
#define DEF_FUNCTION_IN_REFERENCE_PARAM(namespace_index, spectype, type, ctype, name) \
    {                                                                                 \
        pCur->m_pSpec_type = #spectype;                                               \
        pCur->m_pName = #name;                                                        \
        pCur->m_ctype = ctype;                                                        \
        pCur->m_class = VOGL_REF_PARAM;                                                \
        pCur->m_input = true;                                                         \
        pCur->m_namespace = (vogl_namespace_t)namespace_index;                         \
        ++pCur;                                                                       \
    }
#define DEF_FUNCTION_IN_ARRAY_PARAM(namespace_index, spectype, type, ctype, name, size) \
    {                                                                                   \
        pCur->m_pSpec_type = #spectype;                                                 \
        pCur->m_pName = #name;                                                          \
        pCur->m_ctype = ctype;                                                          \
        pCur->m_class = VOGL_ARRAY_PARAM;                                                \
        pCur->m_input = true;                                                           \
        pCur->m_pSize = #size;                                                          \
        pCur->m_namespace = (vogl_namespace_t)namespace_index;                           \
        ++pCur;                                                                         \
    }
#define DEF_FUNCTION_OUT_REFERENCE_PARAM(namespace_index, spectype, type, ctype, name) \
    {                                                                                  \
        pCur->m_pSpec_type = #spectype;                                                \
        pCur->m_pName = #name;                                                         \
        pCur->m_ctype = ctype;                                                         \
        pCur->m_class = VOGL_ARRAY_PARAM;                                               \
        pCur->m_input = false;                                                         \
        pCur->m_namespace = (vogl_namespace_t)namespace_index;                          \
        ++pCur;                                                                        \
    }
#define DEF_FUNCTION_OUT_ARRAY_PARAM(namespace_index, spectype, type, ctype, name, size) \
    {                                                                                    \
        pCur->m_pSpec_type = #spectype;                                                  \
        pCur->m_pName = #name;                                                           \
        pCur->m_ctype = ctype;                                                           \
        pCur->m_class = VOGL_ARRAY_PARAM;                                                 \
        pCur->m_input = false;                                                           \
        pCur->m_pSize = #size;                                                           \
        pCur->m_namespace = (vogl_namespace_t)namespace_index;                            \
        ++pCur;                                                                          \
    }
#define DEF_FUNCTION_END_PARAMS                                    \
    VOGL_ASSERT((pCur - pDst) <= VOGL_MAX_ENTRYPOINT_PARAMETERS); \
    }
#define DEF_FUNCTION_RETURN(spectype, type, ctype)
#define DEF_FUNCTION_END(exported, category, ret, ret_type, num_params, name, args, params) pDst += VOGL_MAX_ENTRYPOINT_PARAMETERS;

#include "gl_glx_wgl_func_descs.inc"

    for (uint32_t i = 0; i < VOGL_ARRAY_SIZE(g_custom_array_size_macro_indices); i++)
    {
        uint32_t idx = g_custom_array_size_macro_indices[i];
        uint32_t func = idx >> 16;
        uint32_t param = idx & 0xFFFF;
        VOGL_ASSERT(func < VOGL_NUM_ENTRYPOINTS);
        VOGL_ASSERT(param < g_vogl_entrypoint_descs[func].m_num_params);

        g_vogl_entrypoint_param_descs[func][param].m_has_custom_array_size_macro = true;
    }

    for (uint32_t i = 0; i < VOGL_NUM_ENTRYPOINTS; i++)
    {
        gl_entrypoint_desc_t &desc = g_vogl_entrypoint_descs[i];
        VOGL_ASSERT(desc.m_return_namespace >= VOGL_NAMESPACE_UNKNOWN);
        VOGL_ASSERT(desc.m_return_namespace < VOGL_TOTAL_NAMESPACES);

        if (strcmp(desc.m_pName, "glGenTextures") == 0)
        {
            if (g_vogl_entrypoint_param_descs[i][1].m_namespace != VOGL_NAMESPACE_TEXTURES)
            {
                vogl_error_printf("%s: vogl_namespace_t enum is bad, please rebuild", VOGL_FUNCTION_INFO_CSTR);
                exit(EXIT_FAILURE);
            }
        }

        if (desc.m_return_ctype != VOGL_VOID)
        {
            if ((size_t)get_vogl_process_gl_ctypes()[desc.m_return_ctype].m_size < 1)
                vogl_warning_printf("%s: function %s's return ctype %s is too small\n", VOGL_FUNCTION_INFO_CSTR, desc.m_pName, get_vogl_process_gl_ctypes()[desc.m_return_ctype].m_pName);

            if ((size_t)get_vogl_process_gl_ctypes()[desc.m_return_ctype].m_size > sizeof(uint64_t))
                vogl_warning_printf("%s: function %s's return ctype %s is too large\n", VOGL_FUNCTION_INFO_CSTR, desc.m_pName, get_vogl_process_gl_ctypes()[desc.m_return_ctype].m_pName);
        }

        for (uint32_t j = 0; j < desc.m_num_params; j++)
        {
            if ((size_t)get_vogl_process_gl_ctypes()[g_vogl_entrypoint_param_descs[i][j].m_ctype].m_size < 1)
                vogl_warning_printf("%s: param %u of function %s ctype %s is too small\n", VOGL_FUNCTION_INFO_CSTR, j, desc.m_pName, get_vogl_process_gl_ctypes()[g_vogl_entrypoint_param_descs[i][j].m_ctype].m_pName);

            if ((size_t)get_vogl_process_gl_ctypes()[g_vogl_entrypoint_param_descs[i][j].m_ctype].m_size > sizeof(uint64_t))
                vogl_warning_printf("%s: param %u of function %s ctype %s is too large\n", VOGL_FUNCTION_INFO_CSTR, j, desc.m_pName, get_vogl_process_gl_ctypes()[g_vogl_entrypoint_param_descs[i][j].m_ctype].m_pName);
        }

        desc.m_pAPI_prefix = "GL";

        // FIXME: Add more prefixes as we add platforms. voglgen should probably supply this.
        // They must correspond with the GL enum prefixes in glx_enum_desc.inc, gl_enum_desc.inc, etc.
        if ((desc.m_pName[0] == 'w') && (desc.m_pName[1] == 'g') && (desc.m_pName[2] == 'l'))
            desc.m_pAPI_prefix = "WGL";
        else if ((desc.m_pName[0] == 'g') && (desc.m_pName[1] == 'l') && (desc.m_pName[2] == 'X'))
            desc.m_pAPI_prefix = "GLX";
        else if ((desc.m_pName[0] == 'g') && (desc.m_pName[1] == 'l'))
            desc.m_pAPI_prefix = "GL";
        else
        {
            vogl_warning_printf("%s: Unknown function prefix: %s\n", VOGL_FUNCTION_INFO_CSTR, desc.m_pName);
        }
    }

    for (uint32_t i = 0; i < VOGL_NUM_ENTRYPOINTS; i++)
    {
        get_vogl_entrypoint_hashmap().insert(g_vogl_entrypoint_descs[i].m_pName, static_cast<gl_entrypoint_id_t>(i));
    }
}
Beispiel #15
0
 bool file_utils::is_older_than(const char *pSrcFilename, const char *pDstFilename)
 {
     vogl_error_printf("Unimplemented\n");
     return false;
 }
Beispiel #16
0
    //----------------------------------------------------------------------------------------------------------------------
    // tool_replay_mode
    //----------------------------------------------------------------------------------------------------------------------
    static bool tool_replay_mode()
    {
        VOGL_FUNC_TRACER

        dynamic_string trace_filename(g_command_line_params().get_value_as_string_or_empty("", 1));
        if (trace_filename.is_empty())
        {
            vogl_error_printf("No trace file specified!\n");
            return false;
        }

        dynamic_string actual_trace_filename;
        vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(
            vogl_open_trace_file(
                trace_filename,
                actual_trace_filename,
                g_command_line_params().get_value_as_string_or_empty("loose_file_path").get_ptr()
            )
        );

        if (!pTrace_reader.get())
        {
            vogl_error_printf("File not found, or unable to determine file type of trace file \"%s\"\n", trace_filename.get_ptr());
            return false;
        }

        vogl_printf("Reading trace file %s\n", actual_trace_filename.get_ptr());

        vogl_gl_replayer replayer;
        vogl_replay_window window;

        #if defined(PLATFORM_WINDOWS)
            // We need to get proc addresses for windows late.
            replayer.set_proc_address_helper(vogl_get_proc_address_helper, false);
        #endif

        uint replayer_flags = get_replayer_flags_from_command_line_params();

        // TODO: This will create a window with default attributes, which seems fine for the majority of traces.
        // Unfortunately, some GL call streams *don't* want an alpha channel, or depth, or stencil etc. in the default framebuffer so this may become a problem.
        // Also, this design only supports a single window, which is going to be a problem with multiple window traces.
        if (!window.open(g_command_line_params().get_value_as_int("width", 0, 1024, 1, 65535), g_command_line_params().get_value_as_int("height", 0, 768, 1, 65535), g_command_line_params().get_value_as_int("msaa", 0, 0, 0, 65535)))
        {
            vogl_error_printf("Failed initializing replay window\n");
            return false;
        }

        if (!replayer.init(replayer_flags, &window, pTrace_reader->get_sof_packet(), pTrace_reader->get_multi_blob_manager()))
        {
            vogl_error_printf("Failed initializing GL replayer\n");
            return false;
        }

        // Disable all glGetError() calls in vogl_utils.cpp.
        vogl_disable_gl_get_error();

        // Bool win_mapped = false;

        vogl_gl_state_snapshot *pSnapshot = NULL;
        int64_t snapshot_loop_start_frame = -1;
        int64_t snapshot_loop_end_frame = -1;

        vogl::hash_map<SDL_Keycode> keys_pressed, keys_down;

        int loop_frame = g_command_line_params().get_value_as_int("loop_frame", 0, -1);
        int loop_len = math::maximum<int>(g_command_line_params().get_value_as_int("loop_len", 0, 1), 1);
        int loop_count = math::maximum<int>(g_command_line_params().get_value_as_int("loop_count", 0, cINT32_MAX), 1);
        bool endless_mode = g_command_line_params().get_value_as_bool("endless");

        timer tm;
        tm.start();

        for (;;)
        {
            tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Main Loop");

            SDL_Event wnd_event;
            
            while (SDL_PollEvent(&wnd_event))
            {
                switch (wnd_event.type)
                {
                    case SDL_KEYDOWN:
                    {
                        keys_down.insert(wnd_event.key.keysym.sym);
                        keys_pressed.insert(wnd_event.key.keysym.sym);
                        break;
                    }

                    case SDL_KEYUP:
                    {
                        keys_down.erase(wnd_event.key.keysym.sym);

                        break;
                    }

                    case SDL_WINDOWEVENT:
                    {
                        switch(wnd_event.window.event)
                        {
                            case SDL_WINDOWEVENT_FOCUS_GAINED:
                            case SDL_WINDOWEVENT_FOCUS_LOST:
                                keys_down.reset();
                                break;

                            case SDL_WINDOWEVENT_CLOSE:
                                vogl_message_printf("Window told to close, exiting.\n");
                                goto normal_exit;
                                break;
                            default:
                                break;
                        };
                        break;
                    }

                    default:
                        // TODO: Handle these somehow?
                        break;
                };
            }

            if (replayer.get_at_frame_boundary())
            {
                if ((!pSnapshot) && (loop_frame != -1) && (static_cast<int64_t>(replayer.get_frame_index()) == loop_frame))
                {
                    vogl_debug_printf("Capturing replayer state at start of frame %u\n", replayer.get_frame_index());

                    pSnapshot = replayer.snapshot_state();

                    if (pSnapshot)
                    {
                        vogl_printf("Snapshot succeeded\n");

                        snapshot_loop_start_frame = pTrace_reader->get_cur_frame();
                        snapshot_loop_end_frame = pTrace_reader->get_cur_frame() + loop_len;

                        vogl_debug_printf("Loop start: %" PRIi64 " Loop end: %" PRIi64 "\n", snapshot_loop_start_frame, snapshot_loop_end_frame);
                    }
                    else
                    {
                        vogl_error_printf("Snapshot failed!\n");
                        loop_frame = -1;
                    }
                }
            }

            vogl_gl_replayer::status_t status = replayer.process_pending_window_resize();
            if (status == vogl_gl_replayer::cStatusOK)
            {
                for (;;)
                {
                    status = replayer.process_pending_packets();

                    if (status == vogl_gl_replayer::cStatusOK)
                    {
                        status = replayer.process_next_packet(*pTrace_reader);
                    }

                    if ((status == vogl_gl_replayer::cStatusNextFrame) ||
                        (status == vogl_gl_replayer::cStatusResizeWindow) ||
                        (status == vogl_gl_replayer::cStatusAtEOF) ||
                        (status == vogl_gl_replayer::cStatusHardFailure))
                    {
                        break;
                    }
                }
            }

            if (status == vogl_gl_replayer::cStatusHardFailure)
                break;

            if (status == vogl_gl_replayer::cStatusAtEOF)
            {
                vogl_message_printf("At trace EOF, frame index %u\n", replayer.get_frame_index());
            }

            if (replayer.get_at_frame_boundary() &&
                pSnapshot && 
                (loop_count > 0) &&
                ((pTrace_reader->get_cur_frame() == snapshot_loop_end_frame) || (status == vogl_gl_replayer::cStatusAtEOF)))
            {
                status = replayer.begin_applying_snapshot(pSnapshot, false);
                if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
                    goto error_exit;

                pTrace_reader->seek_to_frame(static_cast<uint>(snapshot_loop_start_frame));

                vogl_debug_printf("Applying snapshot and seeking back to frame %" PRIi64 "\n", snapshot_loop_start_frame);
                loop_count--;
            }
            else
            {
                bool print_progress = (status == vogl_gl_replayer::cStatusAtEOF) ||
                    ((replayer.get_at_frame_boundary()) && ((replayer.get_frame_index() % 100) == 0));
                if (print_progress)
                {
                    if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER)
                    {
                        vogl_binary_trace_file_reader &binary_trace_reader = *static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get());

                        vogl_printf("Replay now at frame index %u, trace file offet %" PRIu64 ", GL call counter %" PRIu64 ", %3.2f%% percent complete\n",
                            replayer.get_frame_index(),
                            binary_trace_reader.get_cur_file_ofs(),
                            replayer.get_last_parsed_call_counter(),
                            binary_trace_reader.get_trace_file_size() ? (binary_trace_reader.get_cur_file_ofs() * 100.0f) / binary_trace_reader.get_trace_file_size() : 0);
                    }
                }

                if (status == vogl_gl_replayer::cStatusAtEOF)
                {
                    if (!endless_mode)
                    {
                        double time_since_start = tm.get_elapsed_secs();
                        if (replayer.get_flags() & cGLReplayerFSPreprocessor)
                        {
                            double fs_pp_time = replayer.get_fs_pp_time();
                            vogl_printf("Ran with FS Preprocessor (FSPP) enabled:\n");
                            vogl_printf("FSPP Time: %.3f secs\n", fs_pp_time);
                            vogl_printf("Overall Stats:             %u total swaps, %.3f secs, %3.3f avg fps\n", replayer.get_total_swaps(), time_since_start, replayer.get_frame_index() / time_since_start);
                            vogl_printf("Stats Excluding FSPP time: %u total swaps, %.3f secs, %3.3f avg fps\n", replayer.get_total_swaps(), time_since_start-fs_pp_time, replayer.get_frame_index() / (time_since_start-fs_pp_time));
                            break;
                        }
                        else
                        {
                            vogl_printf("%u total swaps, %.3f secs, %3.3f avg fps\n", replayer.get_total_swaps(), time_since_start, replayer.get_frame_index() / time_since_start);
                            break;
                        }
                    }
                    vogl_printf("Resetting state and rewinding back to frame 0\n");

                    replayer.reset_state();

                    if (!pTrace_reader->seek_to_frame(0))
                    {
                        vogl_error_printf("Failed rewinding trace reader!\n");
                        goto error_exit;
                    }
                }
            }

            telemetry_tick();
        }

    normal_exit:
        return true;

    error_exit:
        return false;
    }
bool vogl_default_framebuffer_state::snapshot(const vogl_context_info &context_info, const vogl_default_framebuffer_attribs &fb_attribs)
{
    VOGL_NOTE_UNUSED(context_info);

    clear();

    m_fb_attribs = fb_attribs;

    // Create compatible GL texture
    // Attach this texture to an FBO
    // Blit default framebuffer to this FBO
    // Capture this texture's state

    vogl_scoped_state_saver framebuffer_state_saver(cGSTReadBuffer, cGSTDrawBuffer);

    vogl_scoped_binding_state orig_framebuffers(GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER, GL_TEXTURE_2D, GL_TEXTURE_2D_MULTISAMPLE);

    GL_ENTRYPOINT(glBindFramebuffer)(GL_READ_FRAMEBUFFER, 0);
    VOGL_CHECK_GL_ERROR;

    vogl_scoped_binding_state orig_bindings(GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER);

    GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_PACK_BUFFER, 0);
    VOGL_CHECK_GL_ERROR;

    GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_UNPACK_BUFFER, 0);
    VOGL_CHECK_GL_ERROR;

    vogl_scoped_state_saver pixelstore_state_saver(cGSTPixelStore);

    vogl_scoped_state_saver pixeltransfer_state_saver;
    if (!context_info.is_core_profile())
        pixeltransfer_state_saver.save(cGSTPixelTransfer);

    vogl_reset_pixel_store_states();
    if (!context_info.is_core_profile())
        vogl_reset_pixel_transfer_states();

    // TODO: Test multisampled default framebuffers
    const GLenum tex_target = (fb_attribs.m_samples > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;

    for (uint i = 0; i < cDefFramebufferTotal; i++)
    {
        GLenum internal_fmt, pixel_fmt, pixel_type;

        // TODO: This uses fixed pixel formats, and assumes there's always a depth/stencil buffer.
        if (i == cDefFramebufferDepthStencil)
        {
            if ((fb_attribs.m_depth_size + fb_attribs.m_stencil_size) == 0)
                continue;

            GL_ENTRYPOINT(glReadBuffer)(fb_attribs.m_double_buffered ? GL_BACK_LEFT : GL_FRONT_LEFT);

            internal_fmt = GL_DEPTH_STENCIL;
            pixel_fmt = GL_DEPTH_STENCIL;
            pixel_type = GL_UNSIGNED_INT_24_8;
        }
        else
        {
            if ((fb_attribs.m_r_size + fb_attribs.m_g_size + fb_attribs.m_b_size + fb_attribs.m_a_size) == 0)
                continue;

            GL_ENTRYPOINT(glReadBuffer)(g_def_framebuffer_enums[i]);

            internal_fmt = GL_RGBA;
            pixel_fmt = GL_RGBA;
            pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
        }

        if (vogl_check_gl_error_internal(true))
            continue;

        // Create texture
        GLuint tex_handle = 0;
        GL_ENTRYPOINT(glGenTextures)(1, &tex_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glBindTexture)(tex_target, tex_handle);
        VOGL_CHECK_GL_ERROR;

        if (fb_attribs.m_samples > 1)
        {
            GL_ENTRYPOINT(glTexImage2DMultisample)(tex_target,
                fb_attribs.m_samples,
                internal_fmt,
                fb_attribs.m_width,
                fb_attribs.m_height,
                GL_TRUE);
        }
        else
        {
            GL_ENTRYPOINT(glTexImage2D)(tex_target,
                0,
                internal_fmt,
                fb_attribs.m_width,
                fb_attribs.m_height,
                0,
                pixel_fmt,
                pixel_type,
                NULL);
        }

        if (vogl_check_gl_error_internal())
        {
            GL_ENTRYPOINT(glBindTexture)(tex_target, 0);
            VOGL_CHECK_GL_ERROR;

            GL_ENTRYPOINT(glDeleteTextures)(1, &tex_handle);
            VOGL_CHECK_GL_ERROR;

            continue;
        }

        GL_ENTRYPOINT(glTexParameteri)(tex_target, GL_TEXTURE_MAX_LEVEL, 0);
        VOGL_CHECK_GL_ERROR;

        // Create FBO
        GLuint fbo_handle = 0;
        GL_ENTRYPOINT(glGenFramebuffers)(1, &fbo_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glBindFramebuffer)(GL_DRAW_FRAMEBUFFER, fbo_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glFramebufferTexture2D)(GL_DRAW_FRAMEBUFFER, (i == cDefFramebufferDepthStencil) ? GL_DEPTH_STENCIL_ATTACHMENT : GL_COLOR_ATTACHMENT0, tex_target, tex_handle, 0);
        VOGL_CHECK_GL_ERROR;

        GLenum draw_buf = (i == cDefFramebufferDepthStencil) ? GL_NONE : GL_COLOR_ATTACHMENT0;
        GL_ENTRYPOINT(glDrawBuffers)(1, &draw_buf);
        VOGL_CHECK_GL_ERROR;

        GLenum cur_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_DRAW_FRAMEBUFFER);
        VOGL_CHECK_GL_ERROR;

        bool status = true;

        if (cur_status == GL_FRAMEBUFFER_COMPLETE)
        {
            GL_ENTRYPOINT(glBlitFramebuffer)(
                0, 0, fb_attribs.m_width, fb_attribs.m_height,
                0, 0, fb_attribs.m_width, fb_attribs.m_height,
                (i == cDefFramebufferDepthStencil) ? (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) : GL_COLOR_BUFFER_BIT,
                GL_NEAREST);

            if (vogl_check_gl_error_internal())
            {
                status = false;
            }
        }

        if (status)
        {
            vogl_handle_remapper def_handle_remapper;
            status = m_textures[i].snapshot(context_info, def_handle_remapper, tex_handle, tex_target);

            if (!status)
            {
                vogl_error_printf("%s: Failed snapshotting texture for default framebuffer %s\n", VOGL_METHOD_NAME, g_gl_enums.find_gl_name(g_def_framebuffer_enums[i]));
            }
        }
        else
        {
            vogl_warning_printf("%s: Failed blitting framebuffer %u\n", VOGL_METHOD_NAME, i);
        }

        // Delete FBO
        GL_ENTRYPOINT(glBindFramebuffer)(GL_DRAW_FRAMEBUFFER, 0);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glDeleteFramebuffers)(1, &fbo_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glDrawBuffer)(GL_FRONT_LEFT);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glBindTexture)(tex_target, 0);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glDeleteTextures)(1, &tex_handle);
        VOGL_CHECK_GL_ERROR;
    }

    m_valid = true;

    return true;
}