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