]> git.cworth.org Git - vogl/blob - src/voglreplay/vogl_replay_tool.cpp
Initial vogl checkin
[vogl] / src / voglreplay / vogl_replay_tool.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: vogl_replay_tool.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 #if VOGL_REMOTING
51 #include "vogl_remote.h"
52 #endif // VOGL_REMOTING
53
54 #include "libtelemetry.h"
55
56 #include <X11/Xlib.h>
57 #include <X11/Xutil.h>
58 #include <X11/Xmd.h>
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         { "replay", 0, false, "Replay mode (the default), must specify .BIN or .JSON trace file to replay" },
72         { "dump", 0, false, "Dump mode: Dumps binary trace file to a JSON trace file, must specify input and output filenames" },
73         { "parse", 0, false, "Parse mode: Parse JSON trace file to a binary trace file, must specify input and output filenames" },
74         { "info", 0, false, "Info mode: Output statistics about a trace file" },
75         { "unpack_json", 0, false, "Unpack UBJ to JSON mode: Unpack UBJ (Universal Binary JSON) to textual JSON, must specify input and output filenames" },
76         { "pack_json", 0, false, "Pack JSON to UBJ mode: Pack textual JSON to UBJ, must specify input and output filenames" },
77         { "find", 0, false, "Find all calls with parameters containing a specific value, combine with -find_param, -find_func, find_namespace, etc. params" },
78         { "compare_hash_files", 0, false, "Compare two files containing CRC's or per-component sums (presumably written using dump_backbuffer_hashes)" },
79
80         // replay specific
81         { "width", 1, false, "Replay: Set initial window width (default is 1024)" },
82         { "height", 1, false, "Replay: Set initial window height (default is 768)" },
83         { "lock_window_dimensions", 0, false, "Replay: Don't automatically change window's dimensions during replay" },
84         { "trim_file", 1, false, "Replay: Create a trimmed trace file during replay, must also specify -trim_frame" },
85         { "trim_frame", 1, false, "Replay: Frame index to begin trim, 0=beginning of trace, 1=first API call after first swap, etc." },
86         { "trim_len", 1, false, "Replay: Length of trim file, default=1 frame" },
87         { "multitrim", 0, false, "Replay trimming: Trim each frame to a different file" },
88         { "multitrim_interval", 1, false, "Replay trimming: Set the # of frames between each multitrimmed frame (default is 1)" },
89         { "no_trim_optimization", 0, false, "Replay trimming: If specified, do not remove unused programs, shaders, etc. from trim file" },
90         { "trim_call", 1, false, "Replay: Call counter index to begin trim" },
91         { "write_snapshot_call", 1, false, "Replay: Write JSON snapshot at the specified call counter index" },
92         { "write_snapshot_file", 1, false, "Replay: Write JSON snapshot to specified filename, must also specify --write_snapshot_call" },
93         { "write_snapshot_blobs", 0, false, "Replay: Write JSON snapshot blob files, must also specify --write_snapshot_call" },
94         { "endless", 0, false, "Replay: Loop replay endlessly instead of exiting" },
95         { "hash_backbuffer", 0, false, "Replay: Hash and output backbuffer CRC before every swap" },
96         { "dump_backbuffer_hashes", 1, false, "Replay: Dump backbuffer hashes to a text file" },
97         { "sum_hashing", 0, false, "Replay: Use per-component sums, instead of CRC hashing (useful for multisampling)" },
98         { "dump_screenshots", 0, false, "Replay: Dump backbuffer screenshot before every swap to numbered PNG files" },
99         { "dump_screenshots_prefix", 1, false, "Replay: Set PNG screenshot file prefix" },
100         { "swap_sleep", 1, false, "Replay: Sleep for X milliseconds after every swap" },
101         { "dump_packets_on_error", 0, false, "Replay: Dump GL trace packets as JSON to stdout on replay errors" },
102         { "dump_packet_blob_files_on_error", 0, false, "Replay: Used with -dump_packets_on_error, also dumps all binary blob files associated with each packet" },
103         { "dump_all_packets", 0, false, "Replay: Dump all GL trace packets as JSON to stdout" },
104         { "dump_shaders_on_draw", 0, false, "Replay: Dump shader source on draw calls" },
105         { "dump_framebuffer_on_draw", 0, false, "Replay: Dump framebuffer to PNG files after each draw/glEnd/glCallList" },
106         { "dump_framebuffer_on_draw_prefix", 1, false, "Replay: Base path/filename to use for --dump_framebuffer_on_draw" },
107         { "dump_framebuffer_on_draw_frame", 1, false, "Replay: Limit dumping framebuffer PNG files" },
108         { "dump_framebuffer_on_draw_first_gl_call", 1, false, "Replay: Limit dumping framebuffer PNG files" },
109         { "dump_framebuffer_on_draw_last_gl_call", 1, false, "Replay: Limit dumping framebuffer PNG files" },
110         { "force_debug_context", 0, false, "Replay: Force GL debug contexts" },
111         { "pause_on_exit", 0, false, "Replay: Wait for a keypress on exit" },
112         { "debug_test_snapshot_serialization", 0, false, "Interactive Replay Mode: Immediately serialize/deserialize state snapshots after taking them" },
113         { "pause_on_frame", 1, false, "Replay interactive mode: Pause on specified frame" },
114         { "interactive", 0, false, "Replay mode: Enable keyboard keys" },
115         { "disable_snapshot_caching", 0, false, "Replay mode: Disable caching of all state snapshot files, so they can be manually modified during replay" },
116         { "benchmark", 0, false, "Replay mode: Disable glGetError()'s, divergence checks, during replaying" },
117         { "keyframe_base_filename", 1, false, "Replay: Set base filename of trimmed replay keyframes, used for fast seeking" },
118 #ifdef USE_TELEMETRY
119         { "telemetry_level", 1, false, "Set Telemetry level." },
120 #endif
121         { "loop_frame", 1, false, "Replay: loop mode's start frame" },
122         { "loop_len", 1, false, "Replay: loop mode's loop length" },
123         { "loop_count", 1, false, "Replay: loop mode's loop count" },
124         { "draw_kill_max_thresh", 1, false, "Replay: Enable draw kill mode during looping to visualize order of draws, sets the max # of draws before counter resets to 0" },
125
126         // find specific
127         { "find_func", 1, false, "Find: Limit the find to only the specified function name POSIX regex pattern" },
128         { "find_param", 1, false, "Find: The parameter value to find, hex, decimal integers, or GL enum strings OK" },
129         { "find_namespace", 1, false, "Find: Optionally limits -handle to only parameters using the specified handle namespace: invalid, GLhandleARB, GLframebuffer, GLtexture, GLrenderbuffer, GLquery, GLsampler, GLprogramARB, GLprogram, GLarray, GLlist, GLlocation, GLlocationARB, GLfence, GLsync, GLpipeline, GLshader, GLbuffer, GLfeedback, GLarrayAPPLE, GLfragmentShaderATI" },
130         { "find_param_name", 1, false, "Find: Optionally limits the find to only params with the specified name (specify \"return\" to limit search to only return values)" },
131         { "find_frame_low", 1, false, "Find: Optionally limit the find to frames beginning at the specified frame index" },
132         { "find_frame_high", 1, false, "Find: Optionally limit the find to frames up to and including the specified frame index" },
133         { "find_call_low", 1, false, "Find: Optionally limit the find to GL calls beginning at the specified call index" },
134         { "find_call_high", 1, false, "Find: Optionally limit the find to GL calls up to and including the specified call index" },
135
136         // compare_hash_files specific
137         { "sum_compare_threshold", 1, false, "compare_hash_files: Only report mismatches greater than the specified threshold, use with --sum_hashing" },
138         { "compare_ignore_frames", 1, false, "compare_hash_files: Ignore first X frames" },
139         { "compare_expected_frames", 1, false, "compare_hash_files: Fail if the # of frames is not X" },
140         { "compare_first_frame", 1, false, "compare_hash_files: First frame to compare to in second hash file" },
141         { "ignore_line_count_differences", 0, false, "compare_hash_files: Don't stop if the # of lines differs between the two files" },
142
143         // dump specific
144         { "verify", 0, false, "Dump: Fully round-trip verify all JSON objects vs. the original packet's" },
145         { "no_blobs", 0, false, "Dump: Don't write binary blob files" },
146         { "write_debug_info", 0, false, "Dump: Write extra debug info to output JSON trace files" },
147         { "loose_file_path", 1, false, "Prefer reading trace blob files from this directory vs. the archive referred to or present in the trace file" },
148         { "debug", 0, false, "Enable verbose debug information" },
149         { "logfile", 1, false, "Create logfile" },
150         { "logfile_append", 1, false, "Append output to logfile" },
151         { "help", 0, false, "Display this help" },
152         { "?", 0, false, "Display this help" },
153         { "replay_debug", 0, false, "Enable various debug/verification code in the replayer" },
154         { "pause", 0, false, "Wait for a key at startup (so a debugger can be attached)" },
155         { "verbose", 0, false, "Verbose debug output" },
156         { "quiet", 0, false, "Disable all console output" },
157         { "gl_debug_log", 0, false, "Dump GL prolog/epilog messages to stdout (very slow - helpful to narrow down driver crashes)" },
158         { "vogl_func_tracing", 0, false, NULL },
159     };
160
161 static command_line_param_desc g_command_line_interactive_descs[] =
162     {
163         { "s", 0, false, "slow mode" },
164         { "<space>", 0, false, "pause" },
165         { "r", 0, false, "rewind to beginning" },
166         { "e", 0, false, "seek to last frame" },
167         { "t", 0, false, "trim frame" },
168         { "j", 0, false, "trim and play json" },
169         { "<left>", 0, false, "step left" },
170         { "<right>", 0, false, "step right" },
171     };
172
173 //----------------------------------------------------------------------------------------------------------------------
174 // init_logfile
175 //----------------------------------------------------------------------------------------------------------------------
176 static bool init_logfile()
177 {
178     VOGL_FUNC_TRACER
179
180     dynamic_string backbuffer_hash_file;
181     if (g_command_line_params.get_value_as_string(backbuffer_hash_file, "dump_backbuffer_hashes"))
182     {
183         remove(backbuffer_hash_file.get_ptr());
184         vogl_message_printf("Deleted backbuffer hash file \"%s\"\n", backbuffer_hash_file.get_ptr());
185     }
186
187     dynamic_string log_file(g_command_line_params.get_value_as_string_or_empty("logfile"));
188     dynamic_string log_file_append(g_command_line_params.get_value_as_string_or_empty("logfile_append"));
189     if (log_file.is_empty() && log_file_append.is_empty())
190         return true;
191
192     dynamic_string filename(log_file_append.is_empty() ? log_file : log_file_append);
193
194     // This purposely leaks, don't care
195     g_vogl_pLog_stream = vogl_new(cfile_stream);
196
197     if (!g_vogl_pLog_stream->open(filename.get_ptr(), cDataStreamWritable, !log_file_append.is_empty()))
198     {
199         vogl_error_printf("%s: Failed opening log file \"%s\"\n", VOGL_FUNCTION_NAME, filename.get_ptr());
200         return false;
201     }
202     else
203     {
204         vogl_message_printf("Opened log file \"%s\"\n", filename.get_ptr());
205
206         console::set_log_stream(g_vogl_pLog_stream);
207     }
208
209     return true;
210 }
211
212 //----------------------------------------------------------------------------------------------------------------------
213 // tool_print_title
214 //----------------------------------------------------------------------------------------------------------------------
215 static void tool_print_title()
216 {
217     VOGL_FUNC_TRACER
218
219     printf("voglreplay ");
220     if (sizeof(void *) > 4)
221         console::printf("64-bit ");
222     else
223         console::printf("32-bit ");
224 #ifdef VOGL_BUILD_DEBUG
225     console::printf("Debug ");
226 #else
227     console::printf("Release ");
228 #endif
229     console::printf("Built %s %s\n", __DATE__, __TIME__);
230 }
231
232 //----------------------------------------------------------------------------------------------------------------------
233 // tool_print_help
234 //----------------------------------------------------------------------------------------------------------------------
235 static void tool_print_help()
236 {
237     VOGL_FUNC_TRACER
238
239     console::printf("Usage: voglreplay [ -option ... ] input_file optional_output_file [ -option ... ]\n");
240     console::printf("Command line options may begin with single minus \"-\" or double minus \"--\"\n");
241
242     console::printf("\nCommand line options:\n");
243
244     dump_command_line_info(VOGL_ARRAY_SIZE(g_command_line_param_descs), g_command_line_param_descs, "--");
245
246     console::printf("\nInteractive replay mode keys:\n");
247     dump_command_line_info(VOGL_ARRAY_SIZE(g_command_line_interactive_descs), g_command_line_interactive_descs, " ");
248 }
249
250 //----------------------------------------------------------------------------------------------------------------------
251 // init_command_line_params
252 //----------------------------------------------------------------------------------------------------------------------
253 static bool init_command_line_params(int argc, char *argv[])
254 {
255     VOGL_FUNC_TRACER
256
257     command_line_params::parse_config parse_cfg;
258     parse_cfg.m_single_minus_params = true;
259     parse_cfg.m_double_minus_params = true;
260
261     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))
262     {
263         vogl_error_printf("%s: Failed parsing command line parameters!\n", VOGL_FUNCTION_NAME);
264         return false;
265     }
266
267     if (!init_logfile())
268         return false;
269
270     if (g_command_line_params.get_value_as_bool("help") || g_command_line_params.get_value_as_bool("?"))
271     {
272         tool_print_help();
273         return false;
274     }
275
276     return true;
277 }
278
279 //----------------------------------------------------------------------------------------------------------------------
280 // load_gl
281 //----------------------------------------------------------------------------------------------------------------------
282 static bool load_gl()
283 {
284     VOGL_FUNC_TRACER
285
286     g_actual_libgl_module_handle = dlopen("libGL.so.1", RTLD_LAZY);
287     if (!g_actual_libgl_module_handle)
288     {
289         vogl_error_printf("%s: Failed loading libGL.so.1!\n", VOGL_FUNCTION_NAME);
290         return false;
291     }
292
293     GL_ENTRYPOINT(glXGetProcAddress) = reinterpret_cast<glXGetProcAddress_func_ptr_t>(dlsym(g_actual_libgl_module_handle, "glXGetProcAddress"));
294     if (!GL_ENTRYPOINT(glXGetProcAddress))
295     {
296         vogl_error_printf("%s: Failed getting address of glXGetProcAddress() from libGL.so.1!\n", VOGL_FUNCTION_NAME);
297         return false;
298     }
299
300     return true;
301 }
302
303 //----------------------------------------------------------------------------------------------------------------------
304 // vogl_get_proc_address_helper
305 //----------------------------------------------------------------------------------------------------------------------
306 static vogl_void_func_ptr_t vogl_get_proc_address_helper(const char *pName)
307 {
308     VOGL_FUNC_TRACER
309
310     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;
311
312     if ((!pFunc) && (GL_ENTRYPOINT(glXGetProcAddress)))
313         pFunc = reinterpret_cast<vogl_void_func_ptr_t>(GL_ENTRYPOINT(glXGetProcAddress)(reinterpret_cast<const GLubyte *>(pName)));
314
315     return pFunc;
316 }
317
318 #if 0
319 // HACK HACK - for testing
320 static void vogl_direct_gl_func_prolog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **pStack_data)
321 {
322         printf("*** PROLOG %u\n", entrypoint_id);
323 }
324
325 void vogl_direct_gl_func_epilog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **pStack_data)
326 {
327         printf("*** EPILOG %u\n", entrypoint_id);
328 }
329 #endif
330
331 //----------------------------------------------------------------------------------------------------------------------
332 // vogl_direct_gl_func_prolog - This function is called before EVERY single GL/GLX function call we make.
333 //----------------------------------------------------------------------------------------------------------------------
334 static void vogl_direct_gl_func_prolog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **ppStack_data)
335 {
336     VOGL_NOTE_UNUSED(entrypoint_id);
337     VOGL_NOTE_UNUSED(pUser_data);
338     VOGL_NOTE_UNUSED(ppStack_data);
339
340     printf("* GLPROLOG %s\n", g_vogl_entrypoint_descs[entrypoint_id].m_pName);
341 }
342
343 //----------------------------------------------------------------------------------------------------------------------
344 // vogl_direct_gl_func_epilog - This function is called immediately after EVERY single GL/GLX function call we make.
345 //----------------------------------------------------------------------------------------------------------------------
346 static void vogl_direct_gl_func_epilog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **ppStack_data)
347 {
348     VOGL_NOTE_UNUSED(entrypoint_id);
349     VOGL_NOTE_UNUSED(pUser_data);
350     VOGL_NOTE_UNUSED(ppStack_data);
351
352     printf("* GLEPILOG %s\n", g_vogl_entrypoint_descs[entrypoint_id].m_pName);
353 }
354
355 //----------------------------------------------------------------------------------------------------------------------
356 // vogl_replay_init
357 //----------------------------------------------------------------------------------------------------------------------
358 static bool vogl_replay_init(int argc, char *argv[])
359 {
360     VOGL_FUNC_TRACER
361
362     g_thread_safe_random.seed_from_urandom();
363
364     colorized_console::init();
365     colorized_console::set_exception_callback();
366     //console::set_tool_prefix("(voglreplay) ");
367
368     tool_print_title();
369
370     if (!init_command_line_params(argc, argv))
371         return false;
372
373 #ifdef USE_TELEMETRY
374     int telemetry_level = g_command_line_params.get_value_as_int("telemetry_level", 0,
375                                                                  TELEMETRY_LEVEL_MIN + 1, TELEMETRY_LEVEL_MIN, TELEMETRY_LEVEL_MAX);
376     telemetry_set_level(telemetry_level);
377     telemetry_tick();
378 #endif
379
380     vogl_common_lib_early_init();
381     vogl_common_lib_global_init();
382
383     if (g_command_line_params.get_value_as_bool("quiet"))
384         console::disable_output();
385
386     if (g_command_line_params.get_value_as_bool("gl_debug_log"))
387     {
388         vogl_set_direct_gl_func_prolog(vogl_direct_gl_func_prolog, NULL);
389         vogl_set_direct_gl_func_epilog(vogl_direct_gl_func_epilog, NULL);
390     }
391
392     if (!load_gl())
393         return false;
394
395     bool wrap_all_gl_calls = true;
396
397     if (g_command_line_params.get_value_as_bool("benchmark"))
398         wrap_all_gl_calls = false;
399
400     vogl_init_actual_gl_entrypoints(vogl_get_proc_address_helper, wrap_all_gl_calls);
401
402 #if 0
403         // HACK HACK - for testing
404         vogl_set_direct_gl_func_prolog(vogl_direct_gl_func_prolog, NULL);
405         vogl_set_direct_gl_func_epilog(vogl_direct_gl_func_epilog, NULL);
406 #endif
407
408     return true;
409 }
410
411 //----------------------------------------------------------------------------------------------------------------------
412 // vogl_replay_deinit
413 //----------------------------------------------------------------------------------------------------------------------
414 static void vogl_replay_deinit()
415 {
416     VOGL_FUNC_TRACER
417
418     colorized_console::deinit();
419 }
420
421 //----------------------------------------------------------------------------------------------------------------------
422 // X11_Pending - from SDL
423 //----------------------------------------------------------------------------------------------------------------------
424 static int X11_Pending(Display *display)
425 {
426     VOGL_FUNC_TRACER
427
428     /* Flush the display connection and look to see if events are queued */
429     XFlush(display);
430     if (XEventsQueued(display, QueuedAlready))
431     {
432         return (1);
433     }
434
435     /* More drastic measures are required -- see if X is ready to talk */
436     {
437         static struct timeval zero_time; /* static == 0 */
438         int x11_fd;
439         fd_set fdset;
440
441         x11_fd = ConnectionNumber(display);
442         FD_ZERO(&fdset);
443         FD_SET(x11_fd, &fdset);
444         if (select(x11_fd + 1, &fdset, NULL, NULL, &zero_time) == 1)
445         {
446             return (XPending(display));
447         }
448     }
449
450     /* Oh well, nothing is ready .. */
451     return (0);
452 }
453
454 //----------------------------------------------------------------------------------------------------------------------
455 // read_state_snapshot_from_trace
456 //----------------------------------------------------------------------------------------------------------------------
457 static vogl_gl_state_snapshot *read_state_snapshot_from_trace(dynamic_string filename)
458 {
459     VOGL_FUNC_TRACER
460
461     timed_scope ts(VOGL_FUNCTION_NAME);
462
463     vogl_gl_state_snapshot *pSnapshot = NULL;
464
465     vogl_loose_file_blob_manager file_blob_manager;
466     dynamic_string keyframe_trace_path(file_utils::get_pathname(filename.get_ptr()));
467     file_blob_manager.init(cBMFReadable, keyframe_trace_path.get_ptr());
468
469     dynamic_string actual_keyframe_filename;
470     vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(filename, actual_keyframe_filename, NULL));
471     if (!pTrace_reader.get())
472     {
473         vogl_error_printf("%s: Failed reading keyframe file %s!\n", VOGL_FUNCTION_NAME, filename.get_ptr());
474         return NULL;
475     }
476
477     vogl_ctypes trace_gl_ctypes(pTrace_reader->get_sof_packet().m_pointer_sizes);
478
479     vogl_trace_packet keyframe_trace_packet(&trace_gl_ctypes);
480
481     bool found_snapshot = false;
482     do
483     {
484         vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
485
486         if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
487         {
488             vogl_error_printf("%s: Failed reading from keyframe trace file!\n", VOGL_FUNCTION_NAME);
489             return NULL;
490         }
491
492         if ((read_status == vogl_trace_file_reader::cEOF) || (pTrace_reader->get_packet_type() == cTSPTEOF))
493         {
494             vogl_error_printf("%s: Failed finding state snapshot in keyframe file!\n", VOGL_FUNCTION_NAME);
495             return NULL;
496         }
497
498         if (pTrace_reader->get_packet_type() != cTSPTGLEntrypoint)
499             continue;
500
501         if (!keyframe_trace_packet.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
502         {
503             vogl_error_printf("%s: Failed parsing GL entrypoint packet in keyframe file\n", VOGL_FUNCTION_NAME);
504             return NULL;
505         }
506
507         const vogl_trace_gl_entrypoint_packet *pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
508         gl_entrypoint_id_t entrypoint_id = static_cast<gl_entrypoint_id_t>(pGL_packet->m_entrypoint_id);
509
510         //const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[entrypoint_id];
511
512         if (vogl_is_swap_buffers_entrypoint(entrypoint_id) || vogl_is_draw_entrypoint(entrypoint_id) || vogl_is_make_current_entrypoint(entrypoint_id))
513         {
514             vogl_error_printf("Failed finding state snapshot in keyframe file!\n");
515             return NULL;
516         }
517
518         switch (entrypoint_id)
519         {
520             case VOGL_ENTRYPOINT_glInternalTraceCommandRAD:
521             {
522                 GLuint cmd = keyframe_trace_packet.get_param_value<GLuint>(0);
523                 GLuint size = keyframe_trace_packet.get_param_value<GLuint>(1);
524                 VOGL_NOTE_UNUSED(size);
525
526                 if (cmd == cITCRKeyValueMap)
527                 {
528                     key_value_map &kvm = keyframe_trace_packet.get_key_value_map();
529
530                     dynamic_string cmd_type(kvm.get_string("command_type"));
531                     if (cmd_type == "state_snapshot")
532                     {
533                         dynamic_string id(kvm.get_string("binary_id"));
534                         if (id.is_empty())
535                         {
536                             vogl_error_printf("%s: Missing binary_id field in glInternalTraceCommandRAD key_valye_map command type: \"%s\"\n", VOGL_FUNCTION_NAME, cmd_type.get_ptr());
537                             return NULL;
538                         }
539
540                         uint8_vec snapshot_data;
541                         {
542                             timed_scope ts("get_multi_blob_manager().get");
543                             if (!pTrace_reader->get_multi_blob_manager().get(id, snapshot_data) || (snapshot_data.is_empty()))
544                             {
545                                 vogl_error_printf("%s: Failed reading snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
546                                 return NULL;
547                             }
548                         }
549
550                         vogl_message_printf("%s: Deserializing state snapshot \"%s\", %u bytes\n", VOGL_FUNCTION_NAME, id.get_ptr(), snapshot_data.size());
551
552                         json_document doc;
553                         {
554                             timed_scope ts("doc.binary_deserialize");
555                             if (!doc.binary_deserialize(snapshot_data) || (!doc.get_root()))
556                             {
557                                 vogl_error_printf("%s: Failed deserializing JSON snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
558                                 return NULL;
559                             }
560                         }
561
562                         pSnapshot = vogl_new(vogl_gl_state_snapshot);
563
564                         timed_scope ts("pSnapshot->deserialize");
565                         if (!pSnapshot->deserialize(*doc.get_root(), pTrace_reader->get_multi_blob_manager(), &trace_gl_ctypes))
566                         {
567                             vogl_delete(pSnapshot);
568                             pSnapshot = NULL;
569
570                             vogl_error_printf("%s: Failed deserializing snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
571                             return NULL;
572                         }
573
574                         found_snapshot = true;
575                     }
576                 }
577
578                 break;
579             }
580             default:
581                 break;
582         }
583
584     } while (!found_snapshot);
585
586     return pSnapshot;
587 }
588
589 //----------------------------------------------------------------------------------------------------------------------
590 // get_replayer_flags_from_command_line_params
591 //----------------------------------------------------------------------------------------------------------------------
592 static uint get_replayer_flags_from_command_line_params(bool interactive_mode)
593 {
594     uint replayer_flags = 0;
595
596     static struct
597     {
598         const char *m_pCommand;
599         uint m_flag;
600     } s_replayer_command_line_params[] =
601           {
602               { "benchmark", cGLReplayerBenchmarkMode },
603               { "verbose", cGLReplayerVerboseMode },
604               { "force_debug_context", cGLReplayerForceDebugContexts },
605               { "dump_all_packets", cGLReplayerDumpAllPackets },
606               { "debug", cGLReplayerDebugMode },
607               { "lock_window_dimensions", cGLReplayerLockWindowDimensions },
608               { "replay_debug", cGLReplayerLowLevelDebugMode },
609               { "dump_packet_blob_files_on_error", cGLReplayerDumpPacketBlobFilesOnError },
610               { "dump_shaders_on_draw", cGLReplayerDumpShadersOnDraw },
611               { "dump_packets_on_error", cGLReplayerDumpPacketsOnError },
612               { "dump_screenshots", cGLReplayerDumpScreenshots },
613               { "hash_backbuffer", cGLReplayerHashBackbuffer },
614               { "dump_backbuffer_hashes", cGLReplayerDumpBackbufferHashes },
615               { "sum_hashing", cGLReplayerSumHashing },
616               { "dump_framebuffer_on_draw", cGLReplayerDumpFramebufferOnDraws },
617           };
618
619     for (uint i = 0; i < sizeof(s_replayer_command_line_params) / sizeof(s_replayer_command_line_params[0]); i++)
620         if (g_command_line_params.get_value_as_bool(s_replayer_command_line_params[i].m_pCommand))
621             replayer_flags |= s_replayer_command_line_params[i].m_flag;
622
623     if (interactive_mode && !g_command_line_params.get_value_as_bool("disable_snapshot_caching"))
624         replayer_flags |= cGLReplayerSnapshotCaching;
625
626     return replayer_flags;
627 }
628
629 //----------------------------------------------------------------------------------------------------------------------
630 // tool_replay_mode
631 //----------------------------------------------------------------------------------------------------------------------
632 static bool tool_replay_mode()
633 {
634     VOGL_FUNC_TRACER
635
636     dynamic_string trace_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
637     if (trace_filename.is_empty())
638     {
639         vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_NAME);
640         return false;
641     }
642
643     dynamic_string actual_trace_filename;
644     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()));
645     if (!pTrace_reader.get())
646     {
647         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());
648         return false;
649     }
650
651     vogl_printf("Reading trace file %s\n", actual_trace_filename.get_ptr());
652
653     bool interactive_mode = g_command_line_params.get_value_as_bool("interactive");
654
655     vogl_gl_replayer replayer;
656
657     uint replayer_flags = get_replayer_flags_from_command_line_params(interactive_mode);
658
659     vogl_replay_window window;
660
661     // TODO: This will create a window with default attributes, which seems fine for the majority of traces.
662     // 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.
663     // Also, this design only supports a single window, which is going to be a problem with multiple window traces.
664     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)))
665     {
666         vogl_error_printf("%s: Failed initializing replay window\n", VOGL_FUNCTION_NAME);
667         return false;
668     }
669
670     if (!replayer.init(replayer_flags, &window, pTrace_reader->get_sof_packet(), pTrace_reader->get_multi_blob_manager()))
671     {
672         vogl_error_printf("%s: Failed initializing GL replayer\n", VOGL_FUNCTION_NAME);
673         return false;
674     }
675
676     if (replayer_flags & cGLReplayerBenchmarkMode)
677     {
678         // Also disable all glGetError() calls in vogl_utils.cpp.
679         vogl_disable_gl_get_error();
680     }
681
682     replayer.set_swap_sleep_time(g_command_line_params.get_value_as_uint("swap_sleep"));
683     replayer.set_dump_framebuffer_on_draw_prefix(g_command_line_params.get_value_as_string("dump_framebuffer_on_draw_prefix", 0, "screenshot"));
684     replayer.set_screenshot_prefix(g_command_line_params.get_value_as_string("dump_screenshots_prefix", 0, "screenshot"));
685     replayer.set_backbuffer_hash_filename(g_command_line_params.get_value_as_string_or_empty("dump_backbuffer_hashes"));
686     replayer.set_dump_framebuffer_on_draw_frame_index(g_command_line_params.get_value_as_int("dump_framebuffer_on_draw_frame", 0, -1, 0, INT_MAX));
687     replayer.set_dump_framebuffer_on_draw_first_gl_call_index(g_command_line_params.get_value_as_int("dump_framebuffer_on_draw_first_gl_call", 0, -1, 0, INT_MAX));
688     replayer.set_dump_framebuffer_on_draw_last_gl_call_index(g_command_line_params.get_value_as_int("dump_framebuffer_on_draw_last_gl_call", 0, -1, 0, INT_MAX));
689
690     XSelectInput(window.get_display(), window.get_xwindow(),
691                  EnterWindowMask | LeaveWindowMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | StructureNotifyMask | KeymapStateMask);
692
693     Atom wmDeleteMessage = XInternAtom(window.get_display(), "WM_DELETE_WINDOW", False);
694     XSetWMProtocols(window.get_display(), window.get_xwindow(), &wmDeleteMessage, 1);
695
696     Bool win_mapped = false;
697
698     vogl_gl_state_snapshot *pSnapshot = NULL;
699     int64_t snapshot_loop_start_frame = -1;
700     int64_t snapshot_loop_end_frame = -1;
701
702     vogl::hash_map<uint64_t> keys_pressed, keys_down;
703     dynamic_string keyframe_base_filename(g_command_line_params.get_value_as_string("keyframe_base_filename"));
704     vogl::vector<uint64_t> keyframes;
705     int64_t paused_mode_frame_index = -1;
706     int64_t take_snapshot_at_frame_index = g_command_line_params.get_value_as_int64("pause_on_frame", 0, -1);
707     bool paused_mode = false;
708     bool slow_mode = false;
709
710     if (!keyframe_base_filename.is_empty())
711     {
712         find_files finder;
713         if (!finder.find((keyframe_base_filename + "*.bin").get_ptr()))
714         {
715             vogl_error_printf("Failed finding files: %s\n", keyframe_base_filename.get_ptr());
716             return false;
717         }
718
719         for (uint i = 0; i < finder.get_files().size(); i++)
720         {
721             dynamic_string base_name(finder.get_files()[i].m_name);
722             dynamic_string ext(base_name);
723             file_utils::get_extension(ext);
724             if (ext != "bin")
725                 continue;
726
727             file_utils::remove_extension(base_name);
728             int underscore_ofs = base_name.find_right('_');
729             if (underscore_ofs < 0)
730                 continue;
731
732             dynamic_string frame_index_str(base_name.right(underscore_ofs + 1));
733             if (frame_index_str.is_empty())
734                 continue;
735
736             const char *pFrame_index = frame_index_str.get_ptr();
737
738             uint64_t frame_index = 0;
739             if (!string_ptr_to_uint64(pFrame_index, frame_index))
740                 continue;
741
742             keyframes.push_back(frame_index);
743             printf("Found keyframe file %s index %" PRIu64 "\n", finder.get_files()[i].m_fullname.get_ptr(), frame_index);
744         }
745
746         keyframes.sort();
747     }
748
749     int loop_frame = g_command_line_params.get_value_as_int("loop_frame", 0, -1);
750     int loop_len = math::maximum<int>(g_command_line_params.get_value_as_int("loop_len", 0, 1), 1);
751     int loop_count = math::maximum<int>(g_command_line_params.get_value_as_int("loop_count", 0, cINT32_MAX), 1);
752     int draw_kill_max_thresh = g_command_line_params.get_value_as_int("draw_kill_max_thresh", 0, -1);
753     bool endless_mode = g_command_line_params.get_value_as_bool("endless");
754
755     bool multitrim_mode = g_command_line_params.get_value_as_bool("multitrim");
756     int multitrim_interval = g_command_line_params.get_value_as_int("multitrim_interval", 0, 1, 1);
757     int multitrim_frames_remaining = 0;
758
759     int64_t write_snapshot_index = g_command_line_params.get_value_as_int64("write_snapshot_call", 0, -1, 0);
760     dynamic_string write_snapshot_filename = g_command_line_params.get_value_as_string("write_snapshot_file", 0, "state_snapshot.json");
761
762     int64_t trim_call_index = g_command_line_params.get_value_as_int64("trim_call", 0, -1, 0);
763     vogl::vector<uint> trim_frames(g_command_line_params.get_count("trim_frame"));
764     for (uint i = 0; i < trim_frames.size(); i++)
765     {
766         bool parsed_successfully;
767         trim_frames[i] = g_command_line_params.get_value_as_uint("trim_frame", i, 0, 0, cUINT32_MAX, 0, &parsed_successfully);
768         if (!parsed_successfully)
769         {
770             vogl_error_printf("%s: Failed parsing -trim_frame at index %u\n", VOGL_FUNCTION_NAME, i);
771             return false;
772         }
773     }
774
775     vogl::vector<dynamic_string> trim_filenames(g_command_line_params.get_count("trim_file"));
776     for (uint i = 0; i < trim_filenames.size(); i++)
777     {
778         dynamic_string filename(g_command_line_params.get_value_as_string("trim_file", i));
779
780         if (filename.is_empty())
781         {
782             vogl_error_printf("%s: Invalid trim filename\n", VOGL_FUNCTION_NAME);
783             return false;
784         }
785
786         trim_filenames[i] = filename;
787
788         if (file_utils::add_default_extension(trim_filenames[i], ".bin"))
789             vogl_message_printf("%s: Trim output filename \"%s\", didn't have an extension, appended \".bin\" to the filename: %s\n", VOGL_FUNCTION_NAME, filename.get_ptr(), trim_filenames[i].get_ptr());
790     }
791
792     vogl::vector<uint> trim_lens(g_command_line_params.get_count("trim_len"));
793     for (uint i = 0; i < trim_lens.size(); i++)
794     {
795         bool parsed_successfully;
796         trim_lens[i] = g_command_line_params.get_value_as_uint("trim_len", i, 1, 0, cUINT32_MAX, 0, &parsed_successfully);
797         if (!parsed_successfully)
798         {
799             vogl_error_printf("%s: Failed parsing -trim_len at index %u\n", VOGL_FUNCTION_NAME, i);
800             return false;
801         }
802     }
803
804     uint num_trim_files_written = 0;
805     uint highest_frame_to_trim = 0;
806
807     vogl_loose_file_blob_manager trim_file_blob_manager;
808
809     timer tm;
810
811     if (trim_frames.size())
812     {
813         if (trim_filenames.is_empty())
814         {
815             console::error("%s: -trim_frame specified without specifying at least one -trim_file or -trim_call\n", VOGL_FUNCTION_NAME);
816             return false;
817         }
818     }
819
820     if (write_snapshot_index >= 0)
821     {
822         if ((multitrim_mode) || (trim_frames.size()) || (trim_lens.size()) || (trim_call_index >= 0))
823         {
824             console::warning("%s: Can't write snapshot and trim at the same time, disabling trimming\n", VOGL_FUNCTION_NAME);
825
826             multitrim_mode = false;
827             trim_frames.clear();
828             trim_lens.clear();
829             trim_call_index = -1;
830         }
831
832         if (draw_kill_max_thresh > 0)
833         {
834             console::warning("%s: Write snapshot mode is enabled, disabling -draw_kill_max_thresh\n", VOGL_FUNCTION_NAME);
835             draw_kill_max_thresh = -1;
836         }
837
838         if (endless_mode)
839         {
840             console::warning("%s: Write snapshot mode is enabled, disabling -endless\n", VOGL_FUNCTION_NAME);
841             endless_mode = false;
842         }
843     }
844     else if ((trim_frames.size()) || (trim_call_index != -1))
845     {
846         if (trim_filenames.is_empty())
847         {
848             console::error("%s: Must also specify at least one -trim_file\n", VOGL_FUNCTION_NAME);
849             return false;
850         }
851
852         if (trim_call_index != -1)
853         {
854             if (trim_frames.size())
855             {
856                 console::error("%s: Can't specify both -trim_call and -trim_frame\n", VOGL_FUNCTION_NAME);
857                 return false;
858             }
859
860             if (multitrim_mode)
861             {
862                 console::error("%s: Can't specify both -trim_call and -multitrim\n", VOGL_FUNCTION_NAME);
863                 return false;
864             }
865
866             if (trim_filenames.size() > 1)
867             {
868                 console::warning("%s: -trim_call specified but more than 1 -trim_file specified - ignoring all but first -trim_file's\n", VOGL_FUNCTION_NAME);
869                 trim_filenames.resize(1);
870             }
871         }
872
873         trim_file_blob_manager.init(cBMFWritable);
874
875         if (trim_frames.size() > 1)
876         {
877             if ((trim_filenames.size() > 1) && (trim_filenames.size() != trim_frames.size()))
878             {
879                 console::error("%s: More than 1 -trim_frame was specified, and more than 1 -trim_file was specified, but the number of -trim_file's must match the number of -trim_frame's (or only specify one -trim_file to use it as a base filename)\n", VOGL_FUNCTION_NAME);
880                 return false;
881             }
882
883             if ((trim_lens.size() > 1) && (trim_lens.size() != trim_frames.size()))
884             {
885                 console::error("%s: More than 1 -trim_frame was specified, and more than 1 -trim_len's was specified, but the number of -trim_len's must match the number of -trim_frame's (or only specify one -trim_len to use it for all trims)\n", VOGL_FUNCTION_NAME);
886                 return false;
887             }
888         }
889
890         if ((multitrim_mode) && (trim_filenames.size() > 1))
891         {
892             console::warning("%s: Only 1 filename needs to be specified in -multitrim mode\n", VOGL_FUNCTION_NAME);
893         }
894
895         if (loop_frame != -1)
896         {
897             console::warning("%s: Trim is enabled, disabling -loop_frame\n", VOGL_FUNCTION_NAME);
898             loop_frame = -1;
899             loop_len = 1;
900             loop_count = 1;
901         }
902
903         if (draw_kill_max_thresh > 0)
904         {
905             console::warning("%s: Trim is enabled, disabling -draw_kill_max_thresh\n", VOGL_FUNCTION_NAME);
906             draw_kill_max_thresh = -1;
907         }
908
909         if (endless_mode)
910         {
911             console::warning("%s: Trim is enabled, disabling -endless\n", VOGL_FUNCTION_NAME);
912             endless_mode = false;
913         }
914
915         if (trim_call_index == -1)
916         {
917             for (uint tf = 0; tf < trim_frames.size(); tf++)
918             {
919                 uint len = 1;
920                 if (trim_lens.size())
921                 {
922                     if (trim_lens.size() < trim_frames.size())
923                         len = trim_lens[0];
924                     else
925                         len = trim_lens[tf];
926                 }
927                 len = math::maximum(len, 1U);
928                 highest_frame_to_trim = math::maximum<uint>(highest_frame_to_trim, trim_frames[tf] + len - 1);
929             }
930         }
931     }
932     else
933     {
934         if (trim_filenames.size())
935             console::warning("%s: -trim_file was specified, but -trim_frame was not specified so ignoring\n", VOGL_FUNCTION_NAME);
936         if (trim_lens.size())
937             console::warning("%s: -trim_len was specified, but -trim_frame was not specified so ignoring\n", VOGL_FUNCTION_NAME);
938
939         trim_filenames.clear();
940         trim_lens.clear();
941     }
942
943     tm.start();
944
945     for (;;)
946     {
947         tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Main Loop");
948
949         while (X11_Pending(window.get_display()))
950         {
951             XEvent newEvent;
952
953             // Watch for new X eventsn
954             XNextEvent(window.get_display(), &newEvent);
955
956             switch (newEvent.type)
957             {
958                 case KeyPress:
959                 {
960                     KeySym xsym = XLookupKeysym(&newEvent.xkey, 0);
961
962                     //printf("KeyPress 0%04" PRIX64 "%" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym);
963
964                     keys_down.insert(xsym);
965                     keys_pressed.insert(xsym);
966
967                     break;
968                 }
969                 case KeyRelease:
970                 {
971                     KeySym xsym = XLookupKeysym(&newEvent.xkey, 0);
972
973                     //printf("KeyRelease 0x%04" PRIX64 " %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym);
974
975                     keys_down.erase(xsym);
976
977                     break;
978                 }
979                 case FocusIn:
980                 case FocusOut:
981                 {
982                     //printf("FocusIn/FocusOut\n");
983
984                     keys_down.reset();
985
986                     break;
987                 }
988                 case MappingNotify:
989                 {
990                     //XRefreshKeyboardMapping(&newEvent);
991                     break;
992                 }
993                 case UnmapNotify:
994                 {
995                     //printf("UnmapNotify\n");
996
997                     win_mapped = false;
998
999                     keys_down.reset();
1000
1001                     break;
1002                 }
1003                 case MapNotify:
1004                 {
1005                     //printf("MapNotify\n");
1006
1007                     win_mapped = true;
1008
1009                     keys_down.reset();
1010
1011                     if (!replayer.update_window_dimensions())
1012                         goto error_exit;
1013
1014                     break;
1015                 }
1016                 case ConfigureNotify:
1017                 {
1018                     if (!replayer.update_window_dimensions())
1019                         goto error_exit;
1020
1021                     break;
1022                 }
1023                 case DestroyNotify:
1024                 {
1025                     vogl_message_printf("Exiting\n");
1026                     goto normal_exit;
1027                 }
1028                 case ClientMessage:
1029                 {
1030                     if (newEvent.xclient.data.l[0] == (int)wmDeleteMessage)
1031                     {
1032                         vogl_message_printf("Exiting\n");
1033                         goto normal_exit;
1034                     }
1035
1036                     break;
1037                 }
1038                 default:
1039                     break;
1040             }
1041         }
1042
1043         // TODO: Massively refactor this! Move the replayer into a class, etc.
1044         if (interactive_mode)
1045         {
1046             if (!win_mapped)
1047             {
1048                 vogl_sleep(10);
1049                 continue;
1050             }
1051
1052             tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Interactive");
1053
1054             // Interactive mode is more of a test bad to validate a bunch of classes. It's kind of narly because the replayer's object can be in odd intermediate/pending states during window
1055             // resizes - hopefully this complexity will go away once we get pbuffer's or whatever in.
1056             vogl_debug_printf("%s: At frame boundary: %u, beginning of frame %u, pause frame %" PRIi64 ", taking snapshot at frame %" PRIi64 "\n", VOGL_FUNCTION_NAME, replayer.get_at_frame_boundary(), replayer.get_frame_index(), paused_mode_frame_index, take_snapshot_at_frame_index);
1057
1058             if (keys_pressed.contains('c'))
1059             {
1060                 keys_pressed.erase('c');
1061                 if (replayer.is_valid())
1062                 {
1063                     dynamic_string filename;
1064                     for (uint i = 0; i < 10000000; i++)
1065                     {
1066                         filename.format("screenshot_%06u.png", i);
1067                         if (!file_utils::does_file_exist(filename.get_ptr()))
1068                             break;
1069                     }
1070
1071                     replayer.dump_frontbuffer_screenshot_before_next_swap(filename);
1072                 }
1073             }
1074
1075             if (keys_pressed.contains('s'))
1076             {
1077                 keys_pressed.erase('s');
1078                 slow_mode = !slow_mode;
1079             }
1080
1081             // When paused, we'll NOT be at a frame boundary because the prev. loop applied a state snapshot (which will be pending)
1082             if (replayer.get_at_frame_boundary() && !replayer.get_pending_apply_snapshot())
1083             {
1084                 bool take_new_snapshot = false;
1085
1086                 // See if we've scheduled a snapshot at this frame
1087                 if ((int64_t)replayer.get_frame_index() == take_snapshot_at_frame_index)
1088                 {
1089                     take_new_snapshot = true;
1090
1091                     take_snapshot_at_frame_index = -1;
1092                 }
1093                 // Check for pausing
1094                 else if (keys_pressed.contains(XK_space))
1095                 {
1096                     keys_pressed.erase(XK_space);
1097
1098                     if (paused_mode)
1099                     {
1100                         console::info("Unpausing\n");
1101
1102                         keys_pressed.erase(XK_space);
1103
1104                         vogl_delete(pSnapshot);
1105                         pSnapshot = NULL;
1106
1107                         paused_mode_frame_index = -1;
1108
1109                         paused_mode = false;
1110                     }
1111                     else
1112                     {
1113                         console::info("Pausing\n");
1114
1115                         paused_mode = true;
1116
1117                         take_new_snapshot = true;
1118                     }
1119                 }
1120
1121                 // Snapshot the current state
1122                 if (take_new_snapshot)
1123                 {
1124                     vogl_delete(pSnapshot);
1125                     pSnapshot = NULL;
1126
1127                     pSnapshot = replayer.snapshot_state();
1128                     if (!pSnapshot)
1129                     {
1130                         vogl_error_printf("%s: Snapshot failed!\n", VOGL_FUNCTION_NAME);
1131                         goto error_exit;
1132                     }
1133
1134                     if (g_command_line_params.get_value_as_bool("debug_test_snapshot_serialization"))
1135                     {
1136                         // Obviously, this crap is only for debugging.
1137                         vogl_memory_blob_manager mem_blob_manager;
1138                         mem_blob_manager.init(cBMFReadWrite);
1139
1140                         json_document temp_json_doc;
1141                         if (!pSnapshot->serialize(*temp_json_doc.get_root(), mem_blob_manager, &replayer.get_trace_gl_ctypes()))
1142                         {
1143                             console::error("%s: Failed serializing state snapshot!\n", VOGL_FUNCTION_NAME);
1144                         }
1145                         else
1146                         {
1147                             uint8_vec ubj_data;
1148                             temp_json_doc.binary_serialize(ubj_data);
1149                             temp_json_doc.binary_deserialize(ubj_data);
1150                             ubj_data.clear();
1151
1152                             vogl_gl_state_snapshot *pNew_snapshot = vogl_new(vogl_gl_state_snapshot);
1153
1154                             if (!pNew_snapshot->deserialize(*temp_json_doc.get_root(), mem_blob_manager, &replayer.get_trace_gl_ctypes()))
1155                             {
1156                                 console::error("%s: Failed deserializing state snapshot!\n", VOGL_FUNCTION_NAME);
1157                             }
1158                             else
1159                             {
1160                                 vogl_delete(pSnapshot);
1161
1162                                 pSnapshot = pNew_snapshot;
1163                             }
1164                         }
1165                     }
1166
1167                     paused_mode_frame_index = replayer.get_frame_index();
1168
1169                     paused_mode = true;
1170                 }
1171             }
1172
1173             // Begin processing the next frame
1174             bool applied_snapshot = false;
1175             vogl_gl_replayer::status_t status = replayer.process_pending_window_resize(&applied_snapshot);
1176
1177             if (status == vogl_gl_replayer::cStatusOK)
1178             {
1179                 if (replayer.get_at_frame_boundary())
1180                 {
1181                     const char *pWindow_name = (sizeof(void *) == sizeof(uint32)) ? "voglreplay 32-bit" : "voglreplay 64-bit";
1182                     dynamic_string window_title(cVarArg, "%s: File: %s Frame %u %s %s", pWindow_name, trace_filename.get_ptr(), replayer.get_frame_index(), paused_mode ? "PAUSED" : "", keyframes.find_sorted(replayer.get_frame_index()) >= 0 ? "(At Keyframe)" : "");
1183                     window.set_title(window_title.get_ptr());
1184                 }
1185
1186                 // At this point, if we're paused the frame snapshot as been applied, and we're just about going to replay the frame's commands.
1187                 if (((applied_snapshot) || (replayer.get_at_frame_boundary())) &&
1188                     (keys_pressed.contains('t') || keys_pressed.contains('j')))
1189                 {
1190                     uint64_t frame_to_trim;
1191                     if (paused_mode)
1192                         frame_to_trim = paused_mode_frame_index;
1193                     else
1194                         frame_to_trim = replayer.get_frame_index();
1195
1196                     dynamic_string trim_name;
1197                     for (uint i = 0; i < 1000000; i++)
1198                     {
1199                         trim_name.format("trim_%06" PRIu64 "_%u", frame_to_trim, i);
1200                         if (!file_utils::does_dir_exist(trim_name.get_ptr()))
1201                             break;
1202                     }
1203
1204                     if (!file_utils::create_directory(trim_name.get_ptr()))
1205                     {
1206                         vogl_error_printf("%s: Failed creating trim directory %s\n", VOGL_FUNCTION_NAME, trim_name.get_ptr());
1207                     }
1208                     else
1209                     {
1210                         vogl_loose_file_blob_manager trim_file_blob_manager;
1211                         trim_file_blob_manager.init(cBMFReadWrite, trim_name.get_ptr());
1212
1213                         dynamic_string trim_filename(trim_name + "/" + trim_name + ".bin");
1214                         dynamic_string snapshot_id;
1215                         uint write_trim_file_flags = vogl_gl_replayer::cWriteTrimFileFromStartOfFrame | (g_command_line_params.get_value_as_bool("no_trim_optimization") ? 0 : vogl_gl_replayer::cWriteTrimFileOptimizeSnapshot);
1216                         if (replayer.write_trim_file(write_trim_file_flags, trim_filename, 1, *pTrace_reader, &snapshot_id))
1217                         {
1218                             dynamic_string json_trim_base_filename(trim_name + "/j" + trim_name);
1219
1220                             char voglreplay_exec_filename[1024];
1221                             file_utils::get_exec_filename(voglreplay_exec_filename, sizeof(voglreplay_exec_filename));
1222
1223                             dynamic_string convert_to_json_spawn_str(cVarArg, "%s --dump \"%s\" \"%s\"", voglreplay_exec_filename, trim_filename.get_ptr(), json_trim_base_filename.get_ptr());
1224                             if (system(convert_to_json_spawn_str.get_ptr()) != 0)
1225                             {
1226                                 vogl_error_printf("%s: Failed running voglreplay: %s\n", VOGL_FUNCTION_NAME, convert_to_json_spawn_str.get_ptr());
1227                             }
1228                             else
1229                             {
1230                                 dynamic_string json_trim_full_filename(trim_name + "/j" + trim_name + "_000000.json");
1231
1232                                 dynamic_string view_json_spawn_str(cVarArg, "np \"%s\" &", json_trim_full_filename.get_ptr());
1233                                 system(view_json_spawn_str.get_ptr());
1234                             }
1235
1236                             if (keys_pressed.contains('j'))
1237                             {
1238                                 dynamic_string workdir(".");
1239                                 file_utils::full_path(workdir);
1240
1241                                 dynamic_string replay_json_spawn_str(cVarArg, "konsole --workdir \"%s\" --hold -e \"%s\" --endless \"%s\" &", workdir.get_ptr(), voglreplay_exec_filename, json_trim_base_filename.get_ptr());
1242                                 system(replay_json_spawn_str.get_ptr());
1243                             }
1244                         }
1245                     }
1246
1247                     keys_pressed.erase('t');
1248                     keys_pressed.erase('j');
1249                 }
1250
1251                 // Now replay the next frame's GL commands up to the swap
1252                 status = replayer.process_frame(*pTrace_reader);
1253             }
1254
1255             if (status == vogl_gl_replayer::cStatusHardFailure)
1256                 break;
1257
1258             if ((slow_mode) && (!paused_mode))
1259                 vogl_sleep(100);
1260
1261             int64_t seek_to_target_frame = -1;
1262             bool seek_to_closest_keyframe = false;
1263             int seek_to_closest_frame_dir_bias = 0;
1264
1265             if (status == vogl_gl_replayer::cStatusAtEOF)
1266             {
1267                 vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_NAME, replayer.get_frame_index());
1268
1269                 // Right after the last swap in the file, either rewind or pause back on the last frame
1270                 if ((paused_mode) && (replayer.get_frame_index()))
1271                 {
1272                     seek_to_target_frame = replayer.get_frame_index() - 1;
1273                     take_snapshot_at_frame_index = -1;
1274                 }
1275                 else
1276                 {
1277                     vogl_printf("Resetting state and rewinding back to frame 0\n");
1278
1279                     replayer.reset_state();
1280
1281                     if (!pTrace_reader->seek_to_frame(0))
1282                     {
1283                         vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_NAME);
1284                         goto error_exit;
1285                     }
1286
1287                     take_snapshot_at_frame_index = -1;
1288                     paused_mode_frame_index = -1;
1289                     paused_mode = false;
1290                 }
1291
1292                 vogl_delete(pSnapshot);
1293                 pSnapshot = NULL;
1294             }
1295             else if (replayer.get_at_frame_boundary() && (!replayer.get_pending_apply_snapshot()))
1296             {
1297                 // Rewind to beginning
1298                 if (keys_pressed.contains('r'))
1299                 {
1300                     bool ctrl = (keys_down.contains(XK_Control_L) || keys_down.contains(XK_Control_R));
1301                     keys_pressed.erase('r');
1302
1303                     vogl_delete(pSnapshot);
1304                     pSnapshot = NULL;
1305
1306                     if ((paused_mode) && (ctrl))
1307                         seek_to_target_frame = paused_mode_frame_index;
1308
1309                     take_snapshot_at_frame_index = -1;
1310                     paused_mode_frame_index = -1;
1311                     paused_mode = false;
1312
1313                     vogl_printf("Resetting state and rewinding back to frame 0\n");
1314
1315                     replayer.reset_state();
1316
1317                     if (!pTrace_reader->seek_to_frame(0))
1318                     {
1319                         vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_NAME);
1320                         goto error_exit;
1321                     }
1322                 }
1323                 // Seek to last frame
1324                 else if (keys_pressed.contains('e'))
1325                 {
1326                     keys_pressed.erase('e');
1327
1328                     if (paused_mode)
1329                     {
1330                         vogl_delete(pSnapshot);
1331                         pSnapshot = NULL;
1332
1333                         paused_mode_frame_index = -1;
1334                         take_snapshot_at_frame_index = -1;
1335
1336                         vogl_printf("Seeking to last frame\n");
1337
1338                         int64_t max_frame_index = pTrace_reader->get_max_frame_index();
1339                         if (max_frame_index < 0)
1340                         {
1341                             vogl_error_printf("%s: Failed determining the total number of trace frames!\n", VOGL_FUNCTION_NAME);
1342                             goto error_exit;
1343                         }
1344
1345                         // "frames" are actually "swaps" to the tracer/replayer, so don't seek to the "last" swap (because no rendering will follow that one), but the one before
1346                         seek_to_target_frame = max_frame_index - 1;
1347                     }
1348                 }
1349                 // Check if paused and process seek related keypresses
1350                 else if ((paused_mode) && (pSnapshot))
1351                 {
1352                     int num_key;
1353                     for (num_key = '0'; num_key <= '9'; num_key++)
1354                     {
1355                         if (keys_pressed.contains(num_key))
1356                         {
1357                             keys_pressed.erase(num_key);
1358                             break;
1359                         }
1360                     }
1361
1362                     if (num_key <= '9')
1363                     {
1364                         int64_t max_frame_index = pTrace_reader->get_max_frame_index();
1365                         if (max_frame_index < 0)
1366                         {
1367                             vogl_error_printf("%s: Failed determining the total number of trace frames!\n", VOGL_FUNCTION_NAME);
1368                             goto error_exit;
1369                         }
1370
1371                         float fraction = ((num_key - '0') + 1) / 11.0f;
1372
1373                         seek_to_target_frame = math::clamp<int64_t>(static_cast<int64_t>(max_frame_index * fraction + .5f), 0, max_frame_index - 1);
1374                         seek_to_closest_keyframe = true;
1375                     }
1376                     else if (keys_pressed.contains(XK_Left) || keys_pressed.contains(XK_Right))
1377                     {
1378                         int dir = keys_pressed.contains(XK_Left) ? -1 : 1;
1379
1380                         bool shift = (keys_down.contains(XK_Shift_L) || keys_down.contains(XK_Shift_R));
1381                         bool ctrl = (keys_down.contains(XK_Control_L) || keys_down.contains(XK_Control_R));
1382
1383                         int mag = 1;
1384                         if ((shift) && (ctrl))
1385                             mag = 500;
1386                         else if (shift)
1387                             mag = 10;
1388                         else if (ctrl)
1389                             mag = 100;
1390
1391                         int rel = dir * mag;
1392
1393                         int64_t target_frame_index = math::maximum<int64_t>(0, paused_mode_frame_index + rel);
1394
1395                         seek_to_target_frame = target_frame_index;
1396
1397                         keys_pressed.erase(XK_Left);
1398                         keys_pressed.erase(XK_Right);
1399
1400                         if ((keyframes.size()) && (keys_down.contains(XK_Alt_L) || keys_down.contains(XK_Alt_R)))
1401                         {
1402                             uint keyframe_array_index = 0;
1403                             for (keyframe_array_index = 1; keyframe_array_index < keyframes.size(); keyframe_array_index++)
1404                                 if ((int64_t)keyframes[keyframe_array_index] > paused_mode_frame_index)
1405                                     break;
1406
1407                             if (dir < 0)
1408                             {
1409                                 if ((paused_mode_frame_index == static_cast<int64_t>(keyframes[keyframe_array_index - 1])) && (keyframe_array_index > 1))
1410                                     keyframe_array_index = keyframe_array_index - 2;
1411                                 else
1412                                     keyframe_array_index = keyframe_array_index - 1;
1413                             }
1414                             else
1415                             {
1416                                 if (keyframe_array_index < keyframes.size())
1417                                 {
1418                                     if ((paused_mode_frame_index == static_cast<int64_t>(keyframes[keyframe_array_index])) && ((keyframe_array_index + 1) < keyframes.size()))
1419                                         keyframe_array_index = keyframe_array_index + 1;
1420                                     //else
1421                                     //   keyframe_array_index = keyframe_array_index;
1422                                 }
1423                                 else
1424                                     keyframe_array_index = keyframe_array_index - 1;
1425                             }
1426
1427                             seek_to_target_frame = keyframes[keyframe_array_index];
1428
1429                             if (mag > 1)
1430                             {
1431                                 if (((dir < 0) && (target_frame_index < seek_to_target_frame)) || (target_frame_index > seek_to_target_frame))
1432                                     seek_to_target_frame = target_frame_index;
1433
1434                                 seek_to_closest_keyframe = true;
1435                                 seek_to_closest_frame_dir_bias = dir;
1436                             }
1437
1438                             console::info("Seeking to keyframe array index %u, target frame %" PRIu64 "\n", keyframe_array_index, seek_to_target_frame);
1439                         }
1440                     }
1441                     // Check for unpause
1442                     else if (keys_pressed.contains(XK_space))
1443                     {
1444                         console::info("Unpausing\n");
1445
1446                         keys_pressed.erase(XK_space);
1447
1448                         vogl_delete(pSnapshot);
1449                         pSnapshot = NULL;
1450
1451                         paused_mode_frame_index = -1;
1452
1453                         paused_mode = false;
1454                     }
1455                     else
1456                     {
1457                         // We're paused so seek back to the command right after the swap we're paused on, apply the paused frame's snapshot, then play the frame over
1458                         status = replayer.begin_applying_snapshot(pSnapshot, false);
1459                         if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
1460                         {
1461                             vogl_error_printf("%s: Failed applying snapshot!\n", VOGL_FUNCTION_NAME);
1462                             goto error_exit;
1463                         }
1464
1465                         pTrace_reader->seek_to_frame(static_cast<uint>(paused_mode_frame_index));
1466                     }
1467                 }
1468             }
1469
1470             // Seek to target frame
1471             if (seek_to_target_frame != -1)
1472             {
1473                 vogl_delete(pSnapshot);
1474                 pSnapshot = NULL;
1475                 paused_mode_frame_index = -1;
1476
1477                 if ((int64_t)replayer.get_frame_index() == seek_to_target_frame)
1478                     take_snapshot_at_frame_index = seek_to_target_frame;
1479                 else
1480                 {
1481                     uint keyframe_array_index = 0;
1482                     for (keyframe_array_index = 1; keyframe_array_index < keyframes.size(); keyframe_array_index++)
1483                         if (static_cast<int64_t>(keyframes[keyframe_array_index]) > seek_to_target_frame)
1484                             break;
1485
1486                     if ((!keyframes.is_empty()) && (static_cast<int64_t>(keyframes[keyframe_array_index - 1]) <= seek_to_target_frame))
1487                     {
1488                         int keyframe_array_index_to_use = keyframe_array_index - 1;
1489
1490                         if (seek_to_closest_keyframe)
1491                         {
1492                             if (!seek_to_closest_frame_dir_bias)
1493                             {
1494                                 if ((keyframe_array_index_to_use + 1U) < keyframes.size())
1495                                 {
1496                                     if (llabs(static_cast<int64_t>(keyframes[keyframe_array_index_to_use + 1]) - seek_to_target_frame) < llabs(static_cast<int64_t>(keyframes[keyframe_array_index_to_use]) - seek_to_target_frame))
1497                                     {
1498                                         keyframe_array_index_to_use++;
1499                                     }
1500                                 }
1501                             }
1502                             else if (seek_to_closest_frame_dir_bias > 0)
1503                             {
1504                                 if ((keyframe_array_index_to_use + 1U) < keyframes.size())
1505                                 {
1506                                     if (static_cast<int64_t>(keyframes[keyframe_array_index_to_use]) <= seek_to_target_frame)
1507                                     {
1508                                         keyframe_array_index_to_use++;
1509                                     }
1510                                 }
1511                             }
1512                         }
1513
1514                         int64_t keyframe_index = keyframes[keyframe_array_index_to_use];
1515
1516                         if (seek_to_closest_keyframe)
1517                             seek_to_target_frame = keyframe_index;
1518
1519                         vogl_debug_printf("Seeking to target frame %" PRIu64 "\n", seek_to_target_frame);
1520
1521                         dynamic_string keyframe_filename(cVarArg, "%s_%06" PRIu64 ".bin", keyframe_base_filename.get_ptr(), keyframe_index);
1522
1523                         vogl_gl_state_snapshot *pKeyframe_snapshot = read_state_snapshot_from_trace(keyframe_filename);
1524                         if (!pKeyframe_snapshot)
1525                             goto error_exit;
1526
1527                         bool delete_snapshot_after_applying = true;
1528                         if (seek_to_target_frame == keyframe_index)
1529                             delete_snapshot_after_applying = false;
1530
1531                         status = replayer.begin_applying_snapshot(pKeyframe_snapshot, delete_snapshot_after_applying);
1532                         if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
1533                         {
1534                             vogl_error_printf("%s: Failed applying snapshot!\n", VOGL_FUNCTION_NAME);
1535                             goto error_exit;
1536                         }
1537
1538                         pKeyframe_snapshot->set_frame_index(static_cast<uint>(keyframe_index));
1539
1540                         if (!pTrace_reader->seek_to_frame(static_cast<uint>(keyframe_index)))
1541                         {
1542                             vogl_error_printf("%s: Failed seeking to keyframe!\n", VOGL_FUNCTION_NAME);
1543                             goto error_exit;
1544                         }
1545
1546                         if (seek_to_target_frame == keyframe_index)
1547                         {
1548                             pSnapshot = pKeyframe_snapshot;
1549                             paused_mode_frame_index = seek_to_target_frame;
1550                         }
1551                         else
1552                             take_snapshot_at_frame_index = seek_to_target_frame;
1553                     }
1554                     else
1555                     {
1556                         if (seek_to_target_frame < static_cast<int64_t>(replayer.get_frame_index()))
1557                         {
1558                             replayer.reset_state();
1559                             pTrace_reader->seek_to_frame(0);
1560                         }
1561
1562                         take_snapshot_at_frame_index = seek_to_target_frame;
1563                     }
1564                 }
1565             }
1566         }
1567         else // !interactive_mode
1568         {
1569             tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "!Interactive");
1570
1571             if (replayer.get_at_frame_boundary())
1572             {
1573                 if (trim_frames.size())
1574                 {
1575                     bool should_trim = false;
1576                     uint tf = 0;
1577                     uint len = 1;
1578
1579                     for (tf = 0; tf < trim_frames.size(); tf++)
1580                     {
1581                         if (trim_lens.size())
1582                         {
1583                             if (trim_lens.size() < trim_frames.size())
1584                                 len = trim_lens[0];
1585                             else
1586                                 len = trim_lens[tf];
1587                         }
1588                         len = math::maximum(len, 1U);
1589
1590                         if (multitrim_mode)
1591                         {
1592                             if ((replayer.get_frame_index() >= trim_frames[tf]) && (replayer.get_frame_index() < (trim_frames[tf] + math::maximum(len, 1U))))
1593                             {
1594                                 should_trim = true;
1595                                 break;
1596                             }
1597                         }
1598                         else
1599                         {
1600                             if (replayer.get_frame_index() == trim_frames[tf])
1601                             {
1602                                 should_trim = true;
1603                                 break;
1604                             }
1605                         }
1606                     }
1607
1608                     if (multitrim_mode)
1609                     {
1610                         if (should_trim)
1611                         {
1612                             if (multitrim_frames_remaining)
1613                             {
1614                                 should_trim = false;
1615                             }
1616                             else
1617                             {
1618                                 multitrim_frames_remaining = multitrim_interval;
1619                             }
1620
1621                             multitrim_frames_remaining--;
1622                         }
1623                         else
1624                         {
1625                             multitrim_frames_remaining = 0;
1626                         }
1627                     }
1628                     //printf("multitrim_interval: %u %u\n", multitrim_frames_remaining, multitrim_interval);
1629
1630                     if (should_trim)
1631                     {
1632                         dynamic_string filename;
1633
1634                         if ((multitrim_mode) || (trim_filenames.size() < trim_frames.size()))
1635                         {
1636                             filename = trim_filenames[0];
1637
1638                             if ((multitrim_mode) || (trim_frames.size() > 1))
1639                             {
1640                                 dynamic_string drive, dir, fname, ext;
1641                                 file_utils::split_path(filename.get_ptr(), &drive, &dir, &fname, &ext);
1642
1643                                 dynamic_string new_fname(cVarArg, "%s_%06u", fname.get_ptr(), replayer.get_frame_index());
1644
1645                                 file_utils::combine_path_and_extension(filename, &drive, &dir, &new_fname, &ext);
1646                             }
1647                         }
1648                         else
1649                         {
1650                             filename = trim_filenames[tf];
1651                         }
1652
1653                         dynamic_string trim_path(file_utils::get_pathname(filename.get_ptr()));
1654                         trim_file_blob_manager.set_path(trim_path);
1655
1656                         file_utils::create_directories(trim_path, false);
1657
1658                         uint write_trim_file_flags = vogl_gl_replayer::cWriteTrimFileFromStartOfFrame | (g_command_line_params.get_value_as_bool("no_trim_optimization") ? 0 : vogl_gl_replayer::cWriteTrimFileOptimizeSnapshot);
1659                         if (!replayer.write_trim_file(write_trim_file_flags, filename, multitrim_mode ? 1 : len, *pTrace_reader))
1660                             goto error_exit;
1661
1662                         num_trim_files_written++;
1663
1664                         if (!multitrim_mode)
1665                         {
1666                             if (num_trim_files_written == trim_frames.size())
1667                             {
1668                                 vogl_message_printf("%s: All requested trim files written, stopping replay\n", VOGL_FUNCTION_NAME);
1669                                 goto normal_exit;
1670                             }
1671                         }
1672                     }
1673
1674                     if (multitrim_mode)
1675                     {
1676                         uint64_t next_frame_index = replayer.get_frame_index() + 1;
1677
1678                         if (next_frame_index > highest_frame_to_trim)
1679                         {
1680                             vogl_message_printf("%s: No more frames to trim, stopping replay\n", VOGL_FUNCTION_NAME);
1681                             goto normal_exit;
1682                         }
1683                     }
1684                 }
1685
1686                 if ((!pSnapshot) && (loop_frame != -1) && (static_cast<int64_t>(replayer.get_frame_index()) == loop_frame))
1687                 {
1688                     vogl_debug_printf("%s: Capturing replayer state at start of frame %u\n", VOGL_FUNCTION_NAME, replayer.get_frame_index());
1689
1690                     pSnapshot = replayer.snapshot_state();
1691
1692                     if (pSnapshot)
1693                     {
1694                         vogl_printf("Snapshot succeeded\n");
1695
1696                         snapshot_loop_start_frame = pTrace_reader->get_cur_frame();
1697                         snapshot_loop_end_frame = pTrace_reader->get_cur_frame() + loop_len;
1698
1699                         if (draw_kill_max_thresh > 0)
1700                         {
1701                             replayer.set_frame_draw_counter_kill_threshold(0);
1702                         }
1703
1704                         vogl_debug_printf("%s: Loop start: %" PRIi64 " Loop end: %" PRIi64 "\n", VOGL_FUNCTION_NAME, snapshot_loop_start_frame, snapshot_loop_end_frame);
1705                     }
1706                     else
1707                     {
1708                         vogl_error_printf("Snapshot failed!\n");
1709
1710                         loop_frame = -1;
1711                     }
1712                 }
1713             }
1714
1715             vogl_gl_replayer::status_t status = replayer.process_pending_window_resize();
1716             if (status == vogl_gl_replayer::cStatusOK)
1717             {
1718                 for (;;)
1719                 {
1720                     status = replayer.process_next_packet(*pTrace_reader);
1721
1722                     if ((status != vogl_gl_replayer::cStatusHardFailure) && (status != vogl_gl_replayer::cStatusAtEOF))
1723                     {
1724                         if ((write_snapshot_index >= 0) && (write_snapshot_index == replayer.get_last_processed_call_counter()))
1725                         {
1726                             dynamic_string filename(write_snapshot_filename);
1727
1728                             dynamic_string write_snapshot_path(file_utils::get_pathname(filename.get_ptr()));
1729                             trim_file_blob_manager.init(cBMFReadWrite, write_snapshot_path.get_ptr());
1730
1731                             file_utils::create_directories(write_snapshot_path, false);
1732
1733                             pSnapshot = replayer.snapshot_state();
1734
1735                             if (pSnapshot)
1736                             {
1737                                 vogl_printf("Snapshot succeeded at call counter %" PRIu64 "\n", replayer.get_last_processed_call_counter());
1738
1739                                 vogl_null_blob_manager null_blob_manager;
1740                                 null_blob_manager.init(cBMFReadWrite);
1741
1742                                 json_document doc;
1743                                 vogl_blob_manager *pBlob_manager = g_command_line_params.get_value_as_bool("write_snapshot_blobs") ? static_cast<vogl_blob_manager *>(&trim_file_blob_manager) : static_cast<vogl_blob_manager *>(&null_blob_manager);
1744                                 if (!pSnapshot->serialize(*doc.get_root(), *pBlob_manager, &replayer.get_trace_gl_ctypes()))
1745                                 {
1746                                     vogl_error_printf("Failed serializing state snapshot document!\n");
1747                                 }
1748                                 else if (!doc.serialize_to_file(filename.get_ptr(), true))
1749                                 {
1750                                     vogl_error_printf("Failed writing state snapshot to file \"%s\"!\n", filename.get_ptr());
1751                                 }
1752                                 else
1753                                 {
1754                                     vogl_printf("Successfully wrote JSON snapshot to file \"%s\"\n", filename.get_ptr());
1755                                 }
1756
1757                                 vogl_delete(pSnapshot);
1758                                 pSnapshot = NULL;
1759                             }
1760                             else
1761                             {
1762                                 vogl_error_printf("Snapshot failed!\n");
1763                             }
1764
1765                             goto normal_exit;
1766                         }
1767                         else if ((trim_call_index >= 0) && (trim_call_index == replayer.get_last_processed_call_counter()))
1768                         {
1769                             dynamic_string filename(trim_filenames[0]);
1770
1771                             dynamic_string trim_path(file_utils::get_pathname(filename.get_ptr()));
1772                             trim_file_blob_manager.set_path(trim_path);
1773
1774                             file_utils::create_directories(trim_path, false);
1775
1776                             if (!replayer.write_trim_file(0, filename, trim_lens.size() ? trim_lens[0] : 1, *pTrace_reader, NULL))
1777                                 goto error_exit;
1778
1779                             vogl_message_printf("%s: Trim file written, stopping replay\n", VOGL_FUNCTION_NAME);
1780                             goto normal_exit;
1781                         }
1782                     }
1783
1784                     if ((status == vogl_gl_replayer::cStatusNextFrame) || (status == vogl_gl_replayer::cStatusResizeWindow) || (status == vogl_gl_replayer::cStatusAtEOF) || (status == vogl_gl_replayer::cStatusHardFailure))
1785                         break;
1786                 }
1787             }
1788
1789             if (status == vogl_gl_replayer::cStatusHardFailure)
1790                 break;
1791
1792             if (status == vogl_gl_replayer::cStatusAtEOF)
1793             {
1794                 vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_NAME, replayer.get_frame_index());
1795             }
1796
1797             if ((replayer.get_at_frame_boundary()) && (pSnapshot) && (loop_count > 0) && ((pTrace_reader->get_cur_frame() == snapshot_loop_end_frame) || (status == vogl_gl_replayer::cStatusAtEOF)))
1798             {
1799                 status = replayer.begin_applying_snapshot(pSnapshot, false);
1800                 if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
1801                     goto error_exit;
1802
1803                 pTrace_reader->seek_to_frame(static_cast<uint>(snapshot_loop_start_frame));
1804
1805                 if (draw_kill_max_thresh > 0)
1806                 {
1807                     int64_t thresh = replayer.get_frame_draw_counter_kill_threshold();
1808                     thresh += 1;
1809                     if (thresh >= draw_kill_max_thresh)
1810                         thresh = 0;
1811                     replayer.set_frame_draw_counter_kill_threshold(thresh);
1812
1813                     vogl_debug_printf("%s: Applying snapshot and seeking back to frame %" PRIi64 " draw kill thresh %" PRIu64 "\n", VOGL_FUNCTION_NAME, snapshot_loop_start_frame, thresh);
1814                 }
1815                 else
1816                     vogl_debug_printf("%s: Applying snapshot and seeking back to frame %" PRIi64 "\n", VOGL_FUNCTION_NAME, snapshot_loop_start_frame);
1817
1818                 loop_count--;
1819             }
1820             else
1821             {
1822                 bool print_progress = (status == vogl_gl_replayer::cStatusAtEOF) || ((replayer.get_at_frame_boundary()) && ((replayer.get_frame_index() % 100) == 0));
1823                 if (print_progress)
1824                 {
1825                     if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER)
1826                     {
1827                         vogl_binary_trace_file_reader &binary_trace_reader = *static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get());
1828
1829                         vogl_printf("Replay now at frame index %d, trace file offet %" PRIu64 ", GL call counter %" PRIu64 ", %3.2f%% percent complete\n",
1830                                    replayer.get_frame_index(),
1831                                    binary_trace_reader.get_cur_file_ofs(),
1832                                    replayer.get_last_parsed_call_counter(),
1833                                    binary_trace_reader.get_trace_file_size() ? (binary_trace_reader.get_cur_file_ofs() * 100.0f) / binary_trace_reader.get_trace_file_size() : 0);
1834                     }
1835                 }
1836
1837                 if (status == vogl_gl_replayer::cStatusAtEOF)
1838                 {
1839                     if (!endless_mode)
1840                     {
1841                         double time_since_start = tm.get_elapsed_secs();
1842
1843                         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);
1844
1845                         break;
1846                     }
1847
1848                     vogl_printf("Resetting state and rewinding back to frame 0\n");
1849
1850                     replayer.reset_state();
1851
1852                     if (!pTrace_reader->seek_to_frame(0))
1853                     {
1854                         vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_NAME);
1855                         goto error_exit;
1856                     }
1857                 }
1858             }
1859         } // interactive_mode
1860
1861         telemetry_tick();
1862     }
1863
1864     if (trim_frames.size())
1865     {
1866         console::message("Wrote %u trim file(s)\n", num_trim_files_written);
1867
1868         if (((multitrim_mode) && (!num_trim_files_written)) ||
1869             ((!multitrim_mode) && (num_trim_files_written != trim_frames.size())))
1870             console::warning("Requested %u trim frames, but was only able to write %u trim frames (one or more -trim_frames must have been too large)\n", trim_frames.size(), num_trim_files_written);
1871     }
1872
1873 normal_exit:
1874
1875     if (g_command_line_params.get_value_as_bool("pause_on_exit") && (window.is_opened()))
1876     {
1877         vogl_printf("Press a key to continue.\n");
1878
1879         for (;;)
1880         {
1881             if (vogl_kbhit())
1882                 break;
1883
1884             bool exit_flag = false;
1885             while (!exit_flag && X11_Pending(window.get_display()))
1886             {
1887                 XEvent newEvent;
1888                 XNextEvent(window.get_display(), &newEvent);
1889
1890                 switch (newEvent.type)
1891                 {
1892                     case KeyPress:
1893                     case DestroyNotify:
1894                     {
1895                         exit_flag = true;
1896                         break;
1897                     }
1898                     case ClientMessage:
1899                     {
1900                         if (newEvent.xclient.data.l[0] == (int)wmDeleteMessage)
1901                             exit_flag = true;
1902                         break;
1903                     }
1904                     default:
1905                         break;
1906                 }
1907             }
1908             if (exit_flag)
1909                 break;
1910             vogl_sleep(50);
1911         }
1912     }
1913
1914     return true;
1915
1916 error_exit:
1917     return false;
1918 }
1919
1920 //----------------------------------------------------------------------------------------------------------------------
1921 // tool_dump_mode
1922 //----------------------------------------------------------------------------------------------------------------------
1923 static bool tool_dump_mode()
1924 {
1925     VOGL_FUNC_TRACER
1926
1927     dynamic_string input_trace_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
1928     if (input_trace_filename.is_empty())
1929     {
1930         vogl_error_printf("Must specify filename of input binary trace file!\n");
1931         return false;
1932     }
1933
1934     dynamic_string output_base_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
1935     if (output_base_filename.is_empty())
1936     {
1937         vogl_error_printf("Must specify base filename of output JSON/blob files!\n");
1938         return false;
1939     }
1940
1941     vogl_loose_file_blob_manager output_file_blob_manager;
1942
1943     dynamic_string output_trace_path(file_utils::get_pathname(output_base_filename.get_ptr()));
1944     vogl_debug_printf("%s: Output trace path: %s\n", VOGL_FUNCTION_NAME, output_trace_path.get_ptr());
1945     output_file_blob_manager.init(cBMFReadWrite, output_trace_path.get_ptr());
1946
1947     file_utils::create_directories(output_trace_path, false);
1948
1949     dynamic_string actual_input_trace_filename;
1950     vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(input_trace_filename, actual_input_trace_filename, g_command_line_params.get_value_as_string_or_empty("loose_file_path").get_ptr()));
1951     if (!pTrace_reader.get())
1952     {
1953         vogl_error_printf("%s: Failed opening input trace file \"%s\"\n", VOGL_FUNCTION_NAME, input_trace_filename.get_ptr());
1954         return false;
1955     }
1956
1957     const bool full_verification = g_command_line_params.get_value_as_bool("verify");
1958
1959     vogl_ctypes trace_gl_ctypes;
1960     trace_gl_ctypes.init(pTrace_reader->get_sof_packet().m_pointer_sizes);
1961
1962     dynamic_string archive_name;
1963     if (pTrace_reader->get_archive_blob_manager().is_initialized())
1964     {
1965         dynamic_string archive_filename(output_base_filename.get_ptr());
1966         archive_filename += "_trace_archive.zip";
1967
1968         archive_name = file_utils::get_filename(archive_filename.get_ptr());
1969
1970         vogl_message_printf("Writing trace archive \"%s\", size %" PRIu64 " bytes\n", archive_filename.get_ptr(), pTrace_reader->get_archive_blob_manager().get_archive_size());
1971
1972         cfile_stream archive_stream;
1973         if (!archive_stream.open(archive_filename.get_ptr(), cDataStreamWritable | cDataStreamSeekable))
1974         {
1975             vogl_error_printf("%s: Failed opening output trace archive \"%s\"!\n", VOGL_FUNCTION_NAME, archive_filename.get_ptr());
1976             return false;
1977         }
1978
1979         if (!pTrace_reader->get_archive_blob_manager().write_archive_to_stream(archive_stream))
1980         {
1981             vogl_error_printf("%s: Failed writing to output trace archive \"%s\"!\n", VOGL_FUNCTION_NAME, archive_filename.get_ptr());
1982             return false;
1983         }
1984
1985         if (!archive_stream.close())
1986         {
1987             vogl_error_printf("%s: Failed writing to output trace archive \"%s\"!\n", VOGL_FUNCTION_NAME, archive_filename.get_ptr());
1988             return false;
1989         }
1990     }
1991
1992     vogl_trace_packet gl_packet_cracker(&trace_gl_ctypes);
1993
1994     uint cur_file_index = 0;
1995     uint cur_frame_index = 0;
1996     uint64_t cur_packet_index = 0;
1997     VOGL_NOTE_UNUSED(cur_packet_index);
1998     json_document cur_doc;
1999
2000     {
2001         json_node &meta_node = cur_doc.get_root()->add_object("meta");
2002         meta_node.add_key_value("cur_frame", cur_frame_index);
2003
2004         json_node &sof_node = cur_doc.get_root()->add_object("sof");
2005         sof_node.add_key_value("pointer_sizes", pTrace_reader->get_sof_packet().m_pointer_sizes);
2006         sof_node.add_key_value("version", to_hex_string(pTrace_reader->get_sof_packet().m_version));
2007         if (!archive_name.is_empty())
2008             sof_node.add_key_value("archive_filename", archive_name);
2009
2010         json_node &uuid_array = sof_node.add_array("uuid");
2011         for (uint i = 0; i < VOGL_ARRAY_SIZE(pTrace_reader->get_sof_packet().m_uuid); i++)
2012             uuid_array.add_value(pTrace_reader->get_sof_packet().m_uuid[i]);
2013     }
2014
2015     // TODO: Automatically dump binary snapshot file to text?
2016     // Right now we can't afford to do it at trace time, it takes too much memory.
2017
2018     json_node *pPacket_array = &cur_doc.get_root()->add_array("packets");
2019
2020     bool flush_current_document = false;
2021
2022     bool status = true;
2023
2024     for (;;)
2025     {
2026         uint64_t cur_packet_ofs = (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER) ? static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get())->get_cur_file_ofs() : 0;
2027
2028         vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
2029
2030         if (read_status == vogl_trace_file_reader::cEOF)
2031         {
2032             vogl_message_printf("At trace file EOF\n");
2033             break;
2034         }
2035         else if (read_status != vogl_trace_file_reader::cOK)
2036         {
2037             vogl_error_printf("Failed reading from trace file, or file size was too small\n");
2038
2039             status = false;
2040             break;
2041         }
2042
2043         if (pTrace_reader->get_packet_type() != cTSPTGLEntrypoint)
2044         {
2045             if (pTrace_reader->get_packet_type() == cTSPTSOF)
2046             {
2047                 vogl_error_printf("Encountered redundant SOF packet!\n");
2048                 status = false;
2049             }
2050
2051             json_node *pMeta_node = cur_doc.get_root()->find_child_object("meta");
2052             if (pMeta_node)
2053                 pMeta_node->add_key_value("eof", (pTrace_reader->get_packet_type() == cTSPTEOF) ? 1 : 2);
2054
2055             break;
2056         }
2057
2058         if (flush_current_document)
2059         {
2060             flush_current_document = false;
2061
2062             dynamic_string output_filename(cVarArg, "%s_%06u.json", output_base_filename.get_ptr(), cur_file_index);
2063             vogl_message_printf("Writing file: \"%s\"\n", output_filename.get_ptr());
2064
2065             if (!cur_doc.serialize_to_file(output_filename.get_ptr(), true))
2066             {
2067                 vogl_error_printf("%s: Failed serializing JSON document to file %s\n", VOGL_FUNCTION_NAME, output_filename.get_ptr());
2068
2069                 status = false;
2070                 break;
2071             }
2072
2073             if (cur_doc.get_root()->check_for_duplicate_keys())
2074                 vogl_warning_printf("%s: JSON document %s has nodes with duplicate keys, this document may not be readable by some JSON parsers\n", VOGL_FUNCTION_NAME, output_filename.get_ptr());
2075
2076             cur_file_index++;
2077
2078             pPacket_array = NULL;
2079             cur_doc.clear();
2080
2081             json_node &note_node = cur_doc.get_root()->add_object("meta");
2082             note_node.add_key_value("cur_frame", cur_frame_index);
2083
2084             json_node &uuid_array = note_node.add_array("uuid");
2085             for (uint i = 0; i < VOGL_ARRAY_SIZE(pTrace_reader->get_sof_packet().m_uuid); i++)
2086                 uuid_array.add_value(pTrace_reader->get_sof_packet().m_uuid[i]);
2087
2088             pPacket_array = &cur_doc.get_root()->add_array("packets");
2089         }
2090
2091         const vogl_trace_gl_entrypoint_packet &gl_packet = pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
2092         const char *pFunc_name = g_vogl_entrypoint_descs[gl_packet.m_entrypoint_id].m_pName;
2093         VOGL_NOTE_UNUSED(pFunc_name);
2094
2095         if (g_command_line_params.get_value_as_bool("debug"))
2096         {
2097             vogl_debug_printf("Trace packet: File offset: %" PRIu64 ", Total size %u, Param size: %u, Client mem size %u, Name value size %u, call %" PRIu64 ", ID: %s (%u), Thread ID: 0x%" PRIX64 ", Trace Context: 0x%" PRIX64 "\n",
2098                              cur_packet_ofs,
2099                              gl_packet.m_size,
2100                              gl_packet.m_param_size,
2101                              gl_packet.m_client_memory_size,
2102                              gl_packet.m_name_value_map_size,
2103                              gl_packet.m_call_counter,
2104                              g_vogl_entrypoint_descs[gl_packet.m_entrypoint_id].m_pName,
2105                              gl_packet.m_entrypoint_id,
2106                              gl_packet.m_thread_id,
2107                              gl_packet.m_context_handle);
2108         }
2109
2110         if (!gl_packet_cracker.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), true))
2111         {
2112             vogl_error_printf("Failed deserializing GL entrypoint packet. Trying to continue parsing the file, this may die!\n");
2113
2114             //status = false;
2115             //break;
2116             continue;
2117         }
2118
2119         json_node &new_node = pPacket_array->add_object();
2120
2121         vogl_trace_packet::json_serialize_params serialize_params;
2122         serialize_params.m_output_basename = file_utils::get_filename(output_base_filename.get_ptr());
2123         serialize_params.m_pBlob_manager = &output_file_blob_manager;
2124         serialize_params.m_cur_frame = cur_file_index;
2125         serialize_params.m_write_debug_info = g_command_line_params.get_value_as_bool("write_debug_info");
2126         if (!gl_packet_cracker.json_serialize(new_node, serialize_params))
2127         {
2128             vogl_error_printf("JSON serialization failed!\n");
2129
2130             status = false;
2131             break;
2132         }
2133
2134         if (full_verification)
2135         {
2136 #if 0
2137                         if (!strcmp(pFunc_name, "glClearColor"))
2138                         {
2139                                 vogl_debug_break();
2140                         }
2141 #endif
2142
2143             vogl::vector<char> new_node_as_text;
2144             new_node.serialize(new_node_as_text, true, 0);
2145
2146 #if 0
2147                         if (new_node_as_text.size())
2148                         {
2149                                 printf("%s\n", new_node_as_text.get_ptr());
2150                         }
2151 #endif
2152
2153             json_document round_tripped_node;
2154             if (!round_tripped_node.deserialize(new_node_as_text.get_ptr()) || !round_tripped_node.get_root())
2155             {
2156                 vogl_error_printf("Failed verifying serialized JSON data (step 1)!\n");
2157
2158                 status = false;
2159                 break;
2160             }
2161             else
2162             {
2163                 vogl_trace_packet temp_cracker(&trace_gl_ctypes);
2164                 bool success = temp_cracker.json_deserialize(*round_tripped_node.get_root(), "<memory>", &output_file_blob_manager);
2165                 if (!success)
2166                 {
2167                     vogl_error_printf("Failed verifying serialized JSON data (step 2)!\n");
2168
2169                     status = false;
2170                     break;
2171                 }
2172                 else
2173                 {
2174                     success = gl_packet_cracker.compare(temp_cracker, false);
2175                     if (!success)
2176                     {
2177                         vogl_error_printf("Failed verifying serialized JSON data (step 3)!\n");
2178
2179                         status = false;
2180                         break;
2181                     }
2182                     else
2183                     {
2184                         dynamic_stream dyn_stream;
2185
2186                         success = temp_cracker.serialize(dyn_stream);
2187                         if (!success)
2188                         {
2189                             vogl_error_printf("Failed verifying serialized JSON data (step 4)!\n");
2190
2191                             status = false;
2192                             break;
2193                         }
2194                         else
2195                         {
2196                             vogl_trace_packet temp_cracker2(&trace_gl_ctypes);
2197                             success = temp_cracker2.deserialize(static_cast<const uint8 *>(dyn_stream.get_ptr()), static_cast<uint32>(dyn_stream.get_size()), true);
2198                             if (!success)
2199                             {
2200                                 vogl_error_printf("Failed verifying serialized JSON data (step 5)!\n");
2201
2202                                 status = false;
2203                                 break;
2204                             }
2205                             success = gl_packet_cracker.compare(temp_cracker2, true);
2206                             if (!success)
2207                             {
2208                                 vogl_error_printf("Failed verifying serialized JSON data (step 6)!\n");
2209
2210                                 status = false;
2211                                 break;
2212                             }
2213                             else
2214                             {
2215                                 uint64_t binary_serialized_size = dyn_stream.get_size();
2216                                 if (binary_serialized_size != pTrace_reader->get_packet_buf().size())
2217                                 {
2218                                     vogl_error_printf("Round-tripped binary serialized size differs from original packet's' size (step 7)!\n");
2219
2220                                     status = false;
2221                                     break;
2222                                 }
2223                                 else
2224                                 {
2225 #if 0
2226                                                                         // This is excessive- the key value map fields may be binary serialized in different orders
2227                                                                         // TODO: maybe fix the key value map class so it serializes in a stable order (independent of hash table construction)?
2228                                                                         const uint8 *p = static_cast<const uint8 *>(dyn_stream.get_ptr());
2229                                                                         const uint8 *q = static_cast<const uint8 *>(pTrace_reader->get_packet_buf().get_ptr());
2230                                                                         if (memcmp(p, q, binary_serialized_size) != 0)
2231                                                                         {
2232                                                                                 file_utils::write_buf_to_file("p.bin", p, binary_serialized_size);
2233                                                                                 file_utils::write_buf_to_file("q.bin", q, binary_serialized_size);
2234                                                                                 vogl_error_printf("Round-tripped binary serialized data differs from original packet's data (step 7)!\n");
2235
2236                                                                                 status = false;
2237                                                                                 break;
2238                                                                         }
2239 #endif
2240                                 }
2241                             }
2242                         }
2243                     }
2244                 }
2245             }
2246         }
2247
2248         if (vogl_is_swap_buffers_entrypoint(static_cast<gl_entrypoint_id_t>(gl_packet.m_entrypoint_id)))
2249         {
2250             flush_current_document = true;
2251             cur_frame_index++;
2252         }
2253
2254         if (cur_doc.get_root()->size() >= 1 * 1000 * 1000)
2255         {
2256             // TODO: Support replaying dumps like this, or fix the code to serialize the text as it goes.
2257             vogl_error_printf("Haven't encountered a SwapBuffers() call in over 1000000 GL calls, dumping current in-memory JSON document to disk to avoid running out of memory. This JSON dump may not be replayable, but writing it anyway.\n");
2258             flush_current_document = true;
2259         }
2260     }
2261
2262     json_node *pMeta_node = cur_doc.get_root()->find_child_object("meta");
2263
2264     if (pMeta_node)
2265     {
2266         if (!pMeta_node->has_key("eof"))
2267             pMeta_node->add_key_value("eof", 2);
2268
2269         dynamic_string output_filename(cVarArg, "%s_%06u.json", output_base_filename.get_ptr(), cur_file_index);
2270         vogl_message_printf("Writing file: \"%s\"\n", output_filename.get_ptr());
2271
2272         if (!cur_doc.serialize_to_file(output_filename.get_ptr(), true))
2273         {
2274             vogl_error_printf("%s: Failed serializing JSON document to file %s\n", VOGL_FUNCTION_NAME, output_filename.get_ptr());
2275
2276             status = false;
2277         }
2278
2279         if (cur_doc.get_root()->check_for_duplicate_keys())
2280             vogl_warning_printf("%s: JSON document %s has nodes with duplicate keys, this document may not be readable by some JSON parsers\n", VOGL_FUNCTION_NAME, output_filename.get_ptr());
2281
2282         cur_file_index++;
2283     }
2284
2285     if (!status)
2286         vogl_error_printf("Failed dumping binary trace to JSON files starting with filename prefix \"%s\" (but wrote as much as possible)\n", output_base_filename.get_ptr());
2287     else
2288         vogl_message_printf("Successfully dumped binary trace to %u JSON file(s) starting with filename prefix \"%s\"\n", cur_file_index, output_base_filename.get_ptr());
2289
2290     return status;
2291 }
2292
2293 //----------------------------------------------------------------------------------------------------------------------
2294 // tool_parse_mode
2295 //----------------------------------------------------------------------------------------------------------------------
2296 static bool tool_parse_mode()
2297 {
2298     VOGL_FUNC_TRACER
2299
2300     dynamic_string input_base_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
2301     if (input_base_filename.is_empty())
2302     {
2303         vogl_error_printf("Must specify filename of input JSON/blob trace files!\n");
2304         return false;
2305     }
2306
2307     dynamic_string actual_input_filename;
2308     vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(input_base_filename, actual_input_filename, g_command_line_params.get_value_as_string_or_empty("loose_file_path").get_ptr()));
2309     if (!pTrace_reader.get())
2310         return false;
2311
2312     dynamic_string output_trace_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
2313     if (output_trace_filename.is_empty())
2314     {
2315         vogl_error_printf("Must specify full filename of output binary trace file!\n");
2316         return false;
2317     }
2318
2319     file_utils::create_directories(output_trace_filename, true);
2320
2321     if (file_utils::add_default_extension(output_trace_filename, ".bin"))
2322         vogl_message_printf("Trim output filename doesn't have an extension, appending \".bin\" to the filename\n");
2323
2324     // TODO: Refactor this, I think we're going to write the actual ctypes and entrpoint descs, etc. into the trace archive next.
2325     vogl_ctypes trace_ctypes;
2326     trace_ctypes.init(pTrace_reader->get_sof_packet().m_pointer_sizes);
2327
2328     vogl_trace_file_writer trace_writer(&trace_ctypes);
2329     if (!trace_writer.open(output_trace_filename.get_ptr(), NULL, true, false, pTrace_reader->get_sof_packet().m_pointer_sizes))
2330     {
2331         vogl_error_printf("Unable to create file \"%s\"!\n", output_trace_filename.get_ptr());
2332         return false;
2333     }
2334
2335     if (pTrace_reader->get_archive_blob_manager().is_initialized())
2336     {
2337         dynamic_string_array blob_files(pTrace_reader->get_archive_blob_manager().enumerate());
2338         for (uint i = 0; i < blob_files.size(); i++)
2339         {
2340             if (blob_files[i] == VOGL_TRACE_ARCHIVE_FRAME_FILE_OFFSETS_FILENAME)
2341                 continue;
2342
2343             vogl_message_printf("Adding blob file %s to output trace archive\n", blob_files[i].get_ptr());
2344
2345             uint8_vec blob_data;
2346             if (pTrace_reader->get_archive_blob_manager().get(blob_files[i], blob_data))
2347             {
2348                 if (trace_writer.get_trace_archive()->add_buf_using_id(blob_data.get_ptr(), blob_data.size(), blob_files[i]).is_empty())
2349                 {
2350                     vogl_error_printf("%s: Failed writing blob data %s to output trace archive!\n", VOGL_FUNCTION_NAME, blob_files[i].get_ptr());
2351                     return false;
2352                 }
2353             }
2354             else
2355             {
2356                 vogl_error_printf("%s: Failed reading blob data %s from trace archive!\n", VOGL_FUNCTION_NAME, blob_files[i].get_ptr());
2357                 return false;
2358             }
2359         }
2360     }
2361
2362     for (;;)
2363     {
2364         vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
2365
2366         if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
2367         {
2368             vogl_error_printf("Failed reading from trace file\n");
2369             goto failed;
2370         }
2371
2372         if ((read_status == vogl_trace_file_reader::cEOF) || (pTrace_reader->is_eof_packet()))
2373         {
2374             vogl_message_printf("At trace file EOF\n");
2375             break;
2376         }
2377
2378         const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf();
2379
2380         if (!trace_writer.write_packet(packet_buf.get_ptr(), packet_buf.size(), pTrace_reader->is_swap_buffers_packet()))
2381         {
2382             vogl_error_printf("Failed writing to output trace file \"%s\"\n", output_trace_filename.get_ptr());
2383             goto failed;
2384         }
2385     }
2386
2387     if (!trace_writer.close())
2388     {
2389         vogl_error_printf("Failed closing output trace file \"%s\"\n", output_trace_filename.get_ptr());
2390         goto failed;
2391     }
2392
2393     vogl_message_printf("Successfully wrote binary trace file \"%s\"\n", output_trace_filename.get_ptr());
2394
2395     return true;
2396
2397 failed:
2398     trace_writer.close();
2399
2400     vogl_warning_printf("Processing failed, output trace file \"%s\" may be invalid!\n", output_trace_filename.get_ptr());
2401
2402     return false;
2403 }
2404
2405 //----------------------------------------------------------------------------------------------------------------------
2406 // struct histo_entry
2407 //----------------------------------------------------------------------------------------------------------------------
2408 typedef vogl::map<dynamic_string> dynamic_string_set;
2409 typedef vogl::map<dynamic_string, uint64_t> dynamic_string_hash_map;
2410 typedef vogl::map<uint> uint_map;
2411
2412 struct histo_entry
2413 {
2414     histo_entry()
2415     {
2416     }
2417     histo_entry(dynamic_string_hash_map::const_iterator it, double val)
2418         : m_it(it), m_value(val)
2419     {
2420     }
2421
2422     dynamic_string_hash_map::const_iterator m_it;
2423     double m_value;
2424
2425     bool operator<(const histo_entry &rhs) const
2426     {
2427         return m_value > rhs.m_value;
2428     }
2429 };
2430
2431 //----------------------------------------------------------------------------------------------------------------------
2432 // dump_histogram
2433 //----------------------------------------------------------------------------------------------------------------------
2434 #define SAFE_FLOAT_DIV(n, d) (d ? ((double)(n) / (d)) : 0)
2435
2436 static void dump_histogram(const char *pMsg, const dynamic_string_hash_map &map, uint64_t total_gl_entrypoint_packets, uint64_t total_swaps)
2437 {
2438     VOGL_FUNC_TRACER
2439
2440     vogl::vector<histo_entry> histo;
2441     for (dynamic_string_hash_map::const_iterator it = map.begin(); it != map.end(); ++it)
2442         histo.push_back(histo_entry(it, static_cast<double>(it->second)));
2443     mergesort(histo);
2444     vogl_printf("\n----------------------\n%s: %u\n", pMsg, map.size());
2445
2446     for (uint i = 0; i < histo.size(); i++)
2447     {
2448         dynamic_string_hash_map::const_iterator it = histo[i].m_it;
2449         vogl_printf("%s: Total calls: %" PRIu64 " %3.1f%%, Avg calls per frame: %f\n", it->first.get_ptr(), it->second, SAFE_FLOAT_DIV(it->second * 100.0f, total_gl_entrypoint_packets), SAFE_FLOAT_DIV(it->second, total_swaps));
2450     }
2451 }
2452
2453 //----------------------------------------------------------------------------------------------------------------------
2454 // tool_info_mode
2455 //----------------------------------------------------------------------------------------------------------------------
2456 static bool tool_info_mode()
2457 {
2458     VOGL_FUNC_TRACER
2459
2460     dynamic_string input_base_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
2461     if (input_base_filename.is_empty())
2462     {
2463         vogl_error_printf("Must specify filename of input JSON/blob trace files!\n");
2464         return false;
2465     }
2466
2467     dynamic_string actual_input_filename;
2468     vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(input_base_filename, actual_input_filename, g_command_line_params.get_value_as_string_or_empty("loose_file_path").get_ptr()));
2469     if (!pTrace_reader.get())
2470         return false;
2471
2472     vogl_printf("Scanning trace file %s\n", actual_input_filename.get_ptr());
2473
2474     const vogl_trace_stream_start_of_file_packet &sof_packet = pTrace_reader->get_sof_packet();
2475
2476     if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER)
2477     {
2478         uint64_t file_size = dynamic_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get())->get_stream().get_size();
2479         vogl_printf("Total file size: %s\n", uint64_to_string_with_commas(file_size).get_ptr());
2480     }
2481
2482     vogl_printf("SOF packet size: %" PRIu64 " bytes\n", sof_packet.m_size);
2483     vogl_printf("Version: 0x%04X\n", sof_packet.m_version);
2484     vogl_printf("UUID: 0x%08x 0x%08x 0x%08x 0x%08x\n", sof_packet.m_uuid[0], sof_packet.m_uuid[1], sof_packet.m_uuid[2], sof_packet.m_uuid[3]);
2485     vogl_printf("First packet offset: %" PRIu64 "\n", sof_packet.m_first_packet_offset);
2486     vogl_printf("Trace pointer size: %u\n", sof_packet.m_pointer_sizes);
2487     vogl_printf("Trace archive size: %" PRIu64 " offset: %" PRIu64 "\n", sof_packet.m_archive_size, sof_packet.m_archive_offset);
2488     vogl_printf("Can quickly seek forward: %u\nMax frame index: %" PRIu64 "\n", pTrace_reader->can_quickly_seek_forward(), pTrace_reader->get_max_frame_index());
2489
2490     if (!pTrace_reader->get_archive_blob_manager().is_initialized())
2491     {
2492         vogl_warning_printf("This trace does not have a trace archive!\n");
2493     }
2494     else
2495     {
2496         vogl_printf("----------------------\n");
2497         vogl::vector<dynamic_string> archive_files(pTrace_reader->get_archive_blob_manager().enumerate());
2498         vogl_printf("Total trace archive files: %u\n", archive_files.size());
2499         for (uint i = 0; i < archive_files.size(); i++)
2500             vogl_printf("\"%s\"\n", archive_files[i].get_ptr());
2501         vogl_printf("----------------------\n");
2502     }
2503
2504     uint min_packet_size = cUINT32_MAX;
2505     uint max_packet_size = 0;
2506     uint64_t total_packets = 1; // 1, not 0, to account for the SOF packet
2507     uint64_t total_packet_bytes = 0;
2508     uint64_t total_swaps = 0;
2509     uint64_t total_make_currents = 0;
2510     uint64_t total_internal_trace_commands = 0;
2511     uint64_t total_non_gl_entrypoint_packets = 1; // 1, not 0, to account for the SOF packet
2512     uint64_t total_gl_entrypoint_packets = 0;
2513     uint64_t total_gl_commands = 0;
2514     uint64_t total_glx_commands = 0;
2515     uint64_t total_wgl_commands = 0;
2516     uint64_t total_unknown_commands = 0;
2517     uint64_t total_draws = 0;
2518     bool found_eof_packet = false;
2519
2520     uint64_t min_packets_per_frame = cUINT64_MAX;
2521     uint64_t max_packets_per_frame = 0;
2522     uint64_t max_frame_make_currents = 0;
2523     uint64_t min_frame_make_currents = cUINT64_MAX;
2524
2525     uint64_t min_frame_draws = cUINT64_MAX;
2526     uint64_t max_frame_draws = 0;
2527     uint64_t cur_frame_draws = 0;
2528
2529     uint64_t cur_frame_packet_count = 0;
2530     uint64_t total_frame_make_currents = 0;
2531
2532     uint64_t num_non_whitelisted_funcs = 0;
2533     dynamic_string_set non_whitelisted_funcs_called;
2534
2535     uint64_t total_programs_linked = 0;
2536     uint64_t total_program_binary_calls = 0;
2537     uint_map unique_programs_used;
2538
2539     uint total_gl_state_snapshots = 0;
2540
2541     uint total_display_list_calls = false;
2542     uint total_gl_get_errors = 0;
2543
2544     uint total_context_creates = 0;
2545     uint total_context_destroys = 0;
2546
2547     dynamic_string_hash_map all_apis_called, category_histogram, version_histogram, profile_histogram, deprecated_histogram;
2548
2549     vogl_ctypes trace_gl_ctypes(pTrace_reader->get_sof_packet().m_pointer_sizes);
2550
2551     vogl_trace_packet trace_packet(&trace_gl_ctypes);
2552
2553     for (;;)
2554     {
2555         vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
2556
2557         if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
2558         {
2559             vogl_error_printf("Failed reading from trace file!\n");
2560
2561             goto done;
2562         }
2563
2564         if (read_status == vogl_trace_file_reader::cEOF)
2565         {
2566             vogl_printf("At trace file EOF on swap %" PRIu64 "\n", total_swaps);
2567             break;
2568         }
2569
2570         const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf();
2571         VOGL_NOTE_UNUSED(packet_buf);
2572
2573         uint packet_size = pTrace_reader->get_packet_size();
2574
2575         min_packet_size = math::minimum<uint>(min_packet_size, packet_size);
2576         max_packet_size = math::maximum<uint>(max_packet_size, packet_size);
2577         total_packets++;
2578         total_packet_bytes += packet_size;
2579
2580         cur_frame_packet_count++;
2581
2582         const vogl_trace_stream_packet_base &base_packet = pTrace_reader->get_base_packet();
2583         VOGL_NOTE_UNUSED(base_packet);
2584         const vogl_trace_gl_entrypoint_packet *pGL_packet = NULL;
2585
2586         if (pTrace_reader->get_packet_type() == cTSPTGLEntrypoint)
2587         {
2588             if (!trace_packet.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
2589             {
2590                 console::error("%s: Failed parsing GL entrypoint packet\n", VOGL_FUNCTION_NAME);
2591                 goto done;
2592             }
2593
2594             pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
2595             gl_entrypoint_id_t entrypoint_id = static_cast<gl_entrypoint_id_t>(pGL_packet->m_entrypoint_id);
2596
2597             const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[entrypoint_id];
2598
2599             all_apis_called[entrypoint_desc.m_pName]++;
2600             category_histogram[entrypoint_desc.m_pCategory]++;
2601             version_histogram[entrypoint_desc.m_pVersion]++;
2602             profile_histogram[entrypoint_desc.m_pProfile]++;
2603             deprecated_histogram[entrypoint_desc.m_pDeprecated]++;
2604
2605             if (!entrypoint_desc.m_is_whitelisted)
2606             {
2607                 num_non_whitelisted_funcs++;
2608                 non_whitelisted_funcs_called.insert(entrypoint_desc.m_pName);
2609             }
2610
2611             if (!strcmp(entrypoint_desc.m_pAPI_prefix, "GLX"))
2612                 total_glx_commands++;
2613             else if (!strcmp(entrypoint_desc.m_pAPI_prefix, "WGL"))
2614                 total_wgl_commands++;
2615             else if (!strcmp(entrypoint_desc.m_pAPI_prefix, "GL"))
2616                 total_gl_commands++;
2617             else
2618                 total_unknown_commands++;
2619
2620             total_gl_entrypoint_packets++;
2621
2622             if (vogl_is_swap_buffers_entrypoint(entrypoint_id))
2623             {
2624                 total_swaps++;
2625                 if ((total_swaps & 255) == 255)
2626                     console::progress("Frame %" PRIu64 "\n", total_swaps);
2627
2628                 min_packets_per_frame = math::minimum(min_packets_per_frame, cur_frame_packet_count);
2629                 max_packets_per_frame = math::maximum(max_packets_per_frame, cur_frame_packet_count);
2630
2631                 min_frame_draws = math::minimum(min_frame_draws, cur_frame_draws);
2632                 max_frame_draws = math::maximum(max_frame_draws, cur_frame_draws);
2633
2634                 max_frame_make_currents = math::maximum(max_frame_make_currents, total_frame_make_currents);
2635                 min_frame_make_currents = math::minimum(min_frame_make_currents, total_frame_make_currents);
2636
2637                 cur_frame_packet_count = 0;
2638                 total_frame_make_currents = 0;
2639                 cur_frame_draws = 0;
2640             }
2641             else if (vogl_is_draw_entrypoint(entrypoint_id))
2642             {
2643                 total_draws++;
2644                 cur_frame_draws++;
2645             }
2646             else if (vogl_is_make_current_entrypoint(entrypoint_id))
2647             {
2648                 total_make_currents++;
2649                 total_frame_make_currents++;
2650             }
2651
2652             switch (entrypoint_id)
2653             {
2654                 case VOGL_ENTRYPOINT_glInternalTraceCommandRAD:
2655                 {
2656                     total_internal_trace_commands++;
2657
2658                     GLuint cmd = trace_packet.get_param_value<GLuint>(0);
2659                     GLuint size = trace_packet.get_param_value<GLuint>(1);
2660                     VOGL_NOTE_UNUSED(size);
2661
2662                     if (cmd == cITCRKeyValueMap)
2663                     {
2664                         key_value_map &kvm = trace_packet.get_key_value_map();
2665
2666                         dynamic_string cmd_type(kvm.get_string("command_type"));
2667                         if (cmd_type == "state_snapshot")
2668                         {
2669                             total_gl_state_snapshots++;
2670                         }
2671                     }
2672
2673                     break;
2674                 }
2675                 case VOGL_ENTRYPOINT_glProgramBinary:
2676                 {
2677                     total_program_binary_calls++;
2678                     break;
2679                 }
2680                 case VOGL_ENTRYPOINT_glLinkProgram:
2681                 case VOGL_ENTRYPOINT_glLinkProgramARB:
2682                 {
2683                     total_programs_linked++;
2684                     break;
2685                 }
2686                 case VOGL_ENTRYPOINT_glUseProgram:
2687                 case VOGL_ENTRYPOINT_glUseProgramObjectARB:
2688                 {
2689                     GLuint trace_handle = trace_packet.get_param_value<GLuint>(0);
2690                     unique_programs_used.insert(trace_handle);
2691
2692                     break;
2693                 }
2694                 case VOGL_ENTRYPOINT_glGenLists:
2695                 case VOGL_ENTRYPOINT_glDeleteLists:
2696                 case VOGL_ENTRYPOINT_glXUseXFont:
2697                 case VOGL_ENTRYPOINT_glCallList:
2698                 case VOGL_ENTRYPOINT_glCallLists:
2699                 case VOGL_ENTRYPOINT_glListBase:
2700                 {
2701                     total_display_list_calls++;
2702                     break;
2703                 }
2704                 case VOGL_ENTRYPOINT_glGetError:
2705                 {
2706                     total_gl_get_errors++;
2707                     break;
2708                 }
2709                 case VOGL_ENTRYPOINT_glXCreateContext:
2710                 case VOGL_ENTRYPOINT_glXCreateContextAttribsARB:
2711                 {
2712                     total_context_creates++;
2713                     break;
2714                 }
2715                 case VOGL_ENTRYPOINT_glXDestroyContext:
2716                 {
2717                     total_context_destroys++;
2718                     break;
2719                 }
2720                 default:
2721                     break;
2722             }
2723         }
2724         else
2725         {
2726             total_non_gl_entrypoint_packets++;
2727         }
2728
2729         if (pTrace_reader->get_packet_type() == cTSPTEOF)
2730         {
2731             found_eof_packet = true;
2732             vogl_printf("Found trace file EOF packet on swap %" PRIu64 "\n", total_swaps);
2733             break;
2734         }
2735     }
2736
2737 done:
2738     vogl_printf("\n");
2739
2740 #define PRINT_UINT_VAR(x) vogl_printf("%s: %u\n", dynamic_string(#x).replace("_", " ").get_ptr(), x);
2741 #define PRINT_UINT64_VAR(x) vogl_printf("%s: %" PRIu64 "\n", dynamic_string(#x).replace("_", " ").get_ptr(), x);
2742 #define PRINT_FLOAT(x, f) vogl_printf("%s: %f\n", dynamic_string(#x).replace("_", " ").get_ptr(), f);
2743
2744     PRINT_UINT64_VAR(num_non_whitelisted_funcs);
2745     PRINT_UINT_VAR(total_gl_state_snapshots);
2746
2747     PRINT_UINT64_VAR(total_swaps);
2748
2749     PRINT_UINT64_VAR(total_make_currents);
2750     PRINT_FLOAT(avg_make_currents_per_frame, SAFE_FLOAT_DIV(total_make_currents, total_swaps));
2751     PRINT_UINT64_VAR(max_frame_make_currents);
2752     PRINT_UINT64_VAR(min_frame_make_currents);
2753
2754     PRINT_UINT64_VAR(total_draws);
2755     PRINT_FLOAT(avg_draws_per_frame, SAFE_FLOAT_DIV(total_draws, total_swaps));
2756     PRINT_UINT64_VAR(min_frame_draws);
2757     PRINT_UINT64_VAR(max_frame_draws);
2758
2759     PRINT_UINT_VAR(min_packet_size);
2760     PRINT_UINT_VAR(max_packet_size);
2761     PRINT_UINT64_VAR(total_packets);
2762     PRINT_UINT64_VAR(total_packet_bytes);
2763     PRINT_FLOAT(avg_packet_bytes_per_frame, SAFE_FLOAT_DIV(total_packet_bytes, total_swaps));
2764     PRINT_FLOAT(avg_packet_size, SAFE_FLOAT_DIV(total_packet_bytes, total_packets));
2765     PRINT_UINT64_VAR(min_packets_per_frame);
2766     PRINT_UINT64_VAR(max_packets_per_frame);
2767     PRINT_FLOAT(avg_packets_per_frame, SAFE_FLOAT_DIV(total_packets, total_swaps));
2768
2769     PRINT_UINT64_VAR(total_internal_trace_commands);
2770     PRINT_UINT64_VAR(total_non_gl_entrypoint_packets);
2771     PRINT_UINT64_VAR(total_gl_entrypoint_packets);
2772     PRINT_UINT64_VAR(total_gl_commands);
2773     PRINT_UINT64_VAR(total_glx_commands);
2774     PRINT_UINT64_VAR(total_wgl_commands);
2775     PRINT_UINT64_VAR(total_unknown_commands);
2776
2777     PRINT_UINT_VAR(found_eof_packet);
2778
2779     PRINT_UINT_VAR(total_display_list_calls);
2780     printf("Avg display lists calls per frame: %f\n", SAFE_FLOAT_DIV(total_display_list_calls, total_swaps));
2781
2782     PRINT_UINT_VAR(total_gl_get_errors);
2783     printf("Avg glGetError calls per frame: %f\n", SAFE_FLOAT_DIV(total_gl_get_errors, total_swaps));
2784
2785     PRINT_UINT_VAR(total_context_creates);
2786     PRINT_UINT_VAR(total_context_destroys);
2787
2788 #undef PRINT_UINT
2789 #undef PRINT_UINT64_VAR
2790 #undef PRINT_FLOAT
2791
2792     vogl_printf("----------------------\n%s: %" PRIu64 "\n", "Total calls to glLinkProgram/glLinkProgramARB", total_programs_linked);
2793     vogl_printf("%s: %" PRIu64 "\n", "Total calls to glProgramBinary", total_program_binary_calls);
2794     vogl_printf("%s: %u\n", "Total unique program handles passed to glUseProgram/glUseProgramObjectARB", unique_programs_used.size());
2795
2796     dump_histogram("API histogram", all_apis_called, total_gl_entrypoint_packets, total_swaps);
2797     dump_histogram("API Category histogram", category_histogram, total_gl_entrypoint_packets, total_swaps);
2798     dump_histogram("API Version histogram", version_histogram, total_gl_entrypoint_packets, total_swaps);
2799 //dump_histogram("API Profile histogram", profile_histogram, total_gl_entrypoint_packets, total_swaps);
2800 //dump_histogram("API Deprecated histogram", deprecated_histogram, total_gl_entrypoint_packets, total_swaps);
2801
2802 #undef DUMP_HISTOGRAM
2803
2804     if (non_whitelisted_funcs_called.size())
2805     {
2806         vogl_warning_printf("\n----------------------\n");
2807         vogl_warning_printf("Number of non-whitelisted functions actually called: %u\n", non_whitelisted_funcs_called.size());
2808         for (dynamic_string_set::const_iterator it = non_whitelisted_funcs_called.begin(); it != non_whitelisted_funcs_called.end(); ++it)
2809             vogl_warning_printf("%s\n", it->first.get_ptr());
2810         vogl_warning_printf("\n----------------------\n");
2811     }
2812
2813     return true;
2814 }
2815 #undef SAFE_FLOAT_DIV
2816
2817 //----------------------------------------------------------------------------------------------------------------------
2818 // param_value_matches
2819 //----------------------------------------------------------------------------------------------------------------------
2820 static bool param_value_matches(const bigint128 &value_to_find, vogl_namespace_t find_namespace, uint64_t value_data, vogl_ctype_t value_ctype, vogl_namespace_t value_namespace)
2821 {
2822     if (find_namespace != VOGL_NAMESPACE_UNKNOWN)
2823     {
2824         if (find_namespace != value_namespace)
2825             return false;
2826     }
2827
2828     bigint128 value(0);
2829
2830     switch (value_ctype)
2831     {
2832         case VOGL_BOOL:
2833         case VOGL_GLBOOLEAN:
2834         {
2835             value = value_data;
2836             break;
2837         }
2838         case VOGL_GLENUM:
2839         {
2840             value = value_data;
2841             break;
2842         }
2843         case VOGL_FLOAT:
2844         case VOGL_GLFLOAT:
2845         case VOGL_GLCLAMPF:
2846         {
2847             // Not supporting float/double finds for now
2848             return false;
2849         }
2850         case VOGL_GLDOUBLE:
2851         case VOGL_GLCLAMPD:
2852         {
2853             // Not supporting float/double finds for now
2854             return false;
2855         }
2856         case VOGL_GLINT:
2857         case VOGL_INT:
2858         case VOGL_INT32T:
2859         case VOGL_GLSIZEI:
2860         case VOGL_GLFIXED:
2861         {
2862             value = static_cast<int32>(value_data);
2863             break;
2864         }
2865         case VOGL_GLSHORT:
2866         {
2867             value = static_cast<int16>(value_data);
2868             break;
2869         }
2870         case VOGL_GLBYTE:
2871         {
2872             value = static_cast<int8>(value_data);
2873             break;
2874         }
2875         case VOGL_GLINT64:
2876         case VOGL_GLINT64EXT:
2877         {
2878             value = static_cast<int64_t>(value_data);
2879             break;
2880         }
2881         default:
2882         {
2883             value = value_data;
2884             break;
2885         }
2886     }
2887
2888     if (value == value_to_find)
2889         return true;
2890
2891     return false;
2892 }
2893
2894 //----------------------------------------------------------------------------------------------------------------------
2895 // print_match
2896 //----------------------------------------------------------------------------------------------------------------------
2897 static void print_match(const vogl_trace_packet &trace_packet, int param_index, int array_element_index, uint64_t total_swaps)
2898 {
2899     json_document doc;
2900     vogl_trace_packet::json_serialize_params params;
2901     trace_packet.json_serialize(*doc.get_root(), params);
2902
2903     dynamic_string packet_as_json;
2904     doc.serialize(packet_as_json);
2905
2906     if (param_index == -2)
2907         vogl_printf("----- Function match, frame %" PRIu64 ":\n", total_swaps);
2908     else if (param_index < 0)
2909         vogl_printf("----- Return value match, frame %" PRIu64 ":\n", total_swaps);
2910     else if (array_element_index >= 0)
2911         vogl_printf("----- Parameter %i element %i match, frame %" PRIu64 ":\n", param_index, array_element_index, total_swaps);
2912     else
2913         vogl_printf("----- Parameter %i match, frame %" PRIu64 ":\n", param_index, total_swaps);
2914
2915     vogl_printf("%s\n", packet_as_json.get_ptr());
2916 }
2917
2918 //----------------------------------------------------------------------------------------------------------------------
2919 // tool_find_mode
2920 //----------------------------------------------------------------------------------------------------------------------
2921 static bool tool_find_mode()
2922 {
2923     VOGL_FUNC_TRACER
2924
2925     dynamic_string input_base_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
2926     if (input_base_filename.is_empty())
2927     {
2928         vogl_error_printf("Must specify filename of input JSON/blob trace files!\n");
2929         return false;
2930     }
2931
2932     dynamic_string actual_input_filename;
2933     vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(input_base_filename, actual_input_filename, g_command_line_params.get_value_as_string_or_empty("loose_file_path").get_ptr()));
2934     if (!pTrace_reader.get())
2935         return false;
2936
2937     bigint128 value_to_find(static_cast<uint64_t>(gl_enums::cUnknownEnum));
2938     bool has_find_param = g_command_line_params.has_key("find_param");
2939     if (has_find_param)
2940     {
2941         dynamic_string find_value_str(g_command_line_params.get_value_as_string("find_param"));
2942         if ((find_value_str.has_content()) && (vogl_isalpha(find_value_str[0])))
2943         {
2944             value_to_find = g_gl_enums.find_enum(find_value_str);
2945         }
2946
2947         if (value_to_find == static_cast<uint64_t>(gl_enums::cUnknownEnum))
2948         {
2949             bool find_param_u64_valid = false;
2950             value_to_find = g_command_line_params.get_value_as_uint64("find_param", 0, 0, 0, cUINT64_MAX, 0, &find_param_u64_valid);
2951
2952             if (!find_param_u64_valid)
2953             {
2954                 bool find_param_i64_valid = false;
2955                 value_to_find = g_command_line_params.get_value_as_int64("find_param", 0, 0, 0, cINT64_MAX, 0, &find_param_i64_valid);
2956
2957                 if (!find_param_i64_valid)
2958                 {
2959                     vogl_error_printf("Failed parsing \"-find_param X\" command line option!\n");
2960                     return false;
2961                 }
2962             }
2963         }
2964     }
2965
2966     dynamic_string find_namespace_str(g_command_line_params.get_value_as_string_or_empty("find_namespace"));
2967     vogl_namespace_t find_namespace = VOGL_NAMESPACE_UNKNOWN;
2968     if (find_namespace_str.has_content())
2969     {
2970         find_namespace = vogl_find_namespace_from_gl_type(find_namespace_str.get_ptr());
2971         if ((find_namespace == VOGL_NAMESPACE_INVALID) && (find_namespace_str != "invalid"))
2972         {
2973             vogl_error_printf("Invalid namespace: \"%s\"\n", find_namespace_str.get_ptr());
2974             return false;
2975         }
2976     }
2977
2978     dynamic_string find_param_name(g_command_line_params.get_value_as_string_or_empty("find_param_name"));
2979
2980     dynamic_string find_func_pattern(g_command_line_params.get_value_as_string_or_empty("find_func"));
2981     regexp func_regex;
2982     if (find_func_pattern.has_content())
2983     {
2984         if (!func_regex.init(find_func_pattern.get_ptr(), REGEX_IGNORE_CASE))
2985         {
2986             vogl_error_printf("Invalid func regex: \"%s\"\n", find_func_pattern.get_ptr());
2987             return false;
2988         }
2989     }
2990
2991     int64_t find_frame_low = g_command_line_params.get_value_as_int64("find_frame_low", 0, -1);
2992     int64_t find_frame_high = g_command_line_params.get_value_as_int64("find_frame_high", 0, -1);
2993     int64_t find_call_low = g_command_line_params.get_value_as_int64("find_call_low", 0, -1);
2994     int64_t find_call_high = g_command_line_params.get_value_as_int64("find_call_high", 0, -1);
2995
2996     vogl_printf("Scanning trace file %s\n", actual_input_filename.get_ptr());
2997
2998     vogl_ctypes trace_gl_ctypes(pTrace_reader->get_sof_packet().m_pointer_sizes);
2999     vogl_trace_packet trace_packet(&trace_gl_ctypes);
3000
3001     uint64_t total_matches = 0;
3002     uint64_t total_swaps = 0;
3003
3004     for (;;)
3005     {
3006         vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();
3007
3008         if ((read_status != vogl_trace_file_reader::cOK) && (read_status != vogl_trace_file_reader::cEOF))
3009         {
3010             vogl_error_printf("Failed reading from trace file!\n");
3011             goto done;
3012         }
3013
3014         if (read_status == vogl_trace_file_reader::cEOF)
3015             break;
3016
3017         const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf();
3018         VOGL_NOTE_UNUSED(packet_buf);
3019
3020         const vogl_trace_stream_packet_base &base_packet = pTrace_reader->get_base_packet();
3021         VOGL_NOTE_UNUSED(base_packet);
3022
3023         const vogl_trace_gl_entrypoint_packet *pGL_packet = NULL;
3024         VOGL_NOTE_UNUSED(pGL_packet);
3025
3026         if (pTrace_reader->get_packet_type() == cTSPTEOF)
3027             break;
3028         else if (pTrace_reader->get_packet_type() != cTSPTGLEntrypoint)
3029             continue;
3030
3031         if (!trace_packet.deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
3032         {
3033             console::error("%s: Failed parsing GL entrypoint packet\n", VOGL_FUNCTION_NAME);
3034             goto done;
3035         }
3036
3037         pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
3038
3039         if (find_frame_low >= 0)
3040         {
3041             if (total_swaps < static_cast<uint64_t>(find_frame_low))
3042                 goto skip;
3043         }
3044         if (find_frame_high >= 0)
3045         {
3046             if (total_swaps > static_cast<uint64_t>(find_frame_high))
3047                 break;
3048         }
3049
3050         if (find_call_low >= 0)
3051         {
3052             if (trace_packet.get_call_counter() < static_cast<uint64_t>(find_call_low))
3053                 goto skip;
3054         }
3055         if (find_call_high >= 0)
3056         {
3057             if (trace_packet.get_call_counter() > static_cast<uint64_t>(find_call_high))
3058                 break;
3059         }
3060
3061         if (func_regex.is_initialized())
3062         {
3063             if (!func_regex.full_match(trace_packet.get_entrypoint_desc().m_pName))
3064                 goto skip;
3065         }
3066
3067         if (!has_find_param)
3068         {
3069             print_match(trace_packet, -2, -1, total_swaps);
3070             total_matches++;
3071         }
3072         else
3073         {
3074             if (trace_packet.has_return_value())
3075             {
3076                 if ((find_param_name.is_empty()) || (find_param_name == "return"))
3077                 {
3078                     if (param_value_matches(value_to_find, find_namespace, trace_packet.get_return_value_data(), trace_packet.get_return_value_ctype(), trace_packet.get_return_value_namespace()))
3079                     {
3080                         print_match(trace_packet, -1, -1, total_swaps);
3081                         total_matches++;
3082                     }
3083                 }
3084             }
3085
3086             for (uint i = 0; i < trace_packet.total_params(); i++)
3087             {
3088                 if ((find_param_name.has_content()) && (find_param_name != trace_packet.get_param_desc(i).m_pName))
3089                     continue;
3090
3091                 const vogl_ctype_desc_t &param_ctype_desc = trace_packet.get_param_ctype_desc(i);
3092
3093                 if (param_ctype_desc.m_is_pointer)
3094                 {
3095                     if ((!param_ctype_desc.m_is_opaque_pointer) && (param_ctype_desc.m_pointee_ctype != VOGL_VOID) && (trace_packet.has_param_client_memory(i)))
3096                     {
3097                         const vogl_client_memory_array array(trace_packet.get_param_client_memory_array(i));
3098
3099                         for (uint j = 0; j < array.size(); j++)
3100                         {
3101                             if (param_value_matches(value_to_find, find_namespace, array.get_element<uint64_t>(j), array.get_element_ctype(), trace_packet.get_param_namespace(i)))
3102                             {
3103                                 print_match(trace_packet, i, j, total_swaps);
3104                                 total_matches++;
3105                             }
3106                         }
3107                     }
3108                 }
3109                 else if (param_value_matches(value_to_find, find_namespace, trace_packet.get_param_data(i), trace_packet.get_param_ctype(i), trace_packet.get_param_namespace(i)))
3110                 {
3111                     print_match(trace_packet, i, -1, total_swaps);
3112                     total_matches++;
3113                 }
3114             }
3115         }
3116
3117     skip:
3118         if (vogl_is_swap_buffers_entrypoint(trace_packet.get_entrypoint_id()))
3119             total_swaps++;
3120     }
3121
3122 done:
3123     vogl_printf("Total matches found: %" PRIu64 "\n", total_matches);
3124
3125     return true;
3126 }
3127
3128 //----------------------------------------------------------------------------------------------------------------------
3129 // tool_compare_hash_files
3130 //----------------------------------------------------------------------------------------------------------------------
3131 static bool tool_compare_hash_files()
3132 {
3133     dynamic_string src1_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
3134     dynamic_string src2_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
3135     if ((src1_filename.is_empty()) || (src2_filename.is_empty()))
3136     {
3137         vogl_error_printf("Must specify two source filenames!\n");
3138         return false;
3139     }
3140
3141     dynamic_string_array src1_lines;
3142     if (!file_utils::read_text_file(src1_filename.get_ptr(), src1_lines, file_utils::cRTFTrim | file_utils::cRTFIgnoreEmptyLines | file_utils::cRTFIgnoreCommentedLines | file_utils::cRTFPrintErrorMessages))
3143     {
3144         console::error("%s: Failed reading source file %s!\n", VOGL_FUNCTION_NAME, src1_filename.get_ptr());
3145         return false;
3146     }
3147
3148     vogl_printf("Read 1st source file \"%s\", %u lines\n", src1_filename.get_ptr(), src1_lines.size());
3149
3150     dynamic_string_array src2_lines;
3151     if (!file_utils::read_text_file(src2_filename.get_ptr(), src2_lines, file_utils::cRTFTrim | file_utils::cRTFIgnoreEmptyLines | file_utils::cRTFIgnoreCommentedLines | file_utils::cRTFPrintErrorMessages))
3152     {
3153         console::error("%s: Failed reading source file %s!\n", VOGL_FUNCTION_NAME, src2_filename.get_ptr());
3154         return false;
3155     }
3156
3157     vogl_printf("Read 2nd source file \"%s\", %u lines\n", src2_filename.get_ptr(), src2_lines.size());
3158
3159     const uint64_t sum_comp_thresh = g_command_line_params.get_value_as_uint64("sum_compare_threshold");
3160
3161     const uint compare_first_frame = g_command_line_params.get_value_as_uint("compare_first_frame");
3162     if (compare_first_frame > src2_lines.size())
3163     {
3164         vogl_error_printf("%s: -compare_first_frame is %u, but the second file only has %u frames!\n", VOGL_FUNCTION_NAME, compare_first_frame, src2_lines.size());
3165         return false;
3166     }
3167
3168     const uint lines_to_comp = math::minimum(src1_lines.size(), src2_lines.size() - compare_first_frame);
3169
3170     if (src1_lines.size() != src2_lines.size())
3171     {
3172         // FIXME: When we replay q2, we get 2 more frames vs. tracing. Not sure why, this needs to be investigated.
3173         if ( (!g_command_line_params.get_value_as_bool("ignore_line_count_differences")) && (labs(src1_lines.size() - src2_lines.size()) > 3) )
3174         {
3175             vogl_error_printf("%s: Input files have a different number of lines! (%u vs %u)\n", VOGL_FUNCTION_NAME, src1_lines.size(), src2_lines.size());
3176             return false;
3177         }
3178         else
3179         {
3180             vogl_warning_printf("%s: Input files have a different number of lines! (%u vs %u)\n", VOGL_FUNCTION_NAME, src1_lines.size(), src2_lines.size());
3181         }
3182     }
3183
3184     const uint compare_ignore_frames = g_command_line_params.get_value_as_uint("compare_ignore_frames");
3185     if (compare_ignore_frames > lines_to_comp)
3186     {
3187         vogl_error_printf("%s: -compare_ignore_frames is too large!\n", VOGL_FUNCTION_NAME);
3188         return false;
3189     }
3190
3191     const bool sum_hashing = g_command_line_params.get_value_as_bool("sum_hashing");
3192
3193     if (g_command_line_params.has_key("compare_expected_frames"))
3194     {
3195         const uint compare_expected_frames = g_command_line_params.get_value_as_uint("compare_expected_frames");
3196         if ((src1_lines.size() != compare_expected_frames) || (src2_lines.size() != compare_expected_frames))
3197         {
3198             vogl_warning_printf("%s: Expected %u frames! First file has %u frames, second file has %u frames.\n", VOGL_FUNCTION_NAME, compare_expected_frames, src1_lines.size(), src2_lines.size());
3199             return false;
3200         }
3201     }
3202
3203     uint total_mismatches = 0;
3204     uint64_t max_sum_delta = 0;
3205
3206     for (uint i = compare_ignore_frames; i < lines_to_comp; i++)
3207     {
3208         const char *pStr1 = src1_lines[i].get_ptr();
3209         const char *pStr2 = src2_lines[i + compare_first_frame].get_ptr();
3210
3211         uint64_t val1 = 0, val2 = 0;
3212         if (!string_ptr_to_uint64(pStr1, val1))
3213         {
3214             vogl_error_printf("%s: Failed parsing line at index %u of first source file!\n", VOGL_FUNCTION_NAME, i);
3215             return false;
3216         }
3217
3218         if (!string_ptr_to_uint64(pStr2, val2))
3219         {
3220             vogl_error_printf("%s: Failed parsing line at index %u of second source file!\n", VOGL_FUNCTION_NAME, i);
3221             return false;
3222         }
3223
3224         bool mismatch = false;
3225
3226         if (sum_hashing)
3227         {
3228             uint64_t delta;
3229             if (val1 > val2)
3230                 delta = val1 - val2;
3231             else
3232                 delta = val2 - val1;
3233             max_sum_delta = math::maximum(max_sum_delta, delta);
3234
3235             if (delta > sum_comp_thresh)
3236                 mismatch = true;
3237         }
3238         else
3239         {
3240             mismatch = val1 != val2;
3241         }
3242
3243         if (mismatch)
3244         {
3245             if (sum_hashing)
3246                 vogl_error_printf("Mismatch at frame %u: %" PRIu64 ", %" PRIu64 "\n", i, val1, val2);
3247             else
3248                 vogl_error_printf("Mismatch at frame %u: 0x%" PRIX64 ", 0x%" PRIX64 "\n", i, val1, val2);
3249             total_mismatches++;
3250         }
3251     }
3252
3253     if (sum_hashing)
3254         vogl_printf("Max sum delta: %" PRIu64 "\n", max_sum_delta);
3255
3256     if (!total_mismatches)
3257         vogl_printf("No mismatches\n");
3258     else
3259     {
3260         vogl_error_printf("%u total mismatches!\n", total_mismatches);
3261         return false;
3262     }
3263
3264     return true;
3265 }
3266
3267 //----------------------------------------------------------------------------------------------------------------------
3268 // tool_unpack_json_mode
3269 //----------------------------------------------------------------------------------------------------------------------
3270 static bool tool_unpack_json_mode()
3271 {
3272     VOGL_FUNC_TRACER
3273
3274     dynamic_string input_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
3275     if (input_filename.is_empty())
3276     {
3277         vogl_error_printf("Must specify filename of input UBJ file!\n");
3278         return false;
3279     }
3280
3281     dynamic_string output_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
3282     if (output_filename.is_empty())
3283     {
3284         vogl_error_printf("Must specify filename of output text file!\n");
3285         return false;
3286     }
3287
3288     json_document doc;
3289     vogl_message_printf("Reading UBJ file \"%s\"\n", input_filename.get_ptr());
3290
3291     if (!doc.binary_deserialize_file(input_filename.get_ptr()))
3292     {
3293         vogl_error_printf("Unable to deserialize input file \"%s\"!\n", input_filename.get_ptr());
3294         if (doc.get_error_msg().has_content())
3295             vogl_error_printf("%s\n", doc.get_error_msg().get_ptr());
3296         return false;
3297     }
3298
3299     if (!doc.serialize_to_file(output_filename.get_ptr()))
3300     {
3301         vogl_error_printf("Failed serializing to output file \"%s\"!\n", output_filename.get_ptr());
3302         return false;
3303     }
3304
3305     vogl_message_printf("Wrote textual JSON file to \"%s\"\n", output_filename.get_ptr());
3306
3307     return true;
3308 }
3309
3310 //----------------------------------------------------------------------------------------------------------------------
3311 // tool_pack_json_mode
3312 //----------------------------------------------------------------------------------------------------------------------
3313 static bool tool_pack_json_mode()
3314 {
3315     VOGL_FUNC_TRACER
3316
3317     dynamic_string input_filename(g_command_line_params.get_value_as_string_or_empty("", 1));
3318     if (input_filename.is_empty())
3319     {
3320         vogl_error_printf("Must specify filename of input text file!\n");
3321         return false;
3322     }
3323
3324     dynamic_string output_filename(g_command_line_params.get_value_as_string_or_empty("", 2));
3325     if (output_filename.is_empty())
3326     {
3327         vogl_error_printf("Must specify filename of output UBJ file!\n");
3328         return false;
3329     }
3330
3331     json_document doc;
3332     vogl_message_printf("Reading JSON text file \"%s\"\n", input_filename.get_ptr());
3333
3334     if (!doc.deserialize_file(input_filename.get_ptr()))
3335     {
3336         vogl_error_printf("Unable to deserialize input file \"%s\"!\n", input_filename.get_ptr());
3337         if (doc.get_error_msg().has_content())
3338             vogl_error_printf("%s (Line: %u)\n", doc.get_error_msg().get_ptr(), doc.get_error_line());
3339         return false;
3340     }
3341
3342     if (!doc.binary_serialize_to_file(output_filename.get_ptr()))
3343     {
3344         vogl_error_printf("Failed serializing to output file \"%s\"!\n", output_filename.get_ptr());
3345         return false;
3346     }
3347
3348     vogl_message_printf("Wrote binary UBJ file to \"%s\"\n", output_filename.get_ptr());
3349
3350     return true;
3351 }
3352
3353 //----------------------------------------------------------------------------------------------------------------------
3354 // xerror_handler
3355 //----------------------------------------------------------------------------------------------------------------------
3356 static int xerror_handler(Display *dsp, XErrorEvent *error)
3357 {
3358     char error_string[256];
3359     XGetErrorText(dsp, error->error_code, error_string, sizeof(error_string));
3360
3361     fprintf(stderr, "voglreplay: Fatal X Windows Error: %s\n", error_string);
3362     abort();
3363 }
3364
3365 //----------------------------------------------------------------------------------------------------------------------
3366 // main
3367 //----------------------------------------------------------------------------------------------------------------------
3368 int main(int argc, char *argv[])
3369 {
3370 #if VOGL_FUNCTION_TRACING
3371     fflush(stdout);
3372     fflush(stderr);
3373     setvbuf(stdout, NULL, _IONBF, 0);
3374     setvbuf(stderr, NULL, _IONBF, 0);
3375 #endif
3376
3377     VOGL_FUNC_TRACER
3378
3379     XSetErrorHandler(xerror_handler);
3380
3381     if (!vogl_replay_init(argc, argv))
3382     {
3383         vogl_replay_deinit();
3384         return EXIT_FAILURE;
3385     }
3386
3387 #if VOGL_REMOTING
3388     vogl_init_listener();
3389 #endif // VOGL_REMOTING
3390
3391 #if 0
3392         test();
3393         return EXIT_SUCCESS;
3394 #endif
3395
3396     if (g_command_line_params.get_count("") < 2)
3397     {
3398         vogl_error_printf("Must specify at least one trace (or input) files!\n");
3399
3400         tool_print_help();
3401
3402         vogl_replay_deinit();
3403         return EXIT_FAILURE;
3404     }
3405
3406     if (g_command_line_params.get_value_as_bool("pause"))
3407     {
3408         vogl_message_printf("Press key to continue\n");
3409         vogl_sleep(1000);
3410         getchar();
3411     }
3412
3413     bool success = false;
3414
3415     if (g_command_line_params.get_value_as_bool("dump"))
3416     {
3417         tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "dump");
3418         vogl_message_printf("Dump from binary to JSON mode\n");
3419
3420         success = tool_dump_mode();
3421     }
3422     else if (g_command_line_params.get_value_as_bool("parse"))
3423     {
3424         tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "parse");
3425         vogl_message_printf("Parse from JSON to binary mode\n");
3426
3427         success = tool_parse_mode();
3428     }
3429     else if (g_command_line_params.get_value_as_bool("info"))
3430     {
3431         tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "info");
3432         vogl_message_printf("Dumping trace information\n");
3433
3434         success = tool_info_mode();
3435     }
3436     else if (g_command_line_params.get_value_as_bool("unpack_json"))
3437     {
3438         tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "unpack_json");
3439         vogl_message_printf("Unpacking UBJ to text\n");
3440
3441         success = tool_unpack_json_mode();
3442     }
3443     else if (g_command_line_params.get_value_as_bool("pack_json"))
3444     {
3445         tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "pack_json");
3446         vogl_message_printf("Packing textual JSON to UBJ\n");
3447
3448         success = tool_pack_json_mode();
3449     }
3450     else if (g_command_line_params.get_value_as_bool("find"))
3451     {
3452         vogl_message_printf("Find mode\n");
3453
3454         success = tool_find_mode();
3455     }
3456     else if (g_command_line_params.get_value_as_bool("compare_hash_files"))
3457     {
3458        vogl_message_printf("Comparing hash/sum files\n");
3459
3460        success = tool_compare_hash_files();
3461     }
3462     else
3463     {
3464         tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "replay_mode");
3465         vogl_message_printf("Replay mode\n");
3466
3467         success = tool_replay_mode();
3468     }
3469
3470     console::printf("%u warning(s), %u error(s)\n", console::get_total_messages(cWarningConsoleMessage), console::get_total_messages(cErrorConsoleMessage));
3471
3472     vogl_replay_deinit();
3473
3474     return success ? EXIT_SUCCESS : EXIT_FAILURE;
3475 }