]> git.cworth.org Git - vogl/blob - src/voglbench/voglbench.cpp
Initial vogl checkin
[vogl] / src / voglbench / voglbench.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 // File: voglbench.cpp
27 #include "vogl_common.h"
28 #include "vogl_gl_replayer.h"
29 #include "vogl_texture_format.h"
30 #include "vogl_trace_file_writer.h"
31
32 #include "vogl_colorized_console.h"
33 #include "vogl_command_line_params.h"
34 #include "vogl_cfile_stream.h"
35 #include "vogl_value.h"
36 #include "vogl_dynamic_stream.h"
37 #include "vogl_file_utils.h"
38 #include "vogl_mergesort.h"
39 #include "vogl_unique_ptr.h"
40 #include "vogl_find_files.h"
41 #include "vogl_bigint128.h"
42 #include "vogl_regex.h"
43
44 #include <sys/types.h>
45 #include <sys/stat.h>
46
47 #include "vogl_json.h"
48 #include "vogl_blob_manager.h"
49
50 #include "libtelemetry.h"
51
52 #include <X11/Xlib.h>
53 #include <X11/Xutil.h>
54 #include <X11/Xmd.h>
55
56 //$ TODO: investigate using SDL for windows and any keyboard controls.
57 //$ Run clang-format on everything.
58 //$ Use Telemetry to speed this bugger up.
59
60 //----------------------------------------------------------------------------------------------------------------------
61 // globals
62 //----------------------------------------------------------------------------------------------------------------------
63 static void *g_actual_libgl_module_handle;
64 static cfile_stream *g_vogl_pLog_stream;
65
66 //----------------------------------------------------------------------------------------------------------------------
67 // command line params
68 //----------------------------------------------------------------------------------------------------------------------
69 static command_line_param_desc g_command_line_param_descs[] =
70     {
71         { "width", 1, false, "Replay: Set initial window width (default is 1024)" },
72         { "height", 1, false, "Replay: Set initial window height (default is 768)" },
73         { "lock_window_dimensions", 0, false, "Replay: Don't automatically change window's dimensions during replay" },
74         { "endless", 0, false, "Replay: Loop replay endlessly instead of exiting" },
75         { "force_debug_context", 0, false, "Replay: Force GL debug contexts" },
76 #ifdef USE_TELEMETRY
77         { "telemetry_level", 1, false, "Set Telemetry level." },
78 #endif
79         { "loop_frame", 1, false, "Replay: loop mode's start frame" },
80         { "loop_len", 1, false, "Replay: loop mode's loop length" },
81         { "loop_count", 1, false, "Replay: loop mode's loop count" },
82         { "logfile", 1, false, "Create logfile" },
83         { "logfile_append", 1, false, "Append output to logfile" },
84         { "help", 0, false, "Display this help" },
85         { "?", 0, false, "Display this help" },
86         { "pause", 0, false, "Wait for a key at startup (so a debugger can be attached)" },
87         { "verbose", 0, false, "Verbose debug output" },
88         { "quiet", 0, false, "Disable all console output" },
89     };
90
91 //----------------------------------------------------------------------------------------------------------------------
92 // init_logfile
93 //----------------------------------------------------------------------------------------------------------------------
94 static bool init_logfile()
95 {
96     VOGL_FUNC_TRACER
97
98     dynamic_string log_file(g_command_line_params.get_value_as_string_or_empty("logfile"));
99     dynamic_string log_file_append(g_command_line_params.get_value_as_string_or_empty("logfile_append"));
100     if (log_file.is_empty() && log_file_append.is_empty())
101         return true;
102
103     dynamic_string filename(log_file_append.is_empty() ? log_file : log_file_append);
104
105     // This purposely leaks, don't care
106     g_vogl_pLog_stream = vogl_new(cfile_stream);
107
108     if (!g_vogl_pLog_stream->open(filename.get_ptr(), cDataStreamWritable, !log_file_append.is_empty()))
109     {
110         vogl_error_printf("%s: Failed opening log file \"%s\"\n", VOGL_FUNCTION_NAME, filename.get_ptr());
111         return false;
112     }
113     else
114     {
115         vogl_message_printf("Opened log file \"%s\"\n", filename.get_ptr());
116         console::set_log_stream(g_vogl_pLog_stream);
117     }
118
119     return true;
120 }
121
122 //----------------------------------------------------------------------------------------------------------------------
123 // tool_print_title
124 //----------------------------------------------------------------------------------------------------------------------
125 static void tool_print_title()
126 {
127     VOGL_FUNC_TRACER
128
129     vogl_printf("voglbench ");
130     if (sizeof(void *) > 4)
131         vogl_printf("64-bit ");
132     else
133         vogl_printf("32-bit ");
134 #ifdef VOGL_BUILD_DEBUG
135     vogl_printf("Debug ");
136 #else
137     vogl_printf("Release ");
138 #endif
139     vogl_printf("Built %s %s\n", __DATE__, __TIME__);
140 }
141
142 //----------------------------------------------------------------------------------------------------------------------
143 // tool_print_help
144 //----------------------------------------------------------------------------------------------------------------------
145 static void tool_print_help()
146 {
147     VOGL_FUNC_TRACER
148
149     vogl_printf("Usage: voglbench [ -option ... ] input_file optional_output_file [ -option ... ]\n");
150     vogl_printf("Command line options may begin with single minus \"-\" or double minus \"--\"\n");
151
152     vogl_printf("\nCommand line options:\n");
153
154     dump_command_line_info(VOGL_ARRAY_SIZE(g_command_line_param_descs), g_command_line_param_descs, "--");
155 }
156
157 //----------------------------------------------------------------------------------------------------------------------
158 // init_command_line_params
159 //----------------------------------------------------------------------------------------------------------------------
160 static bool init_command_line_params(int argc, char *argv[])
161 {
162     VOGL_FUNC_TRACER
163
164     command_line_params::parse_config parse_cfg;
165     parse_cfg.m_single_minus_params = true;
166     parse_cfg.m_double_minus_params = true;
167
168     if (!g_command_line_params.parse(get_command_line_params(argc, argv), VOGL_ARRAY_SIZE(g_command_line_param_descs), g_command_line_param_descs, parse_cfg))
169     {
170         vogl_error_printf("%s: Failed parsing command line parameters!\n", VOGL_FUNCTION_NAME);
171         return false;
172     }
173
174     if (!init_logfile())
175         return false;
176
177     if (g_command_line_params.get_value_as_bool("help") || g_command_line_params.get_value_as_bool("?"))
178     {
179         tool_print_help();
180         return false;
181     }
182
183     return true;
184 }
185
186 //----------------------------------------------------------------------------------------------------------------------
187 // load_gl
188 //----------------------------------------------------------------------------------------------------------------------
189 static bool load_gl()
190 {
191     VOGL_FUNC_TRACER
192
193     g_actual_libgl_module_handle = dlopen("libGL.so.1", RTLD_LAZY);
194     if (!g_actual_libgl_module_handle)
195     {
196         vogl_error_printf("%s: Failed loading libGL.so.1!\n", VOGL_FUNCTION_NAME);
197         return false;
198     }
199
200     GL_ENTRYPOINT(glXGetProcAddress) = reinterpret_cast<glXGetProcAddress_func_ptr_t>(dlsym(g_actual_libgl_module_handle, "glXGetProcAddress"));
201     if (!GL_ENTRYPOINT(glXGetProcAddress))
202     {
203         vogl_error_printf("%s: Failed getting address of glXGetProcAddress() from libGL.so.1!\n", VOGL_FUNCTION_NAME);
204         return false;
205     }
206
207     return true;
208 }
209
210 //----------------------------------------------------------------------------------------------------------------------
211 // vogl_get_proc_address_helper
212 //----------------------------------------------------------------------------------------------------------------------
213 static vogl_void_func_ptr_t vogl_get_proc_address_helper(const char *pName)
214 {
215     VOGL_FUNC_TRACER
216
217     vogl_void_func_ptr_t pFunc = g_actual_libgl_module_handle ? reinterpret_cast<vogl_void_func_ptr_t>(dlsym(g_actual_libgl_module_handle, pName)) : NULL;
218
219     if ((!pFunc) && (GL_ENTRYPOINT(glXGetProcAddress)))
220         pFunc = reinterpret_cast<vogl_void_func_ptr_t>(GL_ENTRYPOINT(glXGetProcAddress)(reinterpret_cast<const GLubyte *>(pName)));
221
222     return pFunc;
223 }
224
225 //----------------------------------------------------------------------------------------------------------------------
226 // vogl_direct_gl_func_prolog - This function is called before EVERY single GL/GLX function call we make.
227 //----------------------------------------------------------------------------------------------------------------------
228 static void vogl_direct_gl_func_prolog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **ppStack_data)
229 {
230     VOGL_NOTE_UNUSED(entrypoint_id);
231     VOGL_NOTE_UNUSED(pUser_data);
232     VOGL_NOTE_UNUSED(ppStack_data);
233
234     vogl_printf("* GLPROLOG %s\n", g_vogl_entrypoint_descs[entrypoint_id].m_pName);
235 }
236
237 //----------------------------------------------------------------------------------------------------------------------
238 // vogl_direct_gl_func_epilog - This function is called immediately after EVERY single GL/GLX function call we make.
239 //----------------------------------------------------------------------------------------------------------------------
240 static void vogl_direct_gl_func_epilog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **ppStack_data)
241 {
242     VOGL_NOTE_UNUSED(entrypoint_id);
243     VOGL_NOTE_UNUSED(pUser_data);
244     VOGL_NOTE_UNUSED(ppStack_data);
245
246     vogl_printf("* GLEPILOG %s\n", g_vogl_entrypoint_descs[entrypoint_id].m_pName);
247 }
248
249 //----------------------------------------------------------------------------------------------------------------------
250 // voglbench_init
251 //----------------------------------------------------------------------------------------------------------------------
252 static bool voglbench_init(int argc, char *argv[])
253 {
254     VOGL_FUNC_TRACER
255
256     g_thread_safe_random.seed_from_urandom();
257
258     colorized_console::init();
259     colorized_console::set_exception_callback();
260     //console::set_tool_prefix("(voglbench) ");
261
262     tool_print_title();
263
264     if (!init_command_line_params(argc, argv))
265         return false;
266
267 #ifdef USE_TELEMETRY
268     int telemetry_level = g_command_line_params.get_value_as_int("telemetry_level", 0,
269                                                                  TELEMETRY_LEVEL_MIN + 1, TELEMETRY_LEVEL_MIN, TELEMETRY_LEVEL_MAX);
270     telemetry_set_level(telemetry_level);
271     telemetry_tick();
272 #endif
273
274     vogl_common_lib_early_init();
275     vogl_common_lib_global_init();
276
277     if (g_command_line_params.get_value_as_bool("quiet"))
278         console::disable_output();
279
280     if (g_command_line_params.get_value_as_bool("gl_debug_log"))
281     {
282         vogl_set_direct_gl_func_prolog(vogl_direct_gl_func_prolog, NULL);
283         vogl_set_direct_gl_func_epilog(vogl_direct_gl_func_epilog, NULL);
284     }
285
286     if (!load_gl())
287         return false;
288
289     bool wrap_all_gl_calls = false;
290     vogl_init_actual_gl_entrypoints(vogl_get_proc_address_helper, wrap_all_gl_calls);
291     return true;
292 }
293
294 //----------------------------------------------------------------------------------------------------------------------
295 // voglbench_deinit
296 //----------------------------------------------------------------------------------------------------------------------
297 static void voglbench_deinit()
298 {
299     VOGL_FUNC_TRACER
300
301     colorized_console::deinit();
302 }
303
304 //----------------------------------------------------------------------------------------------------------------------
305 // X11_Pending - from SDL
306 //----------------------------------------------------------------------------------------------------------------------
307 static int X11_Pending(Display *display)
308 {
309     VOGL_FUNC_TRACER
310
311     /* Flush the display connection and look to see if events are queued */
312     XFlush(display);
313     if (XEventsQueued(display, QueuedAlready))
314     {
315         return (1);
316     }
317
318     /* More drastic measures are required -- see if X is ready to talk */
319     {
320         static struct timeval zero_time; /* static == 0 */
321         int x11_fd;
322         fd_set fdset;
323
324         x11_fd = ConnectionNumber(display);
325         FD_ZERO(&fdset);
326         FD_SET(x11_fd, &fdset);
327         if (select(x11_fd + 1, &fdset, NULL, NULL, &zero_time) == 1)
328         {
329             return (XPending(display));
330         }
331     }
332
333     /* Oh well, nothing is ready .. */
334     return (0);
335 }
336
337 //----------------------------------------------------------------------------------------------------------------------
338 // get_replayer_flags_from_command_line_params
339 //----------------------------------------------------------------------------------------------------------------------
340 static uint get_replayer_flags_from_command_line_params()
341 {
342     uint replayer_flags = cGLReplayerBenchmarkMode;
343
344     static struct
345     {
346         const char *m_pCommand;
347         uint m_flag;
348     } s_replayer_command_line_params[] =
349     {
350         { "verbose", cGLReplayerVerboseMode },
351         { "force_debug_context", cGLReplayerForceDebugContexts },
352         { "debug", cGLReplayerDebugMode },
353         { "lock_window_dimensions", cGLReplayerLockWindowDimensions },
354     };
355
356     for (uint i = 0; i < sizeof(s_replayer_command_line_params) / sizeof(s_replayer_command_line_params[0]); i++)
357     {
358         if (g_command_line_params.get_value_as_bool(s_replayer_command_line_params[i].m_pCommand))
359             replayer_flags |= s_replayer_command_line_params[i].m_flag;
360     }
361
362     return replayer_flags;
363 }
364
365 //----------------------------------------------------------------------------------------------------------------------
366 // tool_replay_mode
367 //----------------------------------------------------------------------------------------------------------------------
368 static bool tool_replay_mode()
369 {
370     VOGL_FUNC_TRACER
371
372     dynamic_string trace_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
373     if (trace_filename.is_empty())
374     {
375         vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_NAME);
376         return false;
377     }
378
379     dynamic_string actual_trace_filename;
380     vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(
381                 trace_filename,
382                 actual_trace_filename,
383                 g_command_line_params.get_value_as_string_or_empty("loose_file_path").get_ptr()));
384     if (!pTrace_reader.get())
385     {
386         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());
387         return false;
388     }
389
390     vogl_printf("Reading trace file %s\n", actual_trace_filename.get_ptr());
391
392     vogl_gl_replayer replayer;
393     vogl_replay_window window;
394
395     uint replayer_flags = get_replayer_flags_from_command_line_params();
396
397     // TODO: This will create a window with default attributes, which seems fine for the majority of traces.
398     // 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.
399     // Also, this design only supports a single window, which is going to be a problem with multiple window traces.
400     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)))
401     {
402         vogl_error_printf("%s: Failed initializing replay window\n", VOGL_FUNCTION_NAME);
403         return false;
404     }
405
406     if (!replayer.init(replayer_flags, &window, pTrace_reader->get_sof_packet(), pTrace_reader->get_multi_blob_manager()))
407     {
408         vogl_error_printf("%s: Failed initializing GL replayer\n", VOGL_FUNCTION_NAME);
409         return false;
410     }
411
412     // Disable all glGetError() calls in vogl_utils.cpp.
413     vogl_disable_gl_get_error();
414
415     XSelectInput(window.get_display(), window.get_xwindow(),
416                  EnterWindowMask | LeaveWindowMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | StructureNotifyMask | KeymapStateMask);
417
418     Atom wmDeleteMessage = XInternAtom(window.get_display(), "WM_DELETE_WINDOW", False);
419     XSetWMProtocols(window.get_display(), window.get_xwindow(), &wmDeleteMessage, 1);
420
421     Bool win_mapped = false;
422
423     vogl_gl_state_snapshot *pSnapshot = NULL;
424     int64_t snapshot_loop_start_frame = -1;
425     int64_t snapshot_loop_end_frame = -1;
426
427     vogl::hash_map<uint64_t> keys_pressed, keys_down;
428
429     int loop_frame = g_command_line_params.get_value_as_int("loop_frame", 0, -1);
430     int loop_len = math::maximum<int>(g_command_line_params.get_value_as_int("loop_len", 0, 1), 1);
431     int loop_count = math::maximum<int>(g_command_line_params.get_value_as_int("loop_count", 0, cINT32_MAX), 1);
432     bool endless_mode = g_command_line_params.get_value_as_bool("endless");
433
434     timer tm;
435     tm.start();
436
437     for (;;)
438     {
439         tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Main Loop");
440
441         while (X11_Pending(window.get_display()))
442         {
443             XEvent newEvent;
444
445             // Watch for new X eventsn
446             XNextEvent(window.get_display(), &newEvent);
447
448             switch (newEvent.type)
449             {
450                 case KeyPress:
451                 {
452                     KeySym xsym = XLookupKeysym(&newEvent.xkey, 0);
453
454                     //printf("KeyPress 0%04llX %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym);
455
456                     keys_down.insert(xsym);
457                     keys_pressed.insert(xsym);
458
459                     break;
460                 }
461                 case KeyRelease:
462                 {
463                     KeySym xsym = XLookupKeysym(&newEvent.xkey, 0);
464
465                     //printf("KeyRelease 0x%04llX %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym);
466
467                     keys_down.erase(xsym);
468
469                     break;
470                 }
471                 case FocusIn:
472                 case FocusOut:
473                 {
474                     //printf("FocusIn/FocusOut\n");
475
476                     keys_down.reset();
477
478                     break;
479                 }
480                 case MappingNotify:
481                 {
482                     //XRefreshKeyboardMapping(&newEvent);
483                     break;
484                 }
485                 case UnmapNotify:
486                 {
487                     //printf("UnmapNotify\n");
488
489                     win_mapped = false;
490
491                     keys_down.reset();
492
493                     break;
494                 }
495                 case MapNotify:
496                 {
497                     //printf("MapNotify\n");
498
499                     win_mapped = true;
500
501                     keys_down.reset();
502
503                     if (!replayer.update_window_dimensions())
504                         goto error_exit;
505
506                     break;
507                 }
508                 case ConfigureNotify:
509                 {
510                     if (!replayer.update_window_dimensions())
511                         goto error_exit;
512
513                     break;
514                 }
515                 case DestroyNotify:
516                 {
517                     vogl_message_printf("Exiting\n");
518                     goto normal_exit;
519                 }
520                 case ClientMessage:
521                 {
522                     if (newEvent.xclient.data.l[0] == (int)wmDeleteMessage)
523                     {
524                         vogl_message_printf("Exiting\n");
525                         goto normal_exit;
526                     }
527
528                     break;
529                 }
530                 default:
531                     break;
532             }
533         }
534
535         if (replayer.get_at_frame_boundary())
536         {
537             if ((!pSnapshot) && (loop_frame != -1) && (static_cast<int64_t>(replayer.get_frame_index()) == loop_frame))
538             {
539                 vogl_debug_printf("%s: Capturing replayer state at start of frame %u\n", VOGL_FUNCTION_NAME, replayer.get_frame_index());
540
541                 pSnapshot = replayer.snapshot_state();
542
543                 if (pSnapshot)
544                 {
545                     vogl_printf("Snapshot succeeded\n");
546
547                     snapshot_loop_start_frame = pTrace_reader->get_cur_frame();
548                     snapshot_loop_end_frame = pTrace_reader->get_cur_frame() + loop_len;
549
550                     vogl_debug_printf("%s: Loop start: %" PRIi64 " Loop end: %" PRIi64 "\n", VOGL_FUNCTION_NAME, snapshot_loop_start_frame, snapshot_loop_end_frame);
551                 }
552                 else
553                 {
554                     vogl_error_printf("Snapshot failed!\n");
555                     loop_frame = -1;
556                 }
557             }
558         }
559
560         vogl_gl_replayer::status_t status = replayer.process_pending_window_resize();
561         if (status == vogl_gl_replayer::cStatusOK)
562         {
563             for (;;)
564             {
565                 status = replayer.process_next_packet(*pTrace_reader);
566
567                 if ((status == vogl_gl_replayer::cStatusNextFrame) ||
568                     (status == vogl_gl_replayer::cStatusResizeWindow) ||
569                     (status == vogl_gl_replayer::cStatusAtEOF) ||
570                     (status == vogl_gl_replayer::cStatusHardFailure))
571                 {
572                     break;
573                 }
574             }
575         }
576
577         if (status == vogl_gl_replayer::cStatusHardFailure)
578             break;
579
580         if (status == vogl_gl_replayer::cStatusAtEOF)
581         {
582             vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_NAME, replayer.get_frame_index());
583         }
584
585         if (replayer.get_at_frame_boundary() &&
586                 pSnapshot && 
587                 (loop_count > 0) &&
588                 ((pTrace_reader->get_cur_frame() == snapshot_loop_end_frame) || (status == vogl_gl_replayer::cStatusAtEOF)))
589         {
590             status = replayer.begin_applying_snapshot(pSnapshot, false);
591             if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
592                 goto error_exit;
593
594             pTrace_reader->seek_to_frame(static_cast<uint>(snapshot_loop_start_frame));
595
596             vogl_debug_printf("%s: Applying snapshot and seeking back to frame %" PRIi64 "\n", VOGL_FUNCTION_NAME, snapshot_loop_start_frame);
597             loop_count--;
598         }
599         else
600         {
601             bool print_progress = (status == vogl_gl_replayer::cStatusAtEOF) ||
602                     ((replayer.get_at_frame_boundary()) && ((replayer.get_frame_index() % 100) == 0));
603             if (print_progress)
604             {
605                 if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER)
606                 {
607                     vogl_binary_trace_file_reader &binary_trace_reader = *static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get());
608
609                     vogl_printf("Replay now at frame index %u, trace file offet %" PRIu64 ", GL call counter %" PRIu64 ", %3.2f%% percent complete\n",
610                                replayer.get_frame_index(),
611                                binary_trace_reader.get_cur_file_ofs(),
612                                replayer.get_last_parsed_call_counter(),
613                                binary_trace_reader.get_trace_file_size() ? (binary_trace_reader.get_cur_file_ofs() * 100.0f) / binary_trace_reader.get_trace_file_size() : 0);
614                 }
615             }
616
617             if (status == vogl_gl_replayer::cStatusAtEOF)
618             {
619                 if (!endless_mode)
620                 {
621                     double time_since_start = tm.get_elapsed_secs();
622
623                     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);
624                     break;
625                 }
626
627                 vogl_printf("Resetting state and rewinding back to frame 0\n");
628
629                 replayer.reset_state();
630
631                 if (!pTrace_reader->seek_to_frame(0))
632                 {
633                     vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_NAME);
634                     goto error_exit;
635                 }
636             }
637         }
638
639         telemetry_tick();
640     }
641
642 normal_exit:
643     return true;
644
645 error_exit:
646     return false;
647 }
648
649 //----------------------------------------------------------------------------------------------------------------------
650 // xerror_handler
651 //----------------------------------------------------------------------------------------------------------------------
652 static int xerror_handler(Display *dsp, XErrorEvent *error)
653 {
654     char error_string[256];
655     XGetErrorText(dsp, error->error_code, error_string, sizeof(error_string));
656
657     fprintf(stderr, "voglbench: Fatal X Windows Error: %s\n", error_string);
658     abort();
659 }
660
661 //----------------------------------------------------------------------------------------------------------------------
662 // main
663 //----------------------------------------------------------------------------------------------------------------------
664 int main(int argc, char *argv[])
665 {
666 #if VOGL_FUNCTION_TRACING
667     fflush(stdout);
668     fflush(stderr);
669     setvbuf(stdout, NULL, _IONBF, 0);
670     setvbuf(stderr, NULL, _IONBF, 0);
671 #endif
672
673     VOGL_FUNC_TRACER
674
675     XSetErrorHandler(xerror_handler);
676
677     if (!voglbench_init(argc, argv))
678     {
679         voglbench_deinit();
680         return EXIT_FAILURE;
681     }
682
683     if (g_command_line_params.get_count("") < 2)
684     {
685         vogl_error_printf("No trace file specified!\n");
686
687         tool_print_help();
688
689         voglbench_deinit();
690         return EXIT_FAILURE;
691     }
692
693     if (g_command_line_params.get_value_as_bool("pause"))
694     {
695         vogl_message_printf("Press key to continue\n");
696         vogl_sleep(1000);
697         getchar();
698     }
699
700     bool success = tool_replay_mode();
701
702     vogl_printf("%u warning(s), %u error(s)\n",
703                     console::get_total_messages(cWarningConsoleMessage),
704                     console::get_total_messages(cErrorConsoleMessage));
705
706     voglbench_deinit();
707
708     return success ? EXIT_SUCCESS : EXIT_FAILURE;
709 }