bool vogl_binary_trace_file_reader::read_frame_file_offsets() { VOGL_FUNC_TRACER m_frame_file_offsets.clear(); m_max_frame_index = -1; m_found_frame_file_offsets_packet = false; uint8_vec frame_offsets_data; if (!m_archive_blob_manager.is_initialized() || !m_archive_blob_manager.get(VOGL_TRACE_ARCHIVE_FRAME_FILE_OFFSETS_FILENAME, frame_offsets_data)) { vogl_debug_printf("%s: Couldn't find trace frame file offset file in trace archive, seeking will be slow in this trace file\n", VOGL_FUNCTION_INFO_CSTR); return false; } if (frame_offsets_data.size() & (sizeof(uint64_t) - 1)) { vogl_error_printf("%s: Trace frame file offset file in trace archive is invalid, seeking will be slow in this trace file\n", VOGL_FUNCTION_INFO_CSTR); return false; } uint32_t total_offsets = frame_offsets_data.size() / sizeof(uint64_t); m_frame_file_offsets.resize(total_offsets); memcpy(m_frame_file_offsets.get_ptr(), frame_offsets_data.get_ptr(), m_frame_file_offsets.size_in_bytes()); vogl_debug_printf("%s: Frame file offsets packet is OK, found %u total frame offsets\n", VOGL_FUNCTION_INFO_CSTR, m_frame_file_offsets.size()); m_max_frame_index = m_frame_file_offsets.size() - 1; m_found_frame_file_offsets_packet = true; return true; }
bool file_utils::create_directory(const char *pPath) { #if defined(PLATFORM_WINDOWS) int status = _mkdir(pPath); #else int status = mkdir(pPath, S_IRWXU | S_IRWXG | S_IRWXO); #endif if (status == 0) vogl_debug_printf("Created directory %s\n", pPath); return !status; }
bool vogl_replay_window::open(int width, int height, int samples) { VOGL_FUNC_TRACER close(); const char *pWindow_name = (sizeof(void *) == sizeof(uint32_t)) ? "voglreplay 32-bit" : "voglreplay 64-bit"; #if (VOGL_PLATFORM_HAS_SDL) SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); if (samples > 1) { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samples); } m_win = SDL_CreateWindow(pWindow_name, 20, 20, width, height, SDL_WINDOW_OPENGL); // Don't actually create the context here, that will be done later. // TODO: Support better window creation modes, like with a particular backbuffer format. #else #error "Need vogl_replay_window::open for this platform" return false; #endif m_width = width; m_height = height; uint32_t actual_width = 0, actual_height = 0; vogl_replay_window::get_actual_dimensions(actual_width, actual_height); vogl_debug_printf("Created window, requested dimensions %ux%u, actual dimensions %ux%u\n", m_width, m_height, actual_width, actual_height); return true; }
bool vogl_trace_file_writer::close() { VOGL_FUNC_TRACER vogl_debug_printf("%s\n", VOGL_METHOD_NAME); 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_METHOD_NAME, 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_METHOD_NAME, 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_NAME, 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_NAME, 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_METHOD_NAME, 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_METHOD_NAME, 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_METHOD_NAME, 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_METHOD_NAME, full_trace_filename.get_ptr()); return success; }
bool vogl_replay_window::open(int width, int height, int samples) { VOGL_FUNC_TRACER #if (VOGL_PLATFORM_HAS_GLX) close(); if (!check_glx_version()) return false; // TODO: These attribs (especially the sizes) should be passed in by the caller! int fbAttribs[64]; int *pAttribs = fbAttribs; *pAttribs++ = GLX_RENDER_TYPE; *pAttribs++ = GLX_RGBA_BIT; *pAttribs++ = GLX_X_RENDERABLE; *pAttribs++ = True; *pAttribs++ = GLX_DRAWABLE_TYPE; *pAttribs++ = GLX_WINDOW_BIT; *pAttribs++ = GLX_DOUBLEBUFFER; *pAttribs++ = True; *pAttribs++ = GLX_RED_SIZE; *pAttribs++ = 8; *pAttribs++ = GLX_BLUE_SIZE; *pAttribs++ = 8; *pAttribs++ = GLX_GREEN_SIZE; *pAttribs++ = 8; *pAttribs++ = GLX_ALPHA_SIZE; *pAttribs++ = 8; *pAttribs++ = GLX_DEPTH_SIZE; *pAttribs++ = 24; *pAttribs++ = GLX_STENCIL_SIZE; *pAttribs++ = 8; if (samples > 1) { *pAttribs++ = GLX_SAMPLE_BUFFERS; *pAttribs++ = 1; *pAttribs++ = GLX_SAMPLES; *pAttribs++ = samples; } *pAttribs++ = 0; // Tell X we are going to use the display m_dpy = XOpenDisplay(NULL); if (!m_dpy) { console::error("%s: XOpenDisplay() failed!\n", VOGL_FUNCTION_INFO_CSTR); return false; } // Get a new fb config that meets our attrib requirements m_pFB_configs = GL_ENTRYPOINT(glXChooseFBConfig)(m_dpy, DefaultScreen(m_dpy), fbAttribs, &m_num_fb_configs); if ((!m_pFB_configs) || (!m_num_fb_configs)) { console::error("%s: glXChooseFBConfig() failed!\n", VOGL_FUNCTION_INFO_CSTR); return false; } XVisualInfo *pVisual_info = GL_ENTRYPOINT(glXGetVisualFromFBConfig)(m_dpy, m_pFB_configs[0]); if (!pVisual_info) { console::error("%s: glXGetVisualFromFBConfig() failed!\n", VOGL_FUNCTION_INFO_CSTR); return false; } // Now create an X window XSetWindowAttributes winAttribs; winAttribs.event_mask = ExposureMask | VisibilityChangeMask | KeyPressMask | PointerMotionMask | StructureNotifyMask; winAttribs.border_pixel = 0; winAttribs.bit_gravity = StaticGravity; winAttribs.colormap = XCreateColormap(m_dpy, RootWindow(m_dpy, pVisual_info->screen), pVisual_info->visual, AllocNone); GLint winmask = CWBorderPixel | CWBitGravity | CWEventMask | CWColormap; m_win = XCreateWindow(m_dpy, DefaultRootWindow(m_dpy), 20, 20, width, height, 0, pVisual_info->depth, InputOutput, pVisual_info->visual, winmask, &winAttribs); const char *pWindow_name = (sizeof(void *) == sizeof(uint32)) ? "voglreplay 32-bit" : "voglreplay 64-bit"; XStoreName(m_dpy, m_win, pWindow_name); XSetIconName(m_dpy, m_win, pWindow_name); XSizeHints sh; utils::zero_object(sh); sh.x = 0; // slam position up so when/if we resize the window glReadPixels still works as expected (this may be a bug in the NV driver, I dunno yet) sh.y = 0; sh.width = sh.min_width = sh.max_width = sh.base_width = width; sh.height = sh.min_height = sh.max_height = sh.base_height = height; sh.flags = PSize | PMinSize | PMaxSize | PBaseSize | PPosition; XSetWMNormalHints(m_dpy, m_win, &sh); XResizeWindow(m_dpy, m_win, width, height); XMapWindow(m_dpy, m_win); //glXWaitX(); m_width = width; m_height = height; uint actual_width = 0, actual_height = 0; vogl_replay_window::get_actual_dimensions(actual_width, actual_height); vogl_debug_printf("%s: Created window, requested dimensions %ux%u, actual dimensions %ux%u\n", VOGL_FUNCTION_INFO_CSTR, m_width, m_height, actual_width, actual_height); return true; #else VOGL_ASSERT(!"impl"); return false; #endif }
//---------------------------------------------------------------------------------------------------------------------- // 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(); 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; }
//---------------------------------------------------------------------------------------------------------------------- // 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; }