]> git.cworth.org Git - vogl/blob - src/vogltrace/vogl_intercept.cpp
Initial vogl checkin
[vogl] / src / vogltrace / vogl_intercept.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_intercept.cpp
27 #include "vogl_trace.h"
28 #include "vogl_trace_stream_types.h"
29 #include "vogl_trace_packet.h"
30 #include "vogl_texture_format.h"
31 #include "vogl_gl_state_snapshot.h"
32 #include "vogl_trace_file_writer.h"
33 #include "vogl_framebuffer_capturer.h"
34 #include "vogl_trace_file_reader.h"
35
36 // voglcore
37 #include "vogl_hash_map.h"
38 #include "vogl_console.h"
39 #include "vogl_colorized_console.h"
40 #include "vogl_command_line_params.h"
41 #include "vogl_cfile_stream.h"
42 #include "vogl_value.h"
43 #include "vogl_file_utils.h"
44 #include "vogl_uuid.h"
45 #include "vogl_unique_ptr.h"
46
47 #include <unistd.h>
48 #include <sys/syscall.h>
49
50 #include <X11/Xatom.h>
51
52 #if VOGL_REMOTING
53 #include "vogl_remote.h"
54 #endif
55
56 #define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
57 #include "vogl_miniz.h"
58
59 #include "btrace.h"
60
61 // loki
62 #include "TypeTraits.h"
63
64 #include <turbojpeg.h>
65
66 #define VOGL_INTERCEPT_TRACE_FILE_VERSION 0x0102
67
68 #define VOGL_STOP_CAPTURE_FILENAME "__stop_capture__"
69 #define VOGL_TRIGGER_CAPTURE_FILENAME "__trigger_capture__"
70
71 #define VOGL_CMD_LINE_OPTIONS_FILE "vogl_cmd_line.txt"
72
73 #define VOGL_BACKTRACE_HASHMAP_CAPACITY 50000
74
75 class vogl_context;
76 class vogl_entrypoint_serializer;
77
78 //----------------------------------------------------------------------------------------------------------------------
79 // Globals
80 //----------------------------------------------------------------------------------------------------------------------
81 static atomic32_t g_vogl_has_been_initialized;
82 static pthread_once_t g_vogl_init_once_control = PTHREAD_ONCE_INIT;
83 static pthread_key_t g_vogl_thread_local_data;
84 static cfile_stream *g_vogl_pLog_stream;
85 static vogl_exception_callback_t g_vogl_pPrev_exception_callback;
86 static GLuint g_dummy_program;
87
88 //----------------------------------------------------------------------------------------------------------------------
89 // Forward declaration
90 //----------------------------------------------------------------------------------------------------------------------
91 static void vogl_glInternalTraceCommandRAD(GLuint cmd, GLuint size, const GLubyte *data);
92 static void vogl_end_capture(bool inside_signal_handler = false);
93 static void vogl_atexit();
94
95 //----------------------------------------------------------------------------------------------------------------------
96 // Context sharelist shadow locking
97 //----------------------------------------------------------------------------------------------------------------------
98 static bool g_app_uses_sharelists;
99 static void vogl_context_shadow_lock();
100 static void vogl_context_shadow_unlock();
101
102 class vogl_scoped_context_shadow_lock
103 {
104     bool m_took_lock;
105
106 public:
107     inline vogl_scoped_context_shadow_lock()
108         : m_took_lock(g_app_uses_sharelists)
109     {
110         if (m_took_lock)
111             vogl_context_shadow_lock();
112     }
113
114     inline ~vogl_scoped_context_shadow_lock()
115     {
116         if (m_took_lock)
117             vogl_context_shadow_unlock();
118     }
119 };
120
121 //----------------------------------------------------------------------------------------------------------------------
122 // Command line params
123 //----------------------------------------------------------------------------------------------------------------------
124 static command_line_param_desc g_command_line_param_descs[] =
125     {
126         { "vogl_dump_gl_full", 0, false, NULL },
127         { "vogl_dump_gl_calls", 0, false, NULL },
128         { "vogl_dump_gl_buffers", 0, false, NULL },
129         { "vogl_dump_gl_shaders", 0, false, NULL },
130         { "vogl_sleep_at_startup", 1, false, NULL },
131         { "vogl_pause", 0, false, NULL },
132         { "vogl_long_pause", 0, false, NULL },
133         { "vogl_dump_stats", 0, false, NULL },
134         { "vogl_debug", 0, false, NULL },
135         { "vogl_flush_files_after_each_call", 0, false, NULL },
136         { "vogl_flush_files_after_each_swap", 0, false, NULL },
137         { "vogl_disable_signal_interception", 0, false, NULL },
138         { "vogl_logfile", 1, false, NULL },
139         { "vogl_logfile_append", 1, false, NULL },
140         { "vogl_tracefile", 1, false, NULL },
141         { "vogl_tracepath", 1, false, NULL },
142         { "vogl_dump_png_screenshots", 0, false, NULL },
143         { "vogl_dump_jpeg_screenshots", 0, false, NULL },
144         { "vogl_jpeg_quality", 0, false, NULL },
145         { "vogl_screenshot_prefix", 1, false, NULL },
146         { "vogl_hash_backbuffer", 0, false, NULL },
147         { "vogl_dump_backbuffer_hashes", 1, false, NULL },
148         { "vogl_sum_hashing", 0, false, NULL },
149         { "vogl_disable_atexit_context_flushing", 0, false, NULL },
150         { "vogl_null_mode", 0, false, NULL },
151         { "vogl_force_debug_context", 0, false, NULL },
152         { "vogl_disable_client_side_array_tracing", 0, false, NULL },
153         { "vogl_disable_gl_program_binary", 0, false, NULL },
154         { "vogl_func_tracing", 0, false, NULL },
155         { "vogl_backtrace_all_calls", 0, false, NULL },
156         { "vogl_exit_after_x_frames", 1, false, NULL },
157         { "vogl_traceport", 1, false, NULL },
158     };
159
160 //----------------------------------------------------------------------------------------------------------------------
161 // Globals
162 //----------------------------------------------------------------------------------------------------------------------
163 bool g_dump_gl_calls_flag;
164 bool g_dump_gl_buffers_flag;
165 bool g_dump_gl_shaders_flag;
166 bool g_disable_gl_program_binary_flag;
167 bool g_null_mode;
168 bool g_backtrace_all_calls;
169 static bool g_disable_client_side_array_tracing;
170
171 static bool g_flush_files_after_each_call;
172 static bool g_flush_files_after_each_swap;
173 static bool g_gather_statistics;
174
175 static vogl_trace_file_writer g_vogl_trace_writer(&g_vogl_process_gl_ctypes);
176 static mutex g_vogl_trace_mutex(0, true);
177
178 static uint g_vogl_total_frames_to_capture;
179 static uint g_vogl_frames_remaining_to_capture;
180 static bool g_vogl_stop_capturing;
181 static dynamic_string g_vogl_capture_path, g_vogl_capture_basename;
182 static vogl_capture_status_callback_func_ptr g_vogl_pCapture_status_callback;
183 static void *g_vogl_pCapture_status_opaque;
184
185 static vogl_backtrace_hashmap g_backtrace_hashmap;
186 static mutex g_backtrace_hashmap_mutex(0, false);
187
188 //----------------------------------------------------------------------------------------------------------------------
189 // vogl_get_current_kernel_thread_id
190 //----------------------------------------------------------------------------------------------------------------------
191 static inline uint64_t vogl_get_current_kernel_thread_id()
192 {
193     return static_cast<uint64_t>(syscall(SYS_gettid));
194 }
195
196 //----------------------------------------------------------------------------------------------------------------------
197 // class vogl_entrypoint_serializer
198 //----------------------------------------------------------------------------------------------------------------------
199 class vogl_entrypoint_serializer
200 {
201     VOGL_NO_COPY_OR_ASSIGNMENT_OP(vogl_entrypoint_serializer);
202
203 public:
204     inline vogl_entrypoint_serializer()
205         : m_packet(&g_vogl_process_gl_ctypes),
206           m_in_begin(false)
207     {
208     }
209
210     inline vogl_entrypoint_serializer(gl_entrypoint_id_t id, vogl_context *pContext)
211         : m_packet(&g_vogl_process_gl_ctypes),
212           m_in_begin(false)
213     {
214         begin(id, pContext);
215     }
216
217     const vogl_trace_packet &get_packet() const
218     {
219         return m_packet;
220     }
221     vogl_trace_packet &get_packet()
222     {
223         return m_packet;
224     }
225
226     // begin()/end() is NOT nestable
227     // Returns false if nesting detected
228     bool begin(gl_entrypoint_id_t id, vogl_context *pContext);
229
230     void set_begin_rdtsc(uint64_t val)
231     {
232         m_packet.set_begin_rdtsc(val);
233     }
234
235     bool is_in_begin() const
236     {
237         return m_in_begin;
238     }
239
240     inline gl_entrypoint_id_t get_cur_entrypoint() const
241     {
242         return m_packet.get_entrypoint_id();
243     }
244
245     inline void add_return_param(vogl_ctype_t ctype, const void *pParam, uint param_size)
246     {
247         VOGL_ASSERT(m_in_begin);
248         m_packet.set_return_param(ctype, pParam, param_size);
249     }
250
251     inline void add_param(uint8_t param_id, vogl_ctype_t ctype, const void *pParam, uint param_size)
252     {
253         VOGL_ASSERT(m_in_begin);
254         m_packet.set_param(param_id, ctype, pParam, param_size);
255     }
256
257     inline void add_ref_client_memory(uint8_t param_id, vogl_ctype_t pointee_ctype, const void *pData, uint64_t data_size)
258     {
259         VOGL_ASSERT(m_in_begin);
260         m_packet.set_ref_client_memory(param_id, pointee_ctype, pData, data_size);
261     }
262
263     inline void add_array_client_memory(uint8_t param_id, vogl_ctype_t pointee_ctype, uint64_t array_size, const void *pData, uint64_t data_size)
264     {
265         VOGL_ASSERT(m_in_begin);
266         m_packet.set_array_client_memory(param_id, pointee_ctype, array_size, pData, data_size);
267     }
268
269     inline bool add_key_value(const value &key, const value &value)
270     {
271         VOGL_ASSERT(m_in_begin);
272         return m_packet.set_key_value(key, value);
273     }
274
275     inline bool add_key_value_blob(const value &key, uint8_vec &blob)
276     {
277         VOGL_ASSERT(m_in_begin);
278         return m_packet.set_key_value(key, blob);
279     }
280
281     inline bool add_key_value_blob(const value &key, const void *pData, uint data_size)
282     {
283         VOGL_ASSERT(m_in_begin);
284         return m_packet.set_key_value_blob(key, pData, data_size);
285     }
286
287     inline bool add_key_value_json_document(const value &key, const json_document &doc)
288     {
289         VOGL_ASSERT(m_in_begin);
290         return m_packet.set_key_value_json_document(key, doc);
291     }
292
293     inline const key_value_map &get_key_value_map() const
294     {
295         VOGL_ASSERT(m_in_begin);
296         return m_packet.get_key_value_map();
297     }
298     inline key_value_map &get_key_value_map()
299     {
300         VOGL_ASSERT(m_in_begin);
301         return m_packet.get_key_value_map();
302     }
303
304     inline void set_gl_begin_rdtsc(uint64_t val)
305     {
306         m_packet.set_gl_begin_rdtsc(val);
307     }
308     inline void set_gl_end_rdtsc(uint64_t val)
309     {
310         m_packet.set_gl_end_rdtsc(val);
311     }
312     inline void set_gl_begin_end_rdtsc(uint64_t begin, uint64_t end)
313     {
314         m_packet.set_gl_begin_rdtsc(begin);
315         m_packet.set_gl_end_rdtsc(end);
316     }
317
318     inline void end()
319     {
320         VOGL_ASSERT(m_in_begin);
321
322         if (!m_in_begin)
323         {
324             vogl_error_printf("%s: end() called without a matching call to begin()! Something is seriously wrong!\n", VOGL_METHOD_NAME);
325             return;
326         }
327
328         m_packet.end_construction(utils::RDTSC());
329
330         VOGL_ASSERT(m_packet.check());
331
332         const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[get_cur_entrypoint()];
333
334         if (m_packet.total_params() != entrypoint_desc.m_num_params)
335         {
336             vogl_error_printf("%s: Unexpected number of params passed to serializer! (entrypoint id %u)\n", VOGL_METHOD_NAME, get_cur_entrypoint());
337             m_in_begin = false;
338             return;
339         }
340
341         if (m_packet.has_return_value() != (entrypoint_desc.m_return_ctype != VOGL_VOID))
342         {
343             vogl_error_printf("%s: Return parameter serialization error (entrypoint id %u)!\n", VOGL_METHOD_NAME, get_cur_entrypoint());
344             m_in_begin = false;
345             return;
346         }
347
348         m_in_begin = false;
349     }
350
351 private:
352     vogl_trace_packet m_packet;
353     bool m_in_begin;
354 };
355
356 //----------------------------------------------------------------------------------------------------------------------
357 // struct vogl_thread_local_data
358 //----------------------------------------------------------------------------------------------------------------------
359 class vogl_thread_local_data
360 {
361 public:
362     vogl_thread_local_data()
363         : m_pContext(NULL),
364           m_calling_driver_entrypoint_id(VOGL_ENTRYPOINT_INVALID)
365     {
366     }
367
368     ~vogl_thread_local_data()
369     {
370         m_pContext = NULL;
371     }
372
373     vogl_context *m_pContext;
374     vogl_entrypoint_serializer m_serializer;
375
376     // Set to a valid entrypoint ID if we're currently trying to call the driver on this thread. The "direct" GL function wrappers (in
377     // vogl_entrypoints.cpp) call our vogl_direct_gl_func_prolog/epilog func callbacks below, which manipulate this member.
378     gl_entrypoint_id_t m_calling_driver_entrypoint_id;
379 };
380
381 //----------------------------------------------------------------------------------------------------------------------
382 // vogl_get_or_create_thread_local_data
383 //----------------------------------------------------------------------------------------------------------------------
384 static VOGL_FORCE_INLINE vogl_thread_local_data *vogl_get_or_create_thread_local_data()
385 {
386     vogl_thread_local_data *pTLS_data = static_cast<vogl_thread_local_data *>(pthread_getspecific(g_vogl_thread_local_data));
387     if (!pTLS_data)
388     {
389         pTLS_data = vogl_new(vogl_thread_local_data);
390
391         pthread_setspecific(g_vogl_thread_local_data, pTLS_data);
392     }
393
394     return pTLS_data;
395 }
396
397 //----------------------------------------------------------------------------------------------------------------------
398 // vogl_get_thread_local_data
399 //----------------------------------------------------------------------------------------------------------------------
400 static VOGL_FORCE_INLINE vogl_thread_local_data *vogl_get_thread_local_data()
401 {
402     return static_cast<vogl_thread_local_data *>(pthread_getspecific(g_vogl_thread_local_data));
403 }
404
405 //----------------------------------------------------------------------------------------------------------------------
406 // tls_thread_local_data_destructor
407 //----------------------------------------------------------------------------------------------------------------------
408 static void vogl_thread_local_data_destructor(void *pValue)
409 {
410     vogl_delete(static_cast<vogl_thread_local_data *>(pValue));
411
412     pthread_setspecific(g_vogl_thread_local_data, NULL);
413 }
414
415 //----------------------------------------------------------------------------------------------------------------------
416 // vogl_init_thread_local_data
417 //----------------------------------------------------------------------------------------------------------------------
418 static void vogl_init_thread_local_data()
419 {
420     int rc = pthread_key_create(&g_vogl_thread_local_data, vogl_thread_local_data_destructor);
421     if (rc)
422     {
423         vogl_error_printf("%s: pthread_key_create failed!\n", VOGL_FUNCTION_NAME);
424         exit(EXIT_FAILURE);
425     }
426 }
427
428 //----------------------------------------------------------------------------------------------------------------------
429 // vogl_init_logfile
430 //----------------------------------------------------------------------------------------------------------------------
431 static void vogl_init_logfile()
432 {
433     VOGL_FUNC_TRACER
434
435     dynamic_string backbuffer_hash_file;
436     if (g_command_line_params.get_value_as_string(backbuffer_hash_file, "vogl_dump_backbuffer_hashes"))
437     {
438         remove(backbuffer_hash_file.get_ptr());
439         vogl_message_printf("Deleted backbuffer hash file \"%s\"\n", backbuffer_hash_file.get_ptr());
440     }
441
442     dynamic_string log_file(g_command_line_params.get_value_as_string_or_empty("vogl_logfile"));
443     dynamic_string log_file_append(g_command_line_params.get_value_as_string_or_empty("vogl_logfile_append"));
444     if (log_file.is_empty() && log_file_append.is_empty())
445         return;
446
447     dynamic_string filename(log_file_append.is_empty() ? log_file : log_file_append);
448
449     // This purposely leaks, don't care
450     g_vogl_pLog_stream = vogl_new(cfile_stream);
451
452     if (!g_vogl_pLog_stream->open(filename.get_ptr(), cDataStreamWritable, !log_file_append.is_empty()))
453     {
454         vogl_error_printf("%s: Failed opening log file \"%s\"\n", VOGL_FUNCTION_NAME, filename.get_ptr());
455
456         vogl_delete(g_vogl_pLog_stream);
457         g_vogl_pLog_stream = NULL;
458
459         exit(EXIT_FAILURE);
460     }
461     else
462     {
463         vogl_message_printf("Opened log file \"%s\"\n", filename.get_ptr());
464
465         console::set_log_stream(g_vogl_pLog_stream);
466     }
467 }
468
469 //----------------------------------------------------------------------------------------------------------------------
470 // vogl_capture_on_next_swap
471 //----------------------------------------------------------------------------------------------------------------------
472 bool vogl_capture_on_next_swap(uint total_frames, const char *pPath, const char *pBase_filename, vogl_capture_status_callback_func_ptr pStatus_callback, void *pStatus_callback_opaque)
473 {
474     if (!total_frames)
475     {
476         vogl_error_printf("%s: total_frames cannot be 0\n", VOGL_FUNCTION_NAME);
477         return false;
478     }
479
480     scoped_mutex lock(g_vogl_trace_mutex);
481
482     if ((!g_vogl_frames_remaining_to_capture) && (!g_vogl_trace_writer.is_opened()))
483     {
484         g_vogl_total_frames_to_capture = total_frames;
485         g_vogl_capture_path = pPath ? pPath : "";
486         g_vogl_capture_basename = pBase_filename ? pBase_filename : "";
487         g_vogl_pCapture_status_callback = pStatus_callback;
488         g_vogl_pCapture_status_opaque = pStatus_callback_opaque;
489         g_vogl_stop_capturing = false;
490
491         vogl_debug_printf("%s: Total frames: %u, path: \"%s\", base filename: \"%s\", status callback: %p, status callback opaque: %p\n",
492                          VOGL_FUNCTION_NAME, total_frames, pPath, pBase_filename, pStatus_callback, pStatus_callback_opaque);
493     }
494     else
495     {
496         vogl_error_printf("%s: Cannot trigger capturing while a trace is currently in progress\n", VOGL_FUNCTION_NAME);
497         return false;
498     }
499
500     return true;
501 }
502
503 //----------------------------------------------------------------------------------------------------------------------
504 // vogl_stop_capturing
505 //----------------------------------------------------------------------------------------------------------------------
506 bool vogl_stop_capturing()
507 {
508     scoped_mutex lock(g_vogl_trace_mutex);
509
510     if (!g_vogl_trace_writer.is_opened())
511     {
512         vogl_error_printf("%s: Tracing is not active!\n", VOGL_FUNCTION_NAME);
513         return false;
514     }
515
516     g_vogl_stop_capturing = true;
517
518     vogl_debug_printf("%s: Closing trace immediately after next swap\n", VOGL_FUNCTION_NAME);
519
520     return true;
521 }
522
523 //----------------------------------------------------------------------------------------------------------------------
524 // vogl_stop_capturing
525 //----------------------------------------------------------------------------------------------------------------------
526 bool vogl_stop_capturing(vogl_capture_status_callback_func_ptr pStatus_callback, void *pStatus_callback_opaque)
527 {
528     scoped_mutex lock(g_vogl_trace_mutex);
529
530     if (!g_vogl_trace_writer.is_opened())
531     {
532         vogl_error_printf("%s: Tracing is not active!\n", VOGL_FUNCTION_NAME);
533         return false;
534     }
535
536     g_vogl_stop_capturing = true;
537     g_vogl_pCapture_status_callback = pStatus_callback;
538     g_vogl_pCapture_status_opaque = pStatus_callback_opaque;
539
540     vogl_debug_printf("%s: Closing trace immediately after next swap, status callback: %p, status callback opaque: %p\n",
541                      VOGL_FUNCTION_NAME, pStatus_callback, pStatus_callback_opaque);
542
543     return true;
544 }
545
546 //----------------------------------------------------------------------------------------------------------------------
547 // vogl_is_capturing
548 //----------------------------------------------------------------------------------------------------------------------
549 bool vogl_is_capturing()
550 {
551     scoped_mutex lock(g_vogl_trace_mutex);
552
553     return g_vogl_trace_writer.is_opened();
554 }
555
556 //----------------------------------------------------------------------------------------------------------------------
557 // vogl_dump_statistics
558 //----------------------------------------------------------------------------------------------------------------------
559 static void vogl_dump_statistics()
560 {
561     for (uint i = 0; i < VOGL_NUM_ENTRYPOINTS; i++)
562     {
563         if ((!g_vogl_entrypoint_descs[i].m_is_whitelisted) && (g_vogl_entrypoint_descs[i].m_trace_call_counter))
564         {
565             vogl_error_printf("A non-whitelisted function was called during tracing: %s\n", g_vogl_entrypoint_descs[i].m_pName);
566         }
567     }
568 }
569
570 //----------------------------------------------------------------------------------------------------------------------
571 // Deinitialization
572 //----------------------------------------------------------------------------------------------------------------------
573 void vogl_deinit()
574 {
575     VOGL_FUNC_TRACER
576
577     static bool s_already_deinitialized;
578     if (s_already_deinitialized)
579         return;
580     s_already_deinitialized = true;
581
582     VOGL_FUNC_TRACER
583     vogl_end_capture();
584
585     vogl_dump_statistics();
586 }
587
588 //----------------------------------------------------------------------------------------------------------------------
589 // vogl_exception_callback
590 // Note this function can be called ASYNCHRONOUSLY at signal time. Don't call any API function which is not async safe!
591 // TODO: Fix this function!
592 //----------------------------------------------------------------------------------------------------------------------
593 static void vogl_exception_callback()
594 {
595     VOGL_FUNC_TRACER
596
597     fprintf(stderr, "(vogltrace) Flushing log and closing trace files. Note any outstanding async buffer readbacks (for screen capturing) cannot be safely flushed!\n");
598
599     vogl_end_capture(true);
600
601     //uint num_contexts = g_context_manager.get_context_map().size();
602     //if (num_contexts)
603     //   vogl_error_printf("%s: App is exiting with %u active GL context(s)! Any outstanding async buffer readbacks cannot be safely flushed!\n", VOGL_FUNCTION_NAME, num_contexts);
604
605     if (g_vogl_pLog_stream)
606         g_vogl_pLog_stream->flush();
607
608     if (g_vogl_pPrev_exception_callback)
609     {
610         fprintf(stderr, "Calling prev handler\n");
611         (*g_vogl_pPrev_exception_callback)();
612     }
613 }
614
615 //----------------------------------------------------------------------------------------------------------------------
616 // vogl_init_command_line_params
617 //----------------------------------------------------------------------------------------------------------------------
618 static void vogl_init_command_line_params()
619 {
620     VOGL_FUNC_TRACER
621
622     float sleep_time = g_command_line_params.get_value_as_float("vogl_sleep_at_startup");
623     if (sleep_time > 0.0f)
624     {
625         vogl_sleep(static_cast<uint>(ceil(1000.0f * sleep_time)));
626     }
627
628     dynamic_string_array cmd_line_params;
629     char *pEnv_cmd_line = getenv("VOGL_CMD_LINE");
630     if (pEnv_cmd_line)
631     {
632         console::message("Reading command line params from env var\n");
633
634         dynamic_string cmd_line(pEnv_cmd_line);
635         cmd_line.unquote();
636
637         console::message("VOGL_CMD_LINE: %s\n", cmd_line.get_ptr());
638
639         if (!split_command_line_params(cmd_line.get_ptr(), cmd_line_params))
640             console::error("%s: Failed splitting command line params from env var: %s\n", VOGL_FUNCTION_NAME, pEnv_cmd_line);
641     }
642     else if (file_utils::does_file_exist(VOGL_CMD_LINE_OPTIONS_FILE))
643     {
644         console::message("Reading command line params from file \"%s\"\n", VOGL_CMD_LINE_OPTIONS_FILE);
645
646         dynamic_string_array opts;
647         if (!file_utils::read_text_file(VOGL_CMD_LINE_OPTIONS_FILE, opts, file_utils::cRTFTrim | file_utils::cRTFIgnoreEmptyLines | file_utils::cRTFIgnoreCommentedLines | file_utils::cRTFPrintErrorMessages))
648         {
649             console::error("Failed reading command line params from options file \"%s\"\n", VOGL_CMD_LINE_OPTIONS_FILE);
650         }
651         else
652         {
653             dynamic_string all_opts;
654             for (uint i = 0; i < opts.size(); i++)
655                 all_opts.format_append("%s ", opts[i].get_ptr());
656
657             if (!split_command_line_params(all_opts.get_ptr(), cmd_line_params))
658                 console::error("%s: Failed splitting command line params from options file: %s\n", VOGL_FUNCTION_NAME, pEnv_cmd_line);
659         }
660     }
661     else
662     {
663         console::message("Trying to use app's command line options\n");
664
665         cmd_line_params = get_command_line_params();
666     }
667
668     command_line_params::parse_config parse_cfg;
669     parse_cfg.m_single_minus_params = true;
670     parse_cfg.m_double_minus_params = true;
671     parse_cfg.m_ignore_non_params = pEnv_cmd_line ? false : true;
672     parse_cfg.m_ignore_unrecognized_params = pEnv_cmd_line ? false : true;
673     parse_cfg.m_pParam_accept_prefix = "vogl_";
674
675     if (!g_command_line_params.parse(cmd_line_params, VOGL_ARRAY_SIZE(g_command_line_param_descs), g_command_line_param_descs, parse_cfg))
676     {
677         console::error("%s: Failed parsing command line parameters\n", VOGL_FUNCTION_NAME);
678         exit(EXIT_FAILURE);
679     }
680
681     g_dump_gl_calls_flag = g_command_line_params.get_value_as_bool("vogl_dump_gl_calls");
682     g_dump_gl_buffers_flag = g_command_line_params.get_value_as_bool("vogl_dump_gl_buffers");
683     g_dump_gl_shaders_flag = g_command_line_params.get_value_as_bool("vogl_dump_gl_shaders");
684     g_disable_gl_program_binary_flag = g_command_line_params.get_value_as_bool("vogl_disable_gl_program_binary");
685     g_flush_files_after_each_call = g_command_line_params.get_value_as_bool("vogl_flush_files_after_each_call");
686     g_flush_files_after_each_swap = g_command_line_params.get_value_as_bool("vogl_flush_files_after_each_swap");
687
688     g_gather_statistics = g_command_line_params.get_value_as_bool("vogl_dump_stats");
689     g_null_mode = g_command_line_params.get_value_as_bool("vogl_null_mode");
690     g_backtrace_all_calls = g_command_line_params.get_value_as_bool("vogl_backtrace_all_calls");
691     g_disable_client_side_array_tracing = g_command_line_params.get_value_as_bool("vogl_disable_client_side_array_tracing");
692
693     if (g_command_line_params.get_value_as_bool("vogl_dump_gl_full"))
694     {
695         g_dump_gl_calls_flag = true;
696         g_dump_gl_buffers_flag = true;
697         g_dump_gl_shaders_flag = true;
698     }
699 }
700
701 //----------------------------------------------------------------------------------------------------------------------
702 // vogl_check_for_threaded_driver_optimizations
703 //----------------------------------------------------------------------------------------------------------------------
704 static bool vogl_check_for_threaded_driver_optimizations()
705 {
706     VOGL_FUNC_TRACER
707
708     const char *pThreaded_optimizations = getenv("__GL_THREADED_OPTIMIZATIONS");
709     if (!pThreaded_optimizations)
710         return false;
711
712     if (string_to_int(pThreaded_optimizations) != 0)
713     {
714         vogl_error_printf("-----------------------------------------------------------------------------------------------------------------------------------------------------------------\n");
715         vogl_error_printf("%s: __GL_THREADED_OPTIMIZATIONS is defined and non-zero -- it is HIGHLY recommended you remove this environment variable or tracing will be very slow!\n", VOGL_FUNCTION_NAME);
716         vogl_error_printf("-----------------------------------------------------------------------------------------------------------------------------------------------------------------\n");
717         return true;
718     }
719
720     return false;
721 }
722
723 //----------------------------------------------------------------------------------------------------------------------
724 // vogl_global_init - called once on the first intercepted GL/GLX function call
725 //
726 // Guaranteed to be called once only on the first GLX func call.  You can do more or less whatever you
727 // want here (this is after the low-level init, way past global constructor time, and probably during
728 // app init).  This is where cmd lines are processed, remoting is initialized, the log/trace files are
729 // opened, etc.
730 //----------------------------------------------------------------------------------------------------------------------
731 static void vogl_global_init()
732 {
733 #if VOGL_FUNCTION_TRACING
734     fflush(stdout);
735     fflush(stderr);
736     setvbuf(stdout, NULL, _IONBF, 0);
737     setvbuf(stderr, NULL, _IONBF, 0);
738 #endif
739
740     VOGL_FUNC_TRACER
741
742     g_thread_safe_random.seed_from_urandom();
743
744     colorized_console::init();
745
746     console::set_tool_prefix("(vogltrace) ");
747
748     console::message("Command line params: \"%s\"\n", get_command_line().get_ptr());
749
750     bool reliable_rdtsc = vogl::utils::init_rdtsc();
751     if (reliable_rdtsc)
752         console::message("Reliable tsc clocksource found. Using rdtsc.\n");
753     else
754         console::message("Unreliable tsc clocksource found. Not using rdtsc.\n");
755
756     vogl_init_command_line_params();
757
758     vogl_init_logfile();
759
760     vogl_common_lib_global_init();
761
762     if (g_command_line_params.has_key("vogl_tracefile"))
763     {
764         if (!g_vogl_trace_writer.open(g_command_line_params.get_value_as_string_or_empty("vogl_tracefile").get_ptr()))
765         {
766             // FIXME: What do we do? The caller WANTS a full-stream trace, and continuing execution is probably not desired.
767
768             vogl_error_printf("%s: Failed opening trace file, exiting application!\n", VOGL_FUNCTION_NAME);
769
770             exit(EXIT_FAILURE);
771         }
772     }
773
774     if (!g_command_line_params.get_value_as_bool("vogl_disable_signal_interception"))
775     {
776         console::message("Installing exception/signal callbacks\n");
777
778         colorized_console::set_exception_callback();
779
780         g_vogl_pPrev_exception_callback = vogl_set_exception_callback(vogl_exception_callback);
781     }
782
783 #if VOGL_REMOTING
784     console::message("vogl_traceport = %d\n", g_command_line_params.get_value_as_int("vogl_traceport"));
785     vogl_init_listener(g_command_line_params.get_value_as_int("vogl_traceport"));
786 #endif
787
788     vogl_check_for_threaded_driver_optimizations();
789
790     g_backtrace_hashmap.reserve(VOGL_BACKTRACE_HASHMAP_CAPACITY);
791
792     // atexit routines are called in the reverse order in which they were registered. We would like
793         //  our vogl_atexit() routine to be called before anything else (Ie C++ destructors, etc.) So we
794         //  put atexit at the end of vogl_global_init() and another at the end of glXMakeCurrent.
795     atexit(vogl_atexit);
796
797     console::message("vogl_global_init finished\n");
798
799     atomic_increment32(&g_vogl_has_been_initialized);
800 }
801
802 //----------------------------------------------------------------------------------------------------------------------
803 // vogl_check_context
804 //----------------------------------------------------------------------------------------------------------------------
805 static inline void vogl_check_context(gl_entrypoint_id_t id, vogl_context *pContext)
806 {
807     if (!pContext)
808     {
809         const char *pName = g_vogl_entrypoint_descs[id].m_pName;
810
811         // FIXME: Check to see if the func is in a non-GL category, anything but this!
812         // These are rare enough that this is not the biggest fire right now.
813         if ((pName[0] != 'g') || (pName[1] != 'l') || (pName[2] != 'X'))
814         {
815             uint n = vogl_strlen(pName);
816             if ((n <= 3) || ((pName[n - 3] != 'R') || (pName[n - 2] != 'A') || (pName[n - 1] != 'D')))
817             {
818                 vogl_error_printf("%s: OpenGL function \"%s\" called without an active context!\n", VOGL_FUNCTION_NAME, pName);
819             }
820         }
821     }
822 }
823
824 //----------------------------------------------------------------------------------------------------------------------
825 // vogl_entrypoint_prolog
826 // This function gets called on every GL call - be careful what you do here!
827 //----------------------------------------------------------------------------------------------------------------------
828 static inline vogl_thread_local_data *vogl_entrypoint_prolog(gl_entrypoint_id_t entrypoint_id)
829 {
830     if (!g_vogl_has_been_initialized)
831     {
832         pthread_once(&g_vogl_init_once_control, vogl_global_init);
833     }
834
835     vogl_thread_local_data *pTLS_data = vogl_get_or_create_thread_local_data();
836
837     // Something very odd is going on - the driver somehow called itself while WE where calling it, so let's immediately bail for safety.
838     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)
839         return pTLS_data;
840
841     // Atomically inc the 64-bit counter
842     // TODO: Make a helper for this
843     int64_t cur_ctr;
844     do
845     {
846         cur_ctr = g_vogl_entrypoint_descs[entrypoint_id].m_trace_call_counter;
847     } while (atomic_compare_exchange64(&g_vogl_entrypoint_descs[entrypoint_id].m_trace_call_counter, cur_ctr + 1, cur_ctr) != cur_ctr);
848
849     if ((!cur_ctr) && (!g_vogl_entrypoint_descs[entrypoint_id].m_is_whitelisted))
850         vogl_error_printf("%s: Function \"%s\" not yet in function whitelist, this API will not be replayed and this trace will not be replayable!\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[entrypoint_id].m_pName);
851
852     vogl_check_context(entrypoint_id, pTLS_data->m_pContext);
853
854     return pTLS_data;
855 }
856
857 //----------------------------------------------------------------------------------------------------------------------
858 // struct gl_vertex_attribute
859 //----------------------------------------------------------------------------------------------------------------------
860 struct gl_vertex_attribute
861 {
862     GLint m_size;
863     GLenum m_type;
864     GLboolean m_normalized;
865     GLsizei m_stride;
866     const GLvoid *m_pointer;
867     GLuint m_buffer;
868     bool m_enabled;
869
870     void clear()
871     {
872         m_size = 4;
873         m_type = GL_FLOAT;
874         m_normalized = false;
875         m_stride = 0;
876         m_pointer = NULL;
877         m_buffer = 0;
878         m_enabled = 0;
879     }
880 };
881 const uint VOGL_MAX_SUPPORTED_GL_VERTEX_ATTRIBUTES = 32;
882
883 typedef vogl::hash_map<GLenum, GLuint> gl_buffer_binding_map;
884 //----------------------------------------------------------------------------------------------------------------------
885 // struct gl_buffer_desc
886 //----------------------------------------------------------------------------------------------------------------------
887 struct gl_buffer_desc
888 {
889     GLuint m_handle;
890     int64_t m_size;
891     GLenum m_usage;
892
893     void *m_pMap;
894     int64_t m_map_ofs;
895     int64_t m_map_size;
896
897     // FIXME: Don't alias this!
898     // NOTE: This is a GLbitfield of m_map_range is true, otherwise it's a GL_ENUM!!
899     GLbitfield m_map_access;
900
901     bool m_map_range;
902
903     struct flushed_range
904     {
905         inline flushed_range(int64_t ofs, int64_t size)
906             : m_ofs(ofs), m_size(size)
907         {
908         }
909         inline flushed_range()
910         {
911         }
912
913         int64_t m_ofs;
914         int64_t m_size;
915     };
916     vogl::vector<flushed_range> m_flushed_ranges;
917
918     inline gl_buffer_desc()
919     {
920         clear();
921     }
922     inline gl_buffer_desc(GLuint handle)
923     {
924         clear();
925         m_handle = handle;
926     }
927
928     inline void clear()
929     {
930         m_handle = 0;
931         m_size = 0;
932         m_usage = 0;
933         m_pMap = NULL;
934         m_map_ofs = 0;
935         m_map_size = 0;
936         m_map_access = 0;
937         m_map_range = false;
938         m_flushed_ranges.clear();
939     }
940 };
941
942 typedef vogl::hash_map<GLuint, gl_buffer_desc> gl_buffer_desc_map;
943
944 //----------------------------------------------------------------------------------------------------------------------
945 // vogl_scoped_gl_error_absorber
946 //----------------------------------------------------------------------------------------------------------------------
947 class vogl_scoped_gl_error_absorber
948 {
949     vogl_context *m_pContext;
950
951 public:
952     vogl_scoped_gl_error_absorber(vogl_context *pContext);
953     ~vogl_scoped_gl_error_absorber();
954 };
955
956 //----------------------------------------------------------------------------------------------------------------------
957 // class vogl_tracer_snapshot_handle_remapper
958 //----------------------------------------------------------------------------------------------------------------------
959 class vogl_context_handle_remapper : public vogl_handle_remapper
960 {
961     VOGL_NO_COPY_OR_ASSIGNMENT_OP(vogl_context_handle_remapper);
962
963     vogl_context *m_pContext;
964
965 public:
966     vogl_context_handle_remapper(vogl_context *pContext)
967         : m_pContext(pContext)
968     {
969     }
970
971     virtual bool is_default_remapper() const
972     {
973         return false;
974     }
975
976     virtual bool is_valid_handle(vogl_namespace_t handle_namespace, uint64_t from_handle);
977
978     virtual bool determine_from_object_target(vogl_namespace_t handle_namespace, uint64_t from_handle, GLenum &target);
979
980     virtual bool determine_to_object_target(vogl_namespace_t handle_namespace, uint64_t to_handle, GLenum &target)
981     {
982         return determine_from_object_target(handle_namespace, to_handle, target);
983     }
984 };
985
986 //----------------------------------------------------------------------------------------------------------------------
987 // class vogl_context
988 //----------------------------------------------------------------------------------------------------------------------
989 class vogl_context
990 {
991     VOGL_NO_COPY_OR_ASSIGNMENT_OP(vogl_context);
992
993 public:
994     vogl_context(GLXContext handle)
995         : m_ref_count(1),
996           m_deleted_flag(false),
997           m_pShared_state(this),
998           m_context_handle(handle),
999           m_sharelist_handle(NULL),
1000           m_pDpy(NULL),
1001           m_drawable(None),
1002           m_read_drawable(None),
1003           m_render_type(0),
1004           m_fb_config(NULL),
1005           m_direct(false),
1006           m_created_from_attribs(false),
1007           m_current_thread(0),
1008           m_max_vertex_attribs(0),
1009           m_has_been_made_current(false),
1010           m_window_width(-1),
1011           m_window_height(-1),
1012           m_frame_index(0),
1013           m_creation_func(VOGL_ENTRYPOINT_INVALID),
1014           m_latched_gl_error(GL_NO_ERROR),
1015           m_cur_program(0),
1016           m_in_gl_begin(false),
1017           m_uses_client_side_arrays(false),
1018           m_handle_remapper(this),
1019           m_current_display_list_handle(-1),
1020           m_current_display_list_mode(GL_NONE)
1021     {
1022         utils::zero_object(m_xvisual_info);
1023     }
1024
1025     ~vogl_context()
1026     {
1027     }
1028
1029     int get_ref_count() const
1030     {
1031         return m_ref_count;
1032     }
1033     int add_ref()
1034     {
1035         ++m_ref_count;
1036         return m_ref_count;
1037     }
1038     int del_ref()
1039     {
1040         --m_ref_count;
1041         return m_ref_count;
1042     }
1043
1044     bool get_deleted_flag() const
1045     {
1046         return m_deleted_flag;
1047     }
1048     void set_deleted_flag(bool val)
1049     {
1050         m_deleted_flag = val;
1051     }
1052
1053     vogl_context *get_shared_state() const
1054     {
1055         return m_pShared_state;
1056     }
1057     void set_shared_context(vogl_context *pContext)
1058     {
1059         m_pShared_state = pContext;
1060     }
1061
1062     vogl_context *get_context_state()
1063     {
1064         return this;
1065     }
1066
1067     bool is_root_context() const
1068     {
1069         return this == m_pShared_state;
1070     }
1071     bool is_share_context() const
1072     {
1073         return this != m_pShared_state;
1074     }
1075
1076     GLXContext get_context_handle() const
1077     {
1078         return m_context_handle;
1079     }
1080
1081     GLXContext get_sharelist_handle() const
1082     {
1083         return m_sharelist_handle;
1084     }
1085     void set_sharelist_handle(GLXContext context)
1086     {
1087         m_sharelist_handle = context;
1088     }
1089
1090     const Display *get_display() const
1091     {
1092         return m_pDpy;
1093     }
1094     void set_display(const Display *pDpy)
1095     {
1096         m_pDpy = pDpy;
1097     }
1098
1099     void set_drawable(GLXDrawable drawable)
1100     {
1101         m_drawable = drawable;
1102     }
1103     GLXDrawable get_drawable() const
1104     {
1105         return m_drawable;
1106     }
1107
1108     void set_read_drawable(GLXDrawable read)
1109     {
1110         m_read_drawable = read;
1111     }
1112     GLXDrawable get_read_drawable() const
1113     {
1114         return m_read_drawable;
1115     }
1116
1117     void set_render_type(int render_type)
1118     {
1119         m_render_type = render_type;
1120     }
1121     int get_render_type() const
1122     {
1123         return m_render_type;
1124     }
1125
1126     bool get_direct() const
1127     {
1128         return m_direct;
1129     }
1130     void set_direct(bool direct)
1131     {
1132         m_direct = direct;
1133     }
1134
1135     const XVisualInfo &get_xvisual_info() const
1136     {
1137         return m_xvisual_info;
1138     }
1139     void set_xvisual_info(const XVisualInfo *p)
1140     {
1141         m_xvisual_info = *p;
1142     }
1143
1144     bool get_created_from_attribs() const
1145     {
1146         return m_created_from_attribs;
1147     }
1148     void set_created_from_attribs(bool created_from_attribs)
1149     {
1150         m_created_from_attribs = created_from_attribs;
1151     }
1152
1153     uint64_t get_current_thread() const
1154     {
1155         return m_current_thread;
1156     }
1157     void set_current_thread(uint64_t tid)
1158     {
1159         m_current_thread = tid;
1160     }
1161
1162     void set_creation_func(gl_entrypoint_id_t func)
1163     {
1164         m_creation_func = func;
1165     }
1166     gl_entrypoint_id_t get_creation_func() const
1167     {
1168         return m_creation_func;
1169     }
1170
1171     void set_fb_config(GLXFBConfig config)
1172     {
1173         m_fb_config = config;
1174     }
1175     GLXFBConfig get_fb_config()
1176     {
1177         return m_fb_config;
1178     }
1179
1180     inline bool get_has_been_made_current() const
1181     {
1182         return m_has_been_made_current;
1183     }
1184
1185     void init()
1186     {
1187         VOGL_ASSERT(m_creation_func != VOGL_ENTRYPOINT_INVALID);
1188         if (m_creation_func == VOGL_ENTRYPOINT_INVALID)
1189             return;
1190
1191         vogl_context_attribs attribs(m_attrib_list.get_ptr(), m_attrib_list.size());
1192         m_context_desc.init(m_creation_func, m_direct, (vogl_trace_ptr_value)m_context_handle, (vogl_trace_ptr_value)m_sharelist_handle, attribs);
1193     }
1194
1195     void set_attrib_list(const int *attrib_list)
1196     {
1197         int n = vogl_determine_attrib_list_array_size(attrib_list);
1198         m_attrib_list.clear();
1199         m_attrib_list.append(attrib_list, n);
1200     }
1201
1202     const vogl::vector<int> &get_attrib_list() const
1203     {
1204         return m_attrib_list;
1205     }
1206
1207     // true if a core profile was explictly requested in the attrib list.
1208     inline bool requested_core_profile() const
1209     {
1210         return (m_context_desc.get_attribs().get_value_or_default(GLX_CONTEXT_PROFILE_MASK_ARB) & GLX_CONTEXT_CORE_PROFILE_BIT_ARB) != 0;
1211     }
1212
1213     // true if a compat profile was explictly requested in the attrib list.
1214     inline bool requested_compatibility_profile() const
1215     {
1216         return (m_context_desc.get_attribs().get_value_or_default(GLX_CONTEXT_PROFILE_MASK_ARB) & GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB) != 0;
1217     }
1218
1219     // true if a debug context was explictly requested in the attrib list.
1220     inline bool requested_debug_context() const
1221     {
1222         return (m_context_desc.get_attribs().get_value_or_default(GLX_CONTEXT_FLAGS_ARB) & GLX_CONTEXT_DEBUG_BIT_ARB) != 0;
1223     }
1224
1225     // true if a forward compatible context was explictly requested in the attrib list.
1226     inline bool requested_forward_compatible() const
1227     {
1228         return (m_context_desc.get_attribs().get_value_or_default(GLX_CONTEXT_FLAGS_ARB) & GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB) != 0;
1229     }
1230
1231     // true if this is a core profile context (the context must have been made current first)
1232     inline bool is_core_profile() const
1233     {
1234         return m_context_info.is_core_profile();
1235     }
1236
1237     // only valid after the first MakeCurrent
1238     inline GLuint get_max_vertex_attribs() const
1239     {
1240         return m_max_vertex_attribs;
1241     }
1242     inline GLuint get_max_texture_coords() const
1243     {
1244         return m_context_info.get_max_texture_coords();
1245     }
1246     inline GLuint get_max_texture_units() const
1247     {
1248         return m_context_info.get_max_texture_units();
1249     }
1250
1251     const vogl_context_desc &get_context_desc() const
1252     {
1253         return m_context_desc;
1254     }
1255     const vogl_context_info &get_context_info() const
1256     {
1257         return m_context_info;
1258     }
1259
1260     const vogl_capture_context_params &get_capture_context_params() const
1261     {
1262         return m_capture_context_params;
1263     }
1264     vogl_capture_context_params &get_capture_context_params()
1265     {
1266         return m_capture_context_params;
1267     }
1268
1269     const vogl_framebuffer_capturer &get_framebuffer_capturer() const
1270     {
1271         return m_framebuffer_capturer;
1272     }
1273     vogl_framebuffer_capturer &get_framebuffer_capturer()
1274     {
1275         return m_framebuffer_capturer;
1276     }
1277
1278     inline void on_make_current()
1279     {
1280         set_current_thread(vogl_get_current_kernel_thread_id());
1281
1282         if (!m_has_been_made_current)
1283         {
1284             vogl_scoped_gl_error_absorber gl_error_absorber(this);
1285             VOGL_NOTE_UNUSED(gl_error_absorber);
1286
1287             m_max_vertex_attribs = vogl_get_gl_integer(GL_MAX_VERTEX_ATTRIBS);
1288             if (m_max_vertex_attribs > VOGL_MAX_SUPPORTED_GL_VERTEX_ATTRIBUTES)
1289             {
1290                 vogl_error_printf("%s: GL_MAX_VERTEX_ATTRIBS is larger than supported (%u, max supported is %u), trace may not contain all client side vertex attribs\n", VOGL_METHOD_NAME, m_max_vertex_attribs, VOGL_MAX_SUPPORTED_GL_VERTEX_ATTRIBUTES);
1291                 m_max_vertex_attribs = VOGL_MAX_SUPPORTED_GL_VERTEX_ATTRIBUTES;
1292             }
1293
1294             if (!m_context_info.init(m_context_desc))
1295             {
1296                 vogl_error_printf("%s: Failed initializing m_context_info!\n", VOGL_METHOD_NAME);
1297             }
1298
1299             if (!m_has_been_made_current)
1300             {
1301                 on_first_make_current();
1302             }
1303
1304             m_has_been_made_current = true;
1305         }
1306     }
1307
1308     bool is_context_current()
1309     {
1310         if (!GL_ENTRYPOINT(glXGetCurrentContext))
1311             return false;
1312
1313         // Double check that the context that we think is current is actually current.
1314         GLXContext cur_context = GL_ENTRYPOINT(glXGetCurrentContext)();
1315         if (!cur_context)
1316         {
1317             VOGL_ASSERT(!m_current_thread);
1318             return false;
1319         }
1320
1321         if (get_context_handle() != cur_context)
1322         {
1323             VOGL_ASSERT(!m_current_thread);
1324             return false;
1325         }
1326
1327         VOGL_ASSERT(vogl_get_current_kernel_thread_id() == m_current_thread);
1328
1329         return true;
1330     }
1331
1332     // This will be called with the context made current, however the TLS struct won't indicate that this context is current, and m_current_thread will not reflect this either!
1333     // Note this will be called before glXMakeCurrent() when contexts are switching, and it's possible the call could fail (and this context could remain current). So don't do anything drastic here.
1334     void on_release_current_prolog()
1335     {
1336         if (!is_context_current())
1337             return;
1338
1339         peek_and_record_gl_error();
1340
1341         m_framebuffer_capturer.flush();
1342
1343         peek_and_drop_gl_error();
1344     }
1345
1346     void on_release_current_epilog()
1347     {
1348         set_current_thread(0);
1349     }
1350
1351     void on_destroy_prolog()
1352     {
1353         if (!is_context_current())
1354         {
1355             m_framebuffer_capturer.deinit(false);
1356             return;
1357         }
1358
1359         peek_and_record_gl_error();
1360
1361         m_framebuffer_capturer.deinit(true);
1362
1363         peek_and_drop_gl_error();
1364     }
1365
1366     // buffer handle and target shadowing
1367     void gen_buffers(GLsizei n, GLuint *pIDs)
1368     {
1369         if (!pIDs)
1370             return;
1371
1372         vogl_scoped_context_shadow_lock lock;
1373
1374         for (GLsizei i = 0; i < n; i++)
1375         {
1376             GLuint id = pIDs[i];
1377             if (id)
1378             {
1379                 if (!get_shared_state()->m_capture_context_params.m_buffer_targets.insert(id, GL_NONE).second)
1380                 {
1381                     vogl_error_printf("%s: Can't insert buffer handle 0x%04X into buffer target map!\n", VOGL_METHOD_NAME, id);
1382                 }
1383             }
1384         }
1385     }
1386
1387     void bind_buffer(GLenum target, GLuint id)
1388     {
1389         if (!id)
1390             return;
1391
1392         vogl_scoped_context_shadow_lock lock;
1393
1394         GLenum *pTarget = get_shared_state()->m_capture_context_params.m_buffer_targets.find_value(id);
1395         if (!pTarget)
1396         {
1397             vogl_error_printf("%s: Unable to find buffer handle 0x%04X in buffer target map!\n", VOGL_METHOD_NAME, id);
1398             return;
1399         }
1400
1401         if (*pTarget == GL_NONE)
1402         {
1403             // This is the first bind, so record the target. Otherwise they are rebinding to a different target, which shouldn't matter to us for snapshotting purposes (right??).
1404             *pTarget = target;
1405         }
1406     }
1407
1408     void delete_buffers(GLsizei n, const GLuint *buffers)
1409     {
1410         if ((!n) || (!buffers))
1411             return;
1412
1413         vogl_scoped_context_shadow_lock lock;
1414
1415         for (GLsizei i = 0; i < n; i++)
1416         {
1417             GLuint buffer = buffers[i];
1418             if (!buffer)
1419                 continue;
1420
1421             get_shared_state()->m_buffer_descs.erase(buffer);
1422
1423             if (!get_shared_state()->m_capture_context_params.m_buffer_targets.erase(buffer))
1424             {
1425                 vogl_error_printf("%s: Can't erase buffer handle 0x%04X from buffer target map!\n", VOGL_METHOD_NAME, buffer);
1426             }
1427         }
1428     }
1429
1430     inline gl_buffer_desc &get_or_create_buffer_desc(GLuint handle)
1431     {
1432         vogl_scoped_context_shadow_lock lock;
1433
1434         gl_buffer_desc_map::iterator buf_it(get_shared_state()->m_buffer_descs.find(handle));
1435         if (buf_it != get_shared_state()->m_buffer_descs.end())
1436             return buf_it->second;
1437
1438         gl_buffer_desc desc(handle);
1439         return (get_shared_state()->m_buffer_descs.insert(handle, desc).first)->second;
1440     }
1441
1442     inline uint get_total_mapped_buffers() const
1443     {
1444         vogl_scoped_context_shadow_lock lock;
1445
1446         uint total = 0;
1447         for (gl_buffer_desc_map::const_iterator it = get_shared_state()->m_buffer_descs.begin(); it != get_shared_state()->m_buffer_descs.end(); ++it)
1448         {
1449             if (it->second.m_pMap)
1450                 total++;
1451         }
1452         return total;
1453     }
1454
1455     inline void set_window_dimensions(int width, int height)
1456     {
1457         m_window_width = width;
1458         m_window_height = height;
1459     }
1460
1461     inline int get_window_width() const
1462     {
1463         return m_window_width;
1464     }
1465     inline int get_window_height() const
1466     {
1467         return m_window_height;
1468     }
1469
1470     uint64_t get_frame_index() const
1471     {
1472         return m_frame_index;
1473     }
1474     void set_frame_index(uint f)
1475     {
1476         m_frame_index = f;
1477     }
1478     void inc_frame_index()
1479     {
1480         m_frame_index++;
1481     }
1482
1483     GLuint get_cur_program() const
1484     {
1485         return m_cur_program;
1486     }
1487     void set_cur_program(GLuint program)
1488     {
1489         m_cur_program = program;
1490     }
1491
1492     GLenum get_latched_gl_error() const
1493     {
1494         return m_latched_gl_error;
1495     }
1496     bool has_latched_gl_error() const
1497     {
1498         return m_latched_gl_error != GL_NO_ERROR;
1499     }
1500     void clear_latched_gl_error()
1501     {
1502         m_latched_gl_error = GL_NO_ERROR;
1503     }
1504     void set_latched_gl_error(GLenum err)
1505     {
1506         m_latched_gl_error = err;
1507     }
1508
1509     // Returns the context's most recent GL error, NOT the currently latched error.
1510     GLenum peek_and_record_gl_error()
1511     {
1512         if (m_in_gl_begin)
1513             return GL_NO_ERROR;
1514
1515         GLenum gl_err = GL_ENTRYPOINT(glGetError)();
1516
1517         if (gl_err != GL_NO_ERROR)
1518         {
1519             if (!has_latched_gl_error())
1520             {
1521                 set_latched_gl_error(gl_err);
1522
1523                 vogl_warning_printf("%s: GL error %s occurred sometime before this function was called. This error has been latched by the tracer onto the tracer's context shadow struct. This error will be reported to the caller the next time glGetError() is called, and will mask any GL error returned by that future call.\n",
1524                                    VOGL_METHOD_NAME, g_gl_enums.find_gl_error_name(gl_err));
1525             }
1526             else
1527             {
1528                 vogl_warning_printf("%s: GL error %s occurred sometime before this function was called. The tracer already had a latched GL error of %s. This error will be suppressed by the tracer (because it would have been by GL itself if the tracer was not present).\n",
1529                                    VOGL_METHOD_NAME, g_gl_enums.find_gl_error_name(gl_err), g_gl_enums.find_gl_error_name(get_latched_gl_error()));
1530             }
1531         }
1532
1533         return gl_err;
1534     }
1535
1536     // Gets and drops the current GL error (this is done to "hide" any errors we've introduced by tracing from the client app).
1537     // Be sure to retrieve and latch any client errors by calling peek_and_record_gl_error() first!
1538     GLenum peek_and_drop_gl_error()
1539     {
1540         if (m_in_gl_begin)
1541             return GL_NO_ERROR;
1542
1543         GLenum gl_err = GL_ENTRYPOINT(glGetError)();
1544
1545         if (gl_err != GL_NO_ERROR)
1546         {
1547             vogl_error_printf("%s: GL error %s occurred internally while libvogltrace was making GL calls. This GL error will not be seen by the client app (THIS SHOULD NOT HAPPEN)\n", VOGL_METHOD_NAME, g_gl_enums.find_gl_error_name(gl_err));
1548         }
1549
1550         return gl_err;
1551     }
1552
1553     // TODO: Modify each shadowing method to print detailed error messages
1554     // TODO: Move all these shadow methods right into vogl_capture_context_params
1555
1556     // Texture handle shadowing
1557     void gen_textures(GLsizei n, GLuint *pTextures)
1558     {
1559         if (!pTextures)
1560             return;
1561
1562         vogl_scoped_context_shadow_lock lock;
1563
1564         for (GLsizei i = 0; i < n; i++)
1565         {
1566             GLuint handle = pTextures[i];
1567             if (handle)
1568             {
1569                 if (!get_shared_state()->m_capture_context_params.m_textures.insert(handle, handle, GL_NONE))
1570                 {
1571                     vogl_warning_printf("%s: Unable to add texture handle %u to texture handle shadow map!\n", VOGL_METHOD_NAME, handle);
1572                 }
1573             }
1574         }
1575     }
1576
1577     void del_textures(GLsizei n, const GLuint *pTextures)
1578     {
1579         if (!pTextures)
1580             return;
1581
1582         vogl_scoped_context_shadow_lock lock;
1583
1584         for (GLsizei i = 0; i < n; i++)
1585         {
1586             GLuint handle = pTextures[i];
1587             if (handle)
1588             {
1589                 if (!get_shared_state()->m_capture_context_params.m_textures.erase(handle))
1590                 {
1591                     vogl_warning_printf("%s: Failed erasing handle %u from texture handle shadow map!\n", VOGL_METHOD_NAME, handle);
1592                 }
1593             }
1594         }
1595     }
1596
1597     void bind_texture(GLenum target, GLuint texture)
1598     {
1599         if (texture)
1600         {
1601             vogl_scoped_context_shadow_lock lock;
1602
1603             get_shared_state()->m_capture_context_params.m_textures.update(texture, target);
1604         }
1605     }
1606
1607     void bind_texture_conditionally(GLenum target, GLuint texture)
1608     {
1609         if (texture)
1610         {
1611             vogl_scoped_context_shadow_lock lock;
1612
1613             get_shared_state()->m_capture_context_params.m_textures.conditional_update(texture, GL_NONE, target);
1614         }
1615     }
1616
1617     // Framebuffer handle shadowing
1618     void gen_framebuffers(GLsizei n, GLuint *pFramebuffers)
1619     {
1620         if (!pFramebuffers)
1621             return;
1622         for (GLsizei i = 0; i < n; i++)
1623         {
1624             GLuint handle = pFramebuffers[i];
1625             if (handle)
1626                 get_context_state()->m_capture_context_params.m_framebuffers.insert(handle);
1627         }
1628     }
1629
1630     void del_framebuffers(GLsizei n, const GLuint *pFramebuffers)
1631     {
1632         if (!pFramebuffers)
1633             return;
1634         for (GLsizei i = 0; i < n; i++)
1635         {
1636             GLuint handle = pFramebuffers[i];
1637             if (handle)
1638                 get_context_state()->m_capture_context_params.m_framebuffers.erase(handle);
1639         }
1640     }
1641
1642     // VAO handle shadowing
1643     void gen_vertexarrays(GLsizei n, GLuint *pArrays)
1644     {
1645         if (!pArrays)
1646             return;
1647         for (GLsizei i = 0; i < n; i++)
1648         {
1649             GLuint handle = pArrays[i];
1650             if (handle)
1651                 get_context_state()->m_capture_context_params.m_vaos.insert(handle);
1652         }
1653     }
1654
1655     void del_vertexarrays(GLsizei n, const GLuint *pArrays)
1656     {
1657         if (!pArrays)
1658             return;
1659         for (GLsizei i = 0; i < n; i++)
1660         {
1661             GLuint handle = pArrays[i];
1662             if (handle)
1663                 get_context_state()->m_capture_context_params.m_vaos.erase(handle);
1664         }
1665     }
1666
1667     void bind_vertexarray(GLuint array)
1668     {
1669         if (array)
1670             get_context_state()->m_capture_context_params.m_vaos.insert(array);
1671     }
1672
1673     // sync handle shadowing
1674     void gen_sync(GLsync sync)
1675     {
1676         if (sync)
1677         {
1678             vogl_scoped_context_shadow_lock lock;
1679
1680             get_shared_state()->m_capture_context_params.m_syncs.insert((uint64_t)sync);
1681         }
1682     }
1683
1684     void del_sync(GLsync sync)
1685     {
1686         if (sync)
1687         {
1688             vogl_scoped_context_shadow_lock lock;
1689
1690             get_shared_state()->m_capture_context_params.m_syncs.erase((uint64_t)sync);
1691         }
1692     }
1693
1694     // sampler handle shadowing
1695     void gen_samplers(GLsizei n, const GLuint *pSamplers)
1696     {
1697         if (!pSamplers)
1698             return;
1699
1700         vogl_scoped_context_shadow_lock lock;
1701
1702         for (GLsizei i = 0; i < n; i++)
1703         {
1704             GLuint handle = pSamplers[i];
1705             if (handle)
1706                 get_shared_state()->m_capture_context_params.m_samplers.insert(handle);
1707         }
1708     }
1709
1710     void del_samplers(GLsizei n, const GLuint *pSamplers)
1711     {
1712         if (!pSamplers)
1713             return;
1714
1715         vogl_scoped_context_shadow_lock lock;
1716
1717         for (GLsizei i = 0; i < n; i++)
1718         {
1719             GLuint handle = pSamplers[i];
1720             if (handle)
1721                 get_shared_state()->m_capture_context_params.m_samplers.erase(handle);
1722         }
1723     }
1724
1725     // query handle and target shadowing
1726     void gen_queries(GLsizei n, GLuint *pIDs)
1727     {
1728         if (!pIDs)
1729             return;
1730
1731         vogl_scoped_context_shadow_lock lock;
1732
1733         for (GLsizei i = 0; i < n; i++)
1734         {
1735             GLuint id = pIDs[i];
1736             if (id)
1737                 get_shared_state()->m_capture_context_params.m_query_targets.insert(id, GL_NONE);
1738         }
1739     }
1740
1741     void del_queries(GLsizei n, const GLuint *pIDs)
1742     {
1743         if (!pIDs)
1744             return;
1745
1746         vogl_scoped_context_shadow_lock lock;
1747
1748         for (GLsizei i = 0; i < n; i++)
1749         {
1750             GLuint id = pIDs[i];
1751             if (id)
1752                 get_shared_state()->m_capture_context_params.m_query_targets.erase(id);
1753         }
1754     }
1755
1756     void begin_query(GLenum target, GLuint id)
1757     {
1758         if (id)
1759         {
1760             vogl_scoped_context_shadow_lock lock;
1761
1762             if (get_shared_state()->m_capture_context_params.m_query_targets.contains(id))
1763             {
1764                 get_shared_state()->m_capture_context_params.m_query_targets[id] = target;
1765             }
1766             else
1767             {
1768                 vogl_error_printf("%s: Unable to find query target handle %u in query target handle shadow\n", VOGL_METHOD_NAME, id);
1769             }
1770         }
1771     }
1772
1773     // arb programs
1774     void gen_arb_programs(GLsizei n, const GLuint *pHandles)
1775     {
1776         if (!pHandles)
1777             return;
1778
1779         vogl_scoped_context_shadow_lock lock;
1780
1781         for (GLsizei i = 0; i < n; i++)
1782         {
1783             GLuint handle = pHandles[i];
1784             if (handle)
1785                 get_shared_state()->m_capture_context_params.m_arb_program_targets.insert(handle, GL_NONE);
1786         }
1787     }
1788
1789     void bind_arb_program(GLenum target, GLuint handle)
1790     {
1791         if (handle)
1792         {
1793             vogl_scoped_context_shadow_lock lock;
1794
1795             get_shared_state()->m_capture_context_params.m_arb_program_targets[handle] = target;
1796         }
1797     }
1798
1799     void del_arb_programs(GLsizei n, const GLuint *pHandles)
1800     {
1801         if (!pHandles)
1802             return;
1803
1804         vogl_scoped_context_shadow_lock lock;
1805
1806         for (GLsizei i = 0; i < n; i++)
1807         {
1808             GLuint handle = pHandles[i];
1809             if (handle)
1810                 get_shared_state()->m_capture_context_params.m_arb_program_targets.erase(handle);
1811         }
1812     }
1813
1814     // renderbuffer handle shadowing
1815     void gen_render_buffers(GLsizei n, GLuint *pIDs)
1816     {
1817         if (!pIDs)
1818             return;
1819
1820         vogl_scoped_context_shadow_lock lock;
1821
1822         for (GLsizei i = 0; i < n; i++)
1823         {
1824             GLuint id = pIDs[i];
1825             if (id)
1826             {
1827                 if (!get_shared_state()->m_capture_context_params.m_rbos.insert(id, id, GL_NONE))
1828                 {
1829                     vogl_error_printf("%s: Can't insert render buffer handle 0x%04X into render buffer shadow!\n", VOGL_METHOD_NAME, id);
1830                 }
1831             }
1832         }
1833     }
1834
1835     void del_render_buffers(GLsizei n, const GLuint *buffers)
1836     {
1837         if ((!n) || (!buffers))
1838             return;
1839
1840         vogl_scoped_context_shadow_lock lock;
1841
1842         for (GLsizei i = 0; i < n; i++)
1843         {
1844             GLuint buffer = buffers[i];
1845             if (!buffer)
1846                 continue;
1847
1848             if (!get_shared_state()->m_capture_context_params.m_rbos.erase(buffer))
1849             {
1850                 vogl_error_printf("%s: Can't erase render buffer handle 0x%04X from render buffer shadow!\n", VOGL_METHOD_NAME, buffer);
1851             }
1852         }
1853     }
1854
1855     // program/shader shadowing
1856     GLuint handle_create_program(gl_entrypoint_id_t id)
1857     {
1858         GLuint handle;
1859
1860         if (id == VOGL_ENTRYPOINT_glCreateProgram)
1861             handle = GL_ENTRYPOINT(glCreateProgram)();
1862         else
1863         {
1864             VOGL_ASSERT(id == VOGL_ENTRYPOINT_glCreateProgramObjectARB);
1865             handle = GL_ENTRYPOINT(glCreateProgramObjectARB)();
1866         }
1867
1868         vogl_scoped_context_shadow_lock lock;
1869
1870         if (handle)
1871         {
1872             if (!get_shared_state()->m_capture_context_params.m_objs.update(handle, handle, VOGL_PROGRAM_OBJECT))
1873                 vogl_error_printf("%s: Failed inserting program handle %u into object shadow!\n", VOGL_METHOD_NAME, handle);
1874         }
1875         else
1876         {
1877             vogl_error_printf("%s: glCreateProgram/glCreateProgramObjectARB on handle %u failed!\n", VOGL_METHOD_NAME, handle);
1878         }
1879
1880         return handle;
1881     }
1882
1883     GLuint handle_create_shader(gl_entrypoint_id_t id, GLenum type)
1884     {
1885         GLuint handle;
1886
1887         if (id == VOGL_ENTRYPOINT_glCreateShader)
1888             handle = GL_ENTRYPOINT(glCreateShader)(type);
1889         else
1890         {
1891             VOGL_ASSERT(id == VOGL_ENTRYPOINT_glCreateShaderObjectARB);
1892             handle = GL_ENTRYPOINT(glCreateShaderObjectARB)(type);
1893         }
1894
1895         vogl_scoped_context_shadow_lock lock;
1896
1897         if (handle)
1898         {
1899             if (!get_shared_state()->m_capture_context_params.m_objs.update(handle, handle, VOGL_SHADER_OBJECT))
1900                 vogl_error_printf("%s: Failed inserting shader handle %u into object shadow! (type=%s)\n", VOGL_METHOD_NAME, handle, g_gl_enums.find_gl_name(type));
1901         }
1902         else
1903         {
1904             vogl_error_printf("%s: glCreateShader/glCreateShaderObjectARB on handle %u failed! (type=%s)\n", VOGL_METHOD_NAME, handle, g_gl_enums.find_gl_name(type));
1905         }
1906
1907         return handle;
1908     }
1909
1910     bool has_linked_program_snapshot(GLuint handle)
1911     {
1912         vogl_scoped_context_shadow_lock lock;
1913
1914         return get_shared_state()->m_capture_context_params.m_linked_programs.find_snapshot(handle) != NULL;
1915     }
1916
1917     bool add_linked_program_snapshot(GLuint handle, GLenum binary_format = GL_NONE, const void *pBinary = NULL, uint binary_size = 0)
1918     {
1919         vogl_scoped_context_shadow_lock lock;
1920
1921         return get_shared_state()->m_capture_context_params.m_linked_programs.add_snapshot(m_context_info, m_handle_remapper, handle, binary_format, pBinary, binary_size);
1922     }
1923
1924     void handle_use_program(gl_entrypoint_id_t id, GLuint program)
1925     {
1926         // Note: This mixes ARB and non-ARB funcs. to probe around, which is evil.
1927
1928         peek_and_record_gl_error();
1929
1930         check_program_binding_shadow();
1931
1932         vogl_scoped_context_shadow_lock lock;
1933
1934         VOGL_ASSERT(!program || (get_shared_state()->m_capture_context_params.m_objs.get_target(program) == VOGL_PROGRAM_OBJECT));
1935
1936         GLuint prev_program = m_cur_program;
1937
1938         bool prev_is_program = false;
1939         GLint prev_is_marked_for_deletion = false;
1940         vogl::growable_array<GLuint, 8> prev_attached_replay_shaders;
1941
1942         if ((prev_program) && (program != prev_program))
1943         {
1944             prev_is_program = GL_ENTRYPOINT(glIsProgram)(prev_program);
1945             peek_and_drop_gl_error();
1946
1947             if (prev_is_program)
1948             {
1949                 GL_ENTRYPOINT(glGetProgramiv)(prev_program, GL_DELETE_STATUS, &prev_is_marked_for_deletion);
1950                 peek_and_drop_gl_error();
1951
1952                 if (prev_is_marked_for_deletion)
1953                 {
1954                     // The currently bound program is marked for deletion, so record which shaders are attached to it in case the program is actually deleted by the driver on the UseProgram() call.
1955                     GLint num_attached_shaders = 0;
1956                     GL_ENTRYPOINT(glGetProgramiv)(prev_program, GL_ATTACHED_SHADERS, &num_attached_shaders);
1957                     peek_and_drop_gl_error();
1958
1959                     if (num_attached_shaders)
1960                     {
1961                         prev_attached_replay_shaders.resize(num_attached_shaders);
1962
1963                         GLsizei actual_count = 0;
1964                         GL_ENTRYPOINT(glGetAttachedShaders)(prev_program, num_attached_shaders, &actual_count, prev_attached_replay_shaders.get_ptr());
1965                         peek_and_drop_gl_error();
1966
1967                         VOGL_ASSERT(actual_count == num_attached_shaders);
1968                     }
1969                 }
1970             }
1971         }
1972
1973         if (id == VOGL_ENTRYPOINT_glUseProgram)
1974             GL_ENTRYPOINT(glUseProgram)(program);
1975         else
1976         {
1977             VOGL_ASSERT(id == VOGL_ENTRYPOINT_glUseProgramObjectARB);
1978             GL_ENTRYPOINT(glUseProgramObjectARB)(program);
1979         }
1980
1981         GLenum gl_err = peek_and_record_gl_error();
1982         if (gl_err != GL_NO_ERROR)
1983         {
1984             vogl_error_printf("%s: glUseProgram/glUseProgramObjectARB on handle %u returned GL error %s\n", VOGL_METHOD_NAME, program, g_gl_enums.find_gl_error_name(gl_err));
1985             return;
1986         }
1987
1988         if ((prev_program) && (prev_program != program))
1989         {
1990             bool is_prev_still_program = GL_ENTRYPOINT(glIsProgram)(prev_program);
1991             if (!is_prev_still_program)
1992             {
1993                 VOGL_ASSERT(prev_is_program);
1994                 VOGL_ASSERT(prev_is_marked_for_deletion);
1995
1996                 // The previously bound program is really dead now, kill it from our tables and also check up on any shaders it was attached to.
1997                 bool was_deleted = get_shared_state()->m_capture_context_params.m_objs.erase(prev_program);
1998                 VOGL_ASSERT(was_deleted);
1999                 VOGL_NOTE_UNUSED(was_deleted);
2000
2001                 get_shared_state()->m_capture_context_params.m_linked_programs.remove_snapshot(prev_program);
2002
2003                 for (uint i = 0; i < prev_attached_replay_shaders.size(); i++)
2004                 {
2005                     GLuint shader_handle = prev_attached_replay_shaders[i];
2006
2007                     bool is_still_shader = GL_ENTRYPOINT(glIsShader)(shader_handle);
2008                     peek_and_drop_gl_error();
2009
2010                     if (is_still_shader)
2011                         continue;
2012
2013                     if (!get_shared_state()->m_capture_context_params.m_objs.contains(shader_handle))
2014                     {
2015                         // We didn't create this shader handle, the AMD driver did on a program binary link, so ignore it.
2016                         continue;
2017                     }
2018
2019                     // The attached shader is now really dead.
2020                     VOGL_ASSERT(get_shared_state()->m_capture_context_params.m_objs.get_target(shader_handle) == VOGL_SHADER_OBJECT);
2021                     if (!get_shared_state()->m_capture_context_params.m_objs.erase(shader_handle))
2022                     {
2023                         vogl_error_printf("%s: Failed finding attached shader %u in objects hash table, while handling the actual deletion of program %u\n", VOGL_METHOD_NAME, shader_handle, prev_program);
2024                     }
2025                 }
2026             }
2027         }
2028
2029         m_cur_program = program;
2030
2031         check_program_binding_shadow();
2032     }
2033
2034     void handle_del_shader(gl_entrypoint_id_t id, GLuint obj)
2035     {
2036         // Note: This mixes ARB and non-ARB funcs. to probe around, which is evil.
2037
2038         peek_and_record_gl_error();
2039
2040         check_program_binding_shadow();
2041
2042         vogl_scoped_context_shadow_lock lock;
2043
2044         VOGL_ASSERT(!obj || (get_shared_state()->m_capture_context_params.m_objs.get_target(obj) == VOGL_SHADER_OBJECT));
2045
2046         if (id == VOGL_ENTRYPOINT_glDeleteObjectARB)
2047             GL_ENTRYPOINT(glDeleteObjectARB)(obj);
2048         else
2049             GL_ENTRYPOINT(glDeleteShader)(obj);
2050
2051         GLenum gl_err = peek_and_record_gl_error();
2052         if (gl_err != GL_NO_ERROR)
2053         {
2054             vogl_error_printf("%s: glDeleteShader/glDeleteObjectARB on handle %u returned GL error %s\n", VOGL_METHOD_NAME, obj, g_gl_enums.find_gl_error_name(gl_err));
2055             return;
2056         }
2057
2058         if (!obj)
2059             return;
2060
2061         bool is_still_shader = GL_ENTRYPOINT(glIsShader)(obj);
2062         peek_and_drop_gl_error();
2063
2064         if (!is_still_shader)
2065         {
2066             // The shader is really gone.
2067             bool was_deleted = get_shared_state()->m_capture_context_params.m_objs.erase(obj);
2068             VOGL_ASSERT(was_deleted);
2069             VOGL_NOTE_UNUSED(was_deleted);
2070         }
2071         else
2072         {
2073             GLint marked_for_deletion = 0;
2074             GL_ENTRYPOINT(glGetShaderiv)(obj, GL_DELETE_STATUS, &marked_for_deletion);
2075             peek_and_drop_gl_error();
2076
2077             VOGL_VERIFY(marked_for_deletion);
2078
2079             // The shader is attached to a live program object (which may or may not be actually in the marked_as_deleted state)
2080             // we'll get around to it when the program object is deleted, or when they remove the program object from the current state.
2081         }
2082     }
2083
2084     void handle_del_program(gl_entrypoint_id_t id, GLuint obj)
2085     {
2086         // Note: This mixes ARB and non-ARB funcs. to probe around, which is evil.
2087
2088         peek_and_record_gl_error();
2089
2090         check_program_binding_shadow();
2091
2092         vogl_scoped_context_shadow_lock lock;
2093
2094         VOGL_ASSERT(!obj || (get_shared_state()->m_capture_context_params.m_objs.get_target(obj) == VOGL_PROGRAM_OBJECT));
2095
2096         bool is_program = GL_ENTRYPOINT(glIsProgram)(obj) != 0;
2097         peek_and_drop_gl_error();
2098
2099         vogl::growable_array<GLuint, 8> attached_replay_shaders;
2100
2101         if ((is_program) && (obj))
2102         {
2103             GLint num_attached_shaders = 0;
2104             GL_ENTRYPOINT(glGetProgramiv)(obj, GL_ATTACHED_SHADERS, &num_attached_shaders);
2105             peek_and_drop_gl_error();
2106
2107             if (num_attached_shaders)
2108             {
2109                 attached_replay_shaders.resize(num_attached_shaders);
2110
2111                 GLsizei actual_count = 0;
2112                 GL_ENTRYPOINT(glGetAttachedShaders)(obj, num_attached_shaders, &actual_count, attached_replay_shaders.get_ptr());
2113                 peek_and_drop_gl_error();
2114
2115                 VOGL_ASSERT(actual_count == num_attached_shaders);
2116             }
2117         }
2118
2119         if (id == VOGL_ENTRYPOINT_glDeleteObjectARB)
2120             GL_ENTRYPOINT(glDeleteObjectARB)(obj);
2121         else
2122             GL_ENTRYPOINT(glDeleteProgram)(obj);
2123
2124         GLenum gl_err = peek_and_record_gl_error();
2125         if (gl_err != GL_NO_ERROR)
2126         {
2127             vogl_error_printf("%s: glDeleteProgram/glDeleteObjectARB on handle %u returned GL error %s\n", VOGL_METHOD_NAME, obj, g_gl_enums.find_gl_error_name(gl_err));
2128             return;
2129         }
2130
2131         if (!obj)
2132             return;
2133
2134         bool is_still_program = GL_ENTRYPOINT(glIsProgram)(obj) != 0;
2135         peek_and_drop_gl_error();
2136
2137         GLint marked_for_deletion = 0;
2138         if (is_still_program)
2139         {
2140             // It must still be bound to the context, or referred to in some other way that we don't know about.
2141             GL_ENTRYPOINT(glGetProgramiv)(obj, GL_DELETE_STATUS, &marked_for_deletion);
2142             bool delete_status_check_succeeded = (peek_and_drop_gl_error() == GL_NO_ERROR);
2143
2144             VOGL_VERIFY(delete_status_check_succeeded);
2145             VOGL_VERIFY(marked_for_deletion);
2146         }
2147         else if (!is_still_program)
2148         {
2149             VOGL_ASSERT(is_program);
2150
2151             // The program is really gone now.
2152             bool was_deleted = get_shared_state()->m_capture_context_params.m_objs.erase(obj);
2153             VOGL_ASSERT(was_deleted);
2154             VOGL_NOTE_UNUSED(was_deleted);
2155
2156             get_shared_state()->m_capture_context_params.m_linked_programs.remove_snapshot(obj);
2157
2158             if (m_cur_program == obj)
2159             {
2160                 // This shouldn't happen - if the program is still bound to the context then it should still be a program.
2161                 VOGL_ASSERT_ALWAYS;
2162                 m_cur_program = 0;
2163             }
2164
2165             for (uint i = 0; i < attached_replay_shaders.size(); i++)
2166             {
2167                 GLuint shader_handle = attached_replay_shaders[i];
2168
2169                 bool is_still_shader = GL_ENTRYPOINT(glIsShader)(shader_handle) != 0;
2170                 peek_and_drop_gl_error();
2171
2172                 if (is_still_shader)
2173                     continue;
2174
2175                 if (!get_shared_state()->m_capture_context_params.m_objs.contains(shader_handle))
2176                 {
2177                     // We didn't create this shader handle, the AMD driver did on a program binary link, so ignore it.
2178                     continue;
2179                 }
2180
2181                 // The attached shader is now really dead.
2182                 VOGL_ASSERT(get_shared_state()->m_capture_context_params.m_objs.get_target(shader_handle) == VOGL_SHADER_OBJECT);
2183                 if (!get_shared_state()->m_capture_context_params.m_objs.erase(shader_handle))
2184                 {
2185                     vogl_error_printf("%s: Failed finding attached shader %u in objects shadow, while handling the deletion of program %u\n", VOGL_METHOD_NAME, shader_handle, obj);
2186                 }
2187             }
2188         }
2189
2190         check_program_binding_shadow();
2191     }
2192
2193     void handle_del_object(gl_entrypoint_id_t id, GLuint obj)
2194     {
2195         vogl_scoped_context_shadow_lock lock;
2196
2197         if (!obj)
2198             return;
2199
2200         GLenum target = get_shared_state()->m_capture_context_params.m_objs.get_target(obj);
2201
2202         if (target == VOGL_PROGRAM_OBJECT)
2203             handle_del_program(id, obj);
2204         else if (target == VOGL_SHADER_OBJECT)
2205             handle_del_shader(id, obj);
2206         else
2207         {
2208             vogl_error_printf("%s: glDeleteObjectARB: Unable to find object handle %u in object shadow\n", VOGL_METHOD_NAME, obj);
2209
2210             GL_ENTRYPOINT(glDeleteObjectARB)(obj);
2211         }
2212     }
2213
2214     void handle_detach_shader(gl_entrypoint_id_t id, GLuint program, GLuint shader)
2215     {
2216         vogl_scoped_context_shadow_lock lock;
2217
2218         peek_and_record_gl_error();
2219
2220         // Note: This mixes ARB and non-ARB funcs. to probe around, which is evil.
2221
2222         GLboolean was_shader = GL_ENTRYPOINT(glIsShader)(shader);
2223         peek_and_drop_gl_error();
2224
2225         GLint marked_for_deletion = 0;
2226         GL_ENTRYPOINT(glGetShaderiv)(shader, GL_DELETE_STATUS, &marked_for_deletion);
2227         peek_and_drop_gl_error();
2228
2229         if (id == VOGL_ENTRYPOINT_glDetachObjectARB)
2230             GL_ENTRYPOINT(glDetachObjectARB)(program, shader);
2231         else
2232         {
2233             VOGL_ASSERT(id == VOGL_ENTRYPOINT_glDetachShader);
2234             GL_ENTRYPOINT(glDetachShader)(program, shader);
2235         }
2236
2237         GLenum gl_err = peek_and_record_gl_error();
2238         if (gl_err != GL_NO_ERROR)
2239         {
2240             vogl_error_printf("%s: glDetachShader/glDetachObjectARB on program handle %u shader handle %u returned GL error %s\n", VOGL_METHOD_NAME, program, shader, g_gl_enums.find_gl_error_name(gl_err));
2241             return;
2242         }
2243
2244         GLboolean is_shader = GL_ENTRYPOINT(glIsShader)(shader);
2245         peek_and_drop_gl_error();
2246
2247         if ((marked_for_deletion) && (was_shader) && (!is_shader))
2248         {
2249             // The shader is really gone now.
2250             bool was_deleted = get_shared_state()->m_capture_context_params.m_objs.erase(shader);
2251             VOGL_ASSERT(was_deleted);
2252             VOGL_NOTE_UNUSED(was_deleted);
2253         }
2254     }
2255
2256     // glBegin/glEnd shadowing
2257
2258     // Note: This flag gets set even when the client is composing a display list!
2259     void set_in_gl_begin(bool value)
2260     {
2261         m_in_gl_begin = value;
2262     }
2263     bool get_in_gl_begin() const
2264     {
2265         return m_in_gl_begin;
2266     }
2267
2268     void set_uses_client_side_arrays(bool value)
2269     {
2270         m_uses_client_side_arrays = value;
2271     }
2272     bool get_uses_client_side_arrays() const
2273     {
2274         return m_uses_client_side_arrays;
2275     }
2276
2277     typedef vogl::map<dynamic_string, dynamic_string, dynamic_string_less_than_case_sensitive, dynamic_string_equal_to_case_sensitive> extension_id_to_string_map_t;
2278     extension_id_to_string_map_t &get_extension_map()
2279     {
2280         return m_extension_map;
2281     }
2282
2283     const vogl_context_handle_remapper &get_handle_remapper() const
2284     {
2285         return m_handle_remapper;
2286     }
2287     vogl_context_handle_remapper &get_handle_remapper()
2288     {
2289         return m_handle_remapper;
2290     }
2291
2292     // Display list shadowing
2293
2294     GLenum get_current_display_list_mode() const
2295     {
2296         return m_current_display_list_mode;
2297     }
2298     bool is_composing_display_list() const
2299     {
2300         return m_current_display_list_handle >= 0;
2301     }
2302     GLint get_current_display_list_handle() const
2303     {
2304         return m_current_display_list_handle;
2305     }
2306
2307     bool new_list(GLuint handle, GLenum mode)
2308     {
2309         VOGL_FUNC_TRACER
2310
2311         VOGL_ASSERT((mode == GL_COMPILE) || (mode == GL_COMPILE_AND_EXECUTE));
2312
2313         if (m_current_display_list_handle >= 0)
2314         {
2315             vogl_error_printf("%s: Can't define new display list %u while display list %i is already being defined!\n", VOGL_METHOD_NAME, handle, m_current_display_list_handle);
2316             return false;
2317         }
2318
2319         m_current_display_list_handle = handle;
2320         m_current_display_list_mode = mode;
2321
2322         vogl_scoped_context_shadow_lock lock;
2323
2324         get_shared_state()->m_capture_context_params.m_display_lists.new_list(handle, handle);
2325
2326         return true;
2327     }
2328
2329     bool end_list()
2330     {
2331         VOGL_FUNC_TRACER
2332
2333         if (m_current_display_list_handle < 0)
2334         {
2335             vogl_error_printf("%s: No display list is active!\n", VOGL_METHOD_NAME);
2336             return false;
2337         }
2338
2339         {
2340             vogl_scoped_context_shadow_lock lock;
2341
2342             get_shared_state()->m_capture_context_params.m_display_lists.end_list(m_current_display_list_handle);
2343         }
2344
2345         m_current_display_list_handle = -1;
2346         m_current_display_list_mode = GL_NONE;
2347
2348         return true;
2349     }
2350
2351     void gen_lists(GLuint result, GLsizei range)
2352     {
2353         VOGL_FUNC_TRACER
2354
2355         if (!range)
2356             return;
2357
2358         vogl_scoped_context_shadow_lock lock;
2359
2360         get_shared_state()->m_capture_context_params.m_display_lists.gen_lists(result, range);
2361     }
2362
2363     void del_lists(GLuint list, GLsizei range)
2364     {
2365         VOGL_FUNC_TRACER
2366
2367         if (!range)
2368             return;
2369
2370         vogl_scoped_context_shadow_lock lock;
2371
2372         get_shared_state()->m_capture_context_params.m_display_lists.del_lists(list, range);
2373     }
2374
2375     void glx_font(const char *pFont, int first, int count, int list_base)
2376     {
2377         vogl_scoped_context_shadow_lock lock;
2378
2379         get_shared_state()->m_capture_context_params.m_display_lists.glx_font(pFont, first, count, list_base);
2380     }
2381
2382     bool parse_list_and_update_shadows(GLuint handle, vogl_display_list_state::pBind_callback_func_ptr pBind_callback, void *pBind_callback_opaque)
2383     {
2384         vogl_scoped_context_shadow_lock lock;
2385
2386         return get_shared_state()->m_capture_context_params.m_display_lists.parse_list_and_update_shadows(handle, pBind_callback, pBind_callback_opaque);
2387     }
2388
2389     bool parse_lists_and_update_shadows(GLsizei n, GLenum type, const GLvoid *lists, vogl_display_list_state::pBind_callback_func_ptr pBind_callback, void *pBind_callback_opaque)
2390     {
2391         vogl_scoped_context_shadow_lock lock;
2392
2393         return get_shared_state()->m_capture_context_params.m_display_lists.parse_lists_and_update_shadows(n, type, lists, pBind_callback, pBind_callback_opaque);
2394     }
2395
2396     bool add_packet_to_current_display_list(gl_entrypoint_id_t func, const vogl_trace_packet &packet)
2397     {
2398         VOGL_FUNC_TRACER
2399
2400         if (m_current_display_list_handle < 0)
2401             return true;
2402
2403         if (!vogl_display_list_state::is_call_listable(func, packet))
2404         {
2405             if (g_vogl_entrypoint_descs[func].m_is_listable)
2406             {
2407                 if (!g_vogl_entrypoint_descs[func].m_whitelisted_for_displaylists)
2408                     vogl_error_printf("%s: Failed serializing trace packet into display list shadow! Call is not listable.\n", VOGL_FUNCTION_NAME);
2409                 else
2410                     vogl_error_printf("%s: Failed serializing trace packet into display list shadow! Call with these parameters is not listable.\n", VOGL_FUNCTION_NAME);
2411             }
2412             return false;
2413         }
2414
2415         vogl_scoped_context_shadow_lock lock;
2416
2417         return get_shared_state()->m_capture_context_params.m_display_lists.add_packet_to_list(m_current_display_list_handle, func, packet);
2418     }
2419
2420 private:
2421     int m_ref_count;
2422     bool m_deleted_flag;
2423     vogl_context *m_pShared_state;
2424
2425     GLXContext m_context_handle;
2426     GLXContext m_sharelist_handle;
2427
2428     const Display *m_pDpy;
2429     GLXDrawable m_drawable;
2430     GLXDrawable m_read_drawable;
2431     int m_render_type;
2432     XVisualInfo m_xvisual_info;
2433     GLXFBConfig m_fb_config;
2434
2435     bool m_direct;
2436
2437     bool m_created_from_attribs;
2438     vogl::vector<int> m_attrib_list;
2439
2440     uint64_t m_current_thread; // as returned by vogl_get_current_kernel_thread_id()
2441
2442     GLuint m_max_vertex_attribs;
2443
2444     bool m_has_been_made_current;
2445
2446     int m_window_width, m_window_height;
2447     uint64_t m_frame_index;
2448
2449     gl_entrypoint_id_t m_creation_func;
2450     vogl_context_desc m_context_desc;
2451     vogl_context_info m_context_info; // only valid after first MakeCurrent
2452
2453     extension_id_to_string_map_t m_extension_map;
2454
2455     GLenum m_latched_gl_error;
2456
2457     gl_buffer_desc_map m_buffer_descs;
2458
2459     vogl_capture_context_params m_capture_context_params;
2460
2461     vogl_framebuffer_capturer m_framebuffer_capturer;
2462
2463     GLuint m_cur_program;
2464
2465     bool m_in_gl_begin;
2466     bool m_uses_client_side_arrays;
2467
2468     vogl_context_handle_remapper m_handle_remapper;
2469
2470     int m_current_display_list_handle;
2471     GLenum m_current_display_list_mode;
2472
2473     static void debug_callback_arb(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, GLvoid *pUser_param)
2474     {
2475         (void)length;
2476
2477         char final_message[4096];
2478
2479         vogl_context *pContext = (vogl_context *)(pUser_param);
2480
2481         vogl_format_debug_output_arb(final_message, sizeof(final_message), source, type, id, severity, reinterpret_cast<const char *>(message));
2482
2483         if (pContext)
2484         {
2485             vogl_warning_printf("%s: Trace context: 0x%" PRIX64 ", Thread id: 0x%" PRIX64 "\n%s\n", VOGL_FUNCTION_NAME,
2486                                cast_val_to_uint64(pContext->m_context_handle), vogl_get_current_kernel_thread_id(), final_message);
2487         }
2488         else
2489         {
2490             vogl_warning_printf("%s: %s\n", VOGL_FUNCTION_NAME, final_message);
2491         }
2492     }
2493
2494     void on_first_make_current()
2495     {
2496         if ((g_command_line_params.get_value_as_bool("vogl_force_debug_context")) && (m_context_info.is_debug_context()))
2497         {
2498             if (GL_ENTRYPOINT(glDebugMessageCallbackARB) && m_context_info.supports_extension("GL_ARB_debug_output"))
2499             {
2500                 VOGL_CHECK_GL_ERROR;
2501
2502                 GL_ENTRYPOINT(glDebugMessageCallbackARB)(debug_callback_arb, (GLvoid *)this);
2503                 GL_ENTRYPOINT(glEnable)(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
2504
2505                 VOGL_CHECK_GL_ERROR;
2506             }
2507             else
2508             {
2509                 vogl_error_printf("%s: Can't enable debug context, either glDebugMessageCallbackARB func or the GL_ARB_debug_output extension is not available!\n", VOGL_METHOD_NAME);
2510             }
2511         }
2512     }
2513
2514     // Note: Check for any GL errors before calling this method.
2515     bool check_program_binding_shadow()
2516     {
2517         GLint actual_cur_program = 0;
2518         GL_ENTRYPOINT(glGetIntegerv)(GL_CURRENT_PROGRAM, &actual_cur_program);
2519
2520         if (peek_and_drop_gl_error() != GL_NO_ERROR)
2521             vogl_error_printf("%s: GL error checking program binding shadow!\n", VOGL_METHOD_NAME);
2522
2523         if (m_cur_program == static_cast<GLuint>(actual_cur_program))
2524             return true;
2525
2526         // OK, on AMD it plays games with GL_CURRENT_PROGRAM when the currently bound program is deleted. Check for this scenario.
2527         bool is_still_program = GL_ENTRYPOINT(glIsProgram)(m_cur_program) != 0;
2528         if ((peek_and_drop_gl_error() == GL_NO_ERROR) && (is_still_program))
2529         {
2530             GLint marked_for_deletion = GL_FALSE;
2531             GL_ENTRYPOINT(glGetProgramiv)(m_cur_program, GL_DELETE_STATUS, &marked_for_deletion);
2532
2533             if ((peek_and_drop_gl_error() == GL_NO_ERROR) && (marked_for_deletion))
2534                 return true;
2535         }
2536
2537         VOGL_VERIFY(!"m_cur_program appears out of sync with GL's GL_CURRENT_PROGRAM");
2538         return false;
2539     }
2540 };
2541
2542 typedef vogl::hash_map<GLXContext, vogl_context *, bit_hasher<GLXContext> > glxcontext_map;
2543
2544 //----------------------------------------------------------------------------------------------------------------------
2545 // vogl_context_handle_remapper::is_valid_handle
2546 //----------------------------------------------------------------------------------------------------------------------
2547 bool vogl_context_handle_remapper::is_valid_handle(vogl_namespace_t handle_namespace, uint64_t from_handle)
2548 {
2549     if (!from_handle)
2550         return 0;
2551
2552     uint32 handle = static_cast<uint32>(from_handle);
2553
2554     const vogl_capture_context_params &capture_context_params = m_pContext->get_shared_state()->get_capture_context_params();
2555
2556     // TODO: Support all the object types
2557     if (handle_namespace == VOGL_NAMESPACE_SHADERS)
2558     {
2559         VOGL_ASSERT(handle == from_handle);
2560         return (capture_context_params.m_objs.get_target(handle) == VOGL_SHADER_OBJECT);
2561     }
2562     else if (handle_namespace == VOGL_NAMESPACE_PROGRAMS)
2563     {
2564         VOGL_ASSERT(handle == from_handle);
2565         return (capture_context_params.m_objs.get_target(handle) == VOGL_PROGRAM_OBJECT);
2566     }
2567     else if (handle_namespace == VOGL_NAMESPACE_TEXTURES)
2568     {
2569         VOGL_ASSERT(handle == from_handle);
2570         return capture_context_params.m_textures.contains(handle);
2571     }
2572
2573     VOGL_VERIFY(0);
2574     return false;
2575 }
2576
2577 //----------------------------------------------------------------------------------------------------------------------
2578 // vogl_context_handle_remapper::determine_from_object_target
2579 //----------------------------------------------------------------------------------------------------------------------
2580 bool vogl_context_handle_remapper::determine_from_object_target(vogl_namespace_t handle_namespace, uint64_t from_handle, GLenum &target)
2581 {
2582     target = GL_NONE;
2583
2584     if (!from_handle)
2585         return false;
2586
2587     uint32 handle = static_cast<uint32>(from_handle);
2588
2589     const vogl_capture_context_params &capture_context_params = m_pContext->get_shared_state()->get_capture_context_params();
2590
2591     // TODO: Support all the object types
2592     if (handle_namespace == VOGL_NAMESPACE_TEXTURES)
2593     {
2594         if (capture_context_params.m_textures.contains(handle))
2595         {
2596             target = capture_context_params.m_textures.get_target(handle);
2597             return true;
2598         }
2599     }
2600
2601     VOGL_VERIFY(0);
2602     return false;
2603 }
2604
2605 //----------------------------------------------------------------------------------------------------------------------
2606 // vogl_scoped_gl_error_absorber::vogl_scoped_gl_error_absorber
2607 //----------------------------------------------------------------------------------------------------------------------
2608 vogl_scoped_gl_error_absorber::vogl_scoped_gl_error_absorber(vogl_context *pContext)
2609     : m_pContext(pContext)
2610 {
2611     // Latch any errors that are present on the context, if any, so the client will see it later
2612     if (pContext)
2613         pContext->peek_and_record_gl_error();
2614 }
2615
2616 //----------------------------------------------------------------------------------------------------------------------
2617 // vogl_scoped_gl_error_absorber::~vogl_scoped_gl_error_absorber
2618 //----------------------------------------------------------------------------------------------------------------------
2619 vogl_scoped_gl_error_absorber::~vogl_scoped_gl_error_absorber()
2620 {
2621     // Now get the current GL error and drop it, so the client app doesn't see it
2622     if (m_pContext)
2623         m_pContext->peek_and_drop_gl_error();
2624 }
2625
2626 //----------------------------------------------------------------------------------------------------------------------
2627 // class vogl_context_manager
2628 //----------------------------------------------------------------------------------------------------------------------
2629 class vogl_context_manager
2630 {
2631     VOGL_NO_COPY_OR_ASSIGNMENT_OP(vogl_context_manager);
2632
2633 public:
2634     vogl_context_manager()
2635         : m_glx_context_map_lock(0, true)
2636     {
2637     }
2638
2639     vogl_context *create_context(GLXContext handle)
2640     {
2641         VOGL_ASSERT(handle);
2642
2643         vogl_context *pVOGL_context = vogl_new(vogl_context, handle);
2644
2645         {
2646             scoped_mutex lock(m_glx_context_map_lock);
2647             m_glx_context_map.insert(handle, pVOGL_context);
2648         }
2649
2650         return pVOGL_context;
2651     }
2652
2653     vogl_context *lookup_vogl_context(GLXContext handle)
2654     {
2655         VOGL_ASSERT(handle);
2656
2657         vogl_context *pVOGL_context;
2658         VOGL_NOTE_UNUSED(pVOGL_context);
2659         glxcontext_map::iterator it;
2660
2661         {
2662             scoped_mutex lock(m_glx_context_map_lock);
2663             it = m_glx_context_map.find(handle);
2664         }
2665
2666         return (it != m_glx_context_map.end()) ? it->second : NULL;
2667     }
2668
2669     bool destroy_context(GLXContext handle)
2670     {
2671         VOGL_ASSERT(handle);
2672
2673         vogl_context *pVOGL_context = lookup_vogl_context(handle);
2674         if (!pVOGL_context)
2675             return false;
2676
2677         {
2678             scoped_mutex lock(m_glx_context_map_lock);
2679             m_glx_context_map.erase(handle);
2680         }
2681
2682         vogl_thread_local_data *pTLS_data = vogl_get_or_create_thread_local_data();
2683         if ((pTLS_data) && (pTLS_data->m_pContext == pVOGL_context))
2684             pTLS_data->m_pContext = NULL;
2685
2686         vogl_delete(pVOGL_context);
2687
2688         return true;
2689     }
2690
2691     vogl_context *get_or_create_context(GLXContext handle)
2692     {
2693         VOGL_ASSERT(handle);
2694
2695         vogl_context *pVOGL_context = lookup_vogl_context(handle);
2696         if (!pVOGL_context)
2697             pVOGL_context = vogl_new(vogl_context, handle);
2698
2699         return pVOGL_context;
2700     }
2701
2702     vogl_context *make_current(GLXContext handle)
2703     {
2704         VOGL_ASSERT(handle);
2705
2706         vogl_context *pVOGL_context = get_or_create_context(handle);
2707
2708         if (pVOGL_context->get_current_thread())
2709         {
2710             if (pVOGL_context->get_current_thread() != vogl_get_current_kernel_thread_id())
2711             {
2712                 vogl_error_printf("%s: context is being made current, but is already current on another thread\n", VOGL_METHOD_NAME);
2713             }
2714             else
2715             {
2716                 vogl_warning_printf("%s: context is already current (redundant call)\n", VOGL_METHOD_NAME);
2717             }
2718         }
2719
2720         vogl_get_or_create_thread_local_data()->m_pContext = pVOGL_context;
2721
2722         pVOGL_context->on_make_current();
2723
2724         return pVOGL_context;
2725     }
2726
2727     vogl_context *get_current()
2728     {
2729         return static_cast<vogl_context *>(vogl_get_or_create_thread_local_data()->m_pContext);
2730     }
2731
2732     void release_current()
2733     {
2734         vogl_context *pVOGL_context = get_current();
2735         if (pVOGL_context)
2736         {
2737             if (!pVOGL_context->get_current_thread())
2738             {
2739                 vogl_error_printf("%s: Context manager's current context is not marked as current on any thread\n", VOGL_METHOD_NAME);
2740             }
2741             pVOGL_context->on_release_current_epilog();
2742
2743             vogl_get_or_create_thread_local_data()->m_pContext = NULL;
2744         }
2745     }
2746
2747     void lock()
2748     {
2749         m_glx_context_map_lock.lock();
2750     }
2751
2752     void unlock()
2753     {
2754         m_glx_context_map_lock.unlock();
2755     }
2756
2757     const glxcontext_map &get_context_map() const
2758     {
2759         return m_glx_context_map;
2760     }
2761
2762     vogl_context *find_context(const Display *dpy, GLXDrawable drawable)
2763     {
2764         lock();
2765
2766         const glxcontext_map &contexts = m_glx_context_map;
2767         for (glxcontext_map::const_iterator it = contexts.begin(); it != contexts.end(); ++it)
2768         {
2769             vogl_context *p = it->second;
2770             if ((p->get_display() == dpy) && (p->get_drawable() == drawable))
2771             {
2772                 unlock();
2773                 return p;
2774             }
2775         }
2776
2777         unlock();
2778         return NULL;
2779     }
2780
2781 private:
2782     mutex m_glx_context_map_lock;
2783     glxcontext_map m_glx_context_map;
2784 };
2785
2786 vogl_context_manager g_context_manager;
2787
2788 //----------------------------------------------------------------------------------------------------------------------
2789 // vogl_context_shadow_lock
2790 //----------------------------------------------------------------------------------------------------------------------
2791 static inline void vogl_context_shadow_lock()
2792 {
2793     g_context_manager.lock();
2794 }
2795
2796 //----------------------------------------------------------------------------------------------------------------------
2797 // vogl_context_shadow_unlock
2798 //----------------------------------------------------------------------------------------------------------------------
2799 static inline void vogl_context_shadow_unlock()
2800 {
2801     g_context_manager.unlock();
2802 }
2803
2804 //----------------------------------------------------------------------------------------------------------------------
2805 // vogl_atexit
2806 //----------------------------------------------------------------------------------------------------------------------
2807 static void vogl_atexit()
2808 {
2809     vogl_debug_printf("vogl_atexit()\n");
2810
2811     scoped_mutex lock(g_vogl_trace_mutex);
2812
2813     vogl_deinit();
2814 }
2815
2816 //----------------------------------------------------------------------------------------------------------------------
2817 // vogl_backtrace
2818 //----------------------------------------------------------------------------------------------------------------------
2819 static uint32 vogl_backtrace(uint addrs_to_skip)
2820 {
2821     vogl_backtrace_addrs addrs;
2822     addrs.m_num_addrs = btrace_get(addrs.m_addrs, addrs.cMaxAddrs, addrs_to_skip);
2823
2824     vogl_backtrace_hashmap::insert_result ins_res;
2825
2826     uint32 index = 0;
2827
2828     g_backtrace_hashmap_mutex.lock();
2829
2830     if (!g_backtrace_hashmap.insert_no_grow(ins_res, addrs))
2831         vogl_error_printf("%s: Backtrace hashmap exhausted! Please increase VOGL_BACKTRACE_HASHMAP_CAPACITY. Some backtraces in this trace will not have symbols.\n", VOGL_FUNCTION_NAME);
2832     else
2833     {
2834         index = ins_res.first.get_index();
2835         (ins_res.first)->second++;
2836     }
2837
2838     g_backtrace_hashmap_mutex.unlock();
2839
2840     return index;
2841 }
2842
2843 //----------------------------------------------------------------------------------------------------------------------
2844 // vogl_flush_backtrace_to_trace_file
2845 //----------------------------------------------------------------------------------------------------------------------
2846 static bool vogl_flush_backtrace_to_trace_file()
2847 {
2848     scoped_mutex lock(g_vogl_trace_mutex);
2849
2850     if (!g_vogl_trace_writer.is_opened() || (g_vogl_trace_writer.get_trace_archive() == NULL))
2851         return false;
2852
2853     json_document doc;
2854
2855     g_backtrace_hashmap_mutex.lock();
2856
2857     uint backtrace_hashmap_size = g_backtrace_hashmap.size();
2858     if (backtrace_hashmap_size)
2859     {
2860         vogl_message_printf("%s: Writing backtrace %u addrs\n", VOGL_FUNCTION_NAME, backtrace_hashmap_size);
2861
2862         json_node *pRoot = doc.get_root();
2863         pRoot->init_array();
2864
2865         for (vogl_backtrace_hashmap::const_iterator it = g_backtrace_hashmap.begin(); it != g_backtrace_hashmap.end(); ++it)
2866         {
2867             json_node &node = pRoot->add_array();
2868
2869             node.add_key_value("index", it.get_index());
2870             node.add_key_value("count", it->second);
2871
2872             json_node &addrs_arr = node.add_array("addrs");
2873             const vogl_backtrace_addrs &addrs = it->first;
2874
2875             for (uint i = 0; i < addrs.m_num_addrs; i++)
2876             {
2877                 addrs_arr.add_value(to_hex_string(static_cast<uint64_t>(addrs.m_addrs[i])));
2878             }
2879         }
2880     }
2881
2882     g_backtrace_hashmap.reset();
2883     g_backtrace_hashmap_mutex.unlock();
2884
2885     if (backtrace_hashmap_size)
2886     {
2887         char_vec data;
2888         doc.serialize(data, true, 0, false);
2889
2890         if (g_vogl_trace_writer.get_trace_archive()->add_buf_using_id(data.get_ptr(), data.size(), VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME).is_empty())
2891             vogl_error_printf("%s: Failed adding serialized backtrace addrs to trace archive\n", VOGL_FUNCTION_NAME);
2892         else
2893             vogl_message_printf("%s: Done writing backtrace addrs\n", VOGL_FUNCTION_NAME);
2894     }
2895
2896     return true;
2897 }
2898
2899 //----------------------------------------------------------------------------------------------------------------------
2900 // vogl_flush_compilerinfo_to_trace_file
2901 //----------------------------------------------------------------------------------------------------------------------
2902 static bool vogl_flush_compilerinfo_to_trace_file()
2903 {
2904     scoped_mutex lock(g_vogl_trace_mutex);
2905
2906     if (!g_vogl_trace_writer.is_opened() || (g_vogl_trace_writer.get_trace_archive() == NULL))
2907         return false;
2908
2909     json_document doc;
2910
2911     vogl_message_printf("%s: Begin resolving compilerinfo to symbols\n", VOGL_FUNCTION_NAME);
2912
2913     json_node *pRoot = doc.get_root();
2914
2915     vogl::json_node &compiler_info_node = pRoot->add_array("compiler_info");
2916     compiler_info_node.add_key_value("time", __TIME__);
2917     compiler_info_node.add_key_value("date", __DATE__);
2918 #ifdef __VERSION__
2919     compiler_info_node.add_key_value("version", __VERSION__);
2920 #endif
2921 #ifdef __i386__
2922     compiler_info_node.add_key_value("arch", "32bit");
2923 #else
2924     compiler_info_node.add_key_value("arch", "64bit");
2925 #endif
2926
2927     char_vec data;
2928     doc.serialize(data, true, 0, false);
2929
2930     if (g_vogl_trace_writer.get_trace_archive()->add_buf_using_id(data.get_ptr(), data.size(), VOGL_TRACE_ARCHIVE_COMPILER_INFO_FILENAME).is_empty())
2931         vogl_error_printf("%s: Failed adding serialized compilerinfo to trace archive\n", VOGL_FUNCTION_NAME);
2932     else
2933         vogl_message_printf("%s: Done resolving compilerinfo to symbols\n", VOGL_FUNCTION_NAME);
2934
2935     return true;
2936 }
2937
2938 //----------------------------------------------------------------------------------------------------------------------
2939 // vogl_flush_machineinfo_to_trace_file
2940 //----------------------------------------------------------------------------------------------------------------------
2941 static bool vogl_flush_machineinfo_to_trace_file()
2942 {
2943     scoped_mutex lock(g_vogl_trace_mutex);
2944
2945     if (!g_vogl_trace_writer.is_opened() || (g_vogl_trace_writer.get_trace_archive() == NULL))
2946         return false;
2947
2948     json_document doc;
2949
2950     vogl_message_printf("%s: Begin resolving machineinfo to symbols\n", VOGL_FUNCTION_NAME);
2951
2952     json_node *pRoot = doc.get_root();
2953
2954     btrace_get_machine_info(pRoot);
2955
2956     char_vec data;
2957     doc.serialize(data, true, 0, false);
2958
2959     if (g_vogl_trace_writer.get_trace_archive()->add_buf_using_id(data.get_ptr(), data.size(), VOGL_TRACE_ARCHIVE_MACHINE_INFO_FILENAME).is_empty())
2960         vogl_error_printf("%s: Failed adding serialized machineinfo to trace archive\n", VOGL_FUNCTION_NAME);
2961     else
2962         vogl_message_printf("%s: Done resolving machineinfo to symbols\n", VOGL_FUNCTION_NAME);
2963
2964     return true;
2965 }
2966
2967 //----------------------------------------------------------------------------------------------------------------------
2968 // vogl_entrypoint_serializer::begin
2969 //----------------------------------------------------------------------------------------------------------------------
2970 inline bool vogl_entrypoint_serializer::begin(gl_entrypoint_id_t id, vogl_context *pContext)
2971 {
2972     if (m_in_begin)
2973     {
2974         // Nothing good will come of this - the output will be fucked.
2975         vogl_error_printf("%s: begin() call not matched with end() - something is very wrong!)\n", VOGL_METHOD_NAME);
2976         VOGL_ASSERT_ALWAYS;
2977         return false;
2978     }
2979
2980     m_in_begin = true;
2981
2982     uint64_t thread_id = vogl_get_current_kernel_thread_id();
2983     uint64_t context_id = pContext ? reinterpret_cast<uint64_t>(pContext->get_context_handle()) : 0;
2984
2985     m_packet.begin_construction(id, context_id, g_vogl_trace_writer.get_next_gl_call_counter(), thread_id, utils::RDTSC());
2986
2987     if ((g_backtrace_all_calls) && (g_vogl_trace_writer.is_opened()))
2988     {
2989         // Take a backtrace and store its hashtable index into the packet.
2990         m_packet.set_backtrace_hash_index(vogl_backtrace(1));
2991     }
2992
2993     return true;
2994 }
2995
2996 //----------------------------------------------------------------------------------------------------------------------
2997 // vogl_is_in_null_mode
2998 //----------------------------------------------------------------------------------------------------------------------
2999 static inline bool vogl_is_in_null_mode()
3000 {
3001     return g_null_mode;
3002 }
3003
3004 //----------------------------------------------------------------------------------------------------------------------
3005 // vogl_is_in_null_mode
3006 //----------------------------------------------------------------------------------------------------------------------
3007 static inline bool vogl_func_is_nulled(gl_entrypoint_id_t id)
3008 {
3009     return vogl_is_in_null_mode() && g_vogl_entrypoint_descs[id].m_is_nullable;
3010 }
3011
3012 //----------------------------------------------------------------------------------------------------------------------
3013 // Custom array parameter size helper macros
3014 // IMPORTANT: These helpers need to not crash or return weird shit if there is no context current, in case the client
3015 // messes up and makes a GL call without an active context.
3016 //----------------------------------------------------------------------------------------------------------------------
3017 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_GL_ENUM(pname) g_gl_enums.get_pname_count(pname)
3018
3019 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXChooseFBConfig_attrib_list(e, c, rt, r, nu, ne, a, p) vogl_determine_attrib_list_array_size(attrib_list)
3020 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXChooseVisual_attribList(e, c, rt, r, nu, ne, a, p) vogl_determine_attrib_list_array_size(attribList)
3021 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXCreateWindow_attrib_list(e, c, rt, r, nu, ne, a, p) vogl_determine_attrib_list_array_size(attrib_list)
3022 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXCreatePixmap_attrib_list(e, c, rt, r, nu, ne, a, p) vogl_determine_attrib_list_array_size(attrib_list)
3023 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXCreatePbuffer_attrib_list(e, c, rt, r, nu, ne, a, p) vogl_determine_attrib_list_array_size(attrib_list)
3024 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXCreateContextAttribsARB_attrib_list(e, c, rt, r, nu, ne, a, p) vogl_determine_attrib_list_array_size(attrib_list)
3025
3026 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXBindVideoDeviceNV_attrib_list(e, c, rt, r, nu, ne, a, p) vogl_determine_attrib_list_array_size(attrib_list)
3027 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXBindTexImageEXT_attrib_list(e, c, rt, r, nu, ne, a, p) vogl_determine_attrib_list_array_size(attrib_list)
3028
3029 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetUniformLocation_name(e, c, rt, r, nu, ne, a, p) (name ? (strlen((const char *)name) + 1) : -1)
3030 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetUniformLocationARB_name(e, c, rt, r, nu, ne, a, p) (name ? (strlen(name) + 1) : -1)
3031
3032 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glBindAttribLocation_name(e, c, rt, r, nu, ne, a, p) (name ? (strlen((const char *)name) + 1) : -1)
3033 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glBindAttribLocationARB_name(e, c, rt, r, nu, ne, a, p) (name ? (strlen(name) + 1) : -1)
3034
3035 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetAttribLocationARB_name(e, c, rt, r, nu, ne, a, p) (name ? (strlen((const char *)name) + 1) : -1)
3036 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetAttribLocation_name(e, c, rt, r, nu, ne, a, p) (name ? (strlen((const char *)name) + 1) : -1)
3037
3038 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetShaderInfoLog_infoLog(e, c, rt, r, nu, ne, a, p) (length ? (*length + 1) : bufSize)
3039 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetInfoLogARB_infoLog(e, c, rt, r, nu, ne, a, p) (length ? (*length + 1) : maxLength)
3040
3041 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glClearBufferData_data(e, c, rt, r, nu, ne, a, p) vogl_get_image_format_size_in_bytes(format, type)
3042
3043 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glVertexPointer_pointer(e, c, rt, r, nu, ne, a, p) 0
3044 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glVertexPointerEXT_pointer(e, c, rt, r, nu, ne, a, p) 0
3045 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glColorPointer_pointer(e, c, rt, r, nu, ne, a, p) 0
3046 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glColorPointerEXT_pointer(e, c, rt, r, nu, ne, a, p) 0
3047 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glSecondaryColorPointer_pointer(e, c, rt, r, nu, ne, a, p) 0
3048 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTexCoordPointer_pointer(e, c, rt, r, nu, ne, a, p) 0
3049 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTexCoordPointerEXT_pointer(e, c, rt, r, nu, ne, a, p) 0
3050 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glFogCoordPointer_pointer(e, c, rt, r, nu, ne, a, p) 0
3051 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glFogCoordPointerEXT_pointer(e, c, rt, r, nu, ne, a, p) 0
3052 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glIndexPointer_pointer(e, c, rt, r, nu, ne, a, p) 0
3053 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glIndexPointerEXT_pointer(e, c, rt, r, nu, ne, a, p) 0
3054 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glNormalPointer_pointer(e, c, rt, r, nu, ne, a, p) 0
3055 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glNormalPointerEXT_pointer(e, c, rt, r, nu, ne, a, p) 0
3056 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glEdgeFlagPointer_pointer(e, c, rt, r, nu, ne, a, p) 0
3057 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glEdgeFlagPointerEXT_pointer(e, c, rt, r, nu, ne, a, p) 0
3058 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glInterleavedArrays_pointer(e, c, rt, r, nu, ne, a, p) 0
3059
3060 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glVertexAttribPointer_pointer(e, c, rt, r, nu, ne, a, p) 0
3061 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glVertexAttribIPointer_pointer(e, c, rt, r, nu, ne, a, p) 0
3062 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glVertexAttribPointerARB_pointer(e, c, rt, r, nu, ne, a, p) 0
3063
3064 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glDrawRangeElements_indices(e, c, rt, r, nu, ne, a, p) 0
3065 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glDrawRangeElementsBaseVertex_indices(e, c, rt, r, nu, ne, a, p) 0
3066 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glDrawRangeElementsEXT_indices(e, c, rt, r, nu, ne, a, p) 0
3067 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glDrawElements_indices(e, c, rt, r, nu, ne, a, p) 0
3068 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glDrawElementsInstanced_indices(e, c, rt, r, nu, ne, a, p) 0
3069 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glDrawElementsInstancedARB_indices(e, c, rt, r, nu, ne, a, p) 0
3070 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glDrawElementsInstancedEXT_indices(e, c, rt, r, nu, ne, a, p) 0
3071 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glDrawElementsBaseVertex_indices(e, c, rt, r, nu, ne, a, p) 0
3072
3073 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glNamedBufferDataEXT_data(e, c, rt, r, nu, ne, a, p) size
3074 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glNamedBufferSubDataEXT_data(e, c, rt, r, nu, ne, a, p) size
3075
3076 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glMap1f_points(e, c, rt, r, nu, ne, a, p) vogl_determine_glMap1_size(target, stride, order)
3077 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glMap1d_points(e, c, rt, r, nu, ne, a, p) vogl_determine_glMap1_size(target, stride, order)
3078 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glMap2f_points(e, c, rt, r, nu, ne, a, p) vogl_determine_glMap2_size(target, ustride, uorder, vstride, vorder)
3079 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glMap2d_points(e, c, rt, r, nu, ne, a, p) vogl_determine_glMap2_size(target, ustride, uorder, vstride, vorder)
3080
3081 //----------------------------------------------------------------------------------------------------------------------
3082 // Texture/image API's array size helper macros
3083 //----------------------------------------------------------------------------------------------------------------------
3084 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTexImage1D_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, 1, 1)
3085 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTexImage2D_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, 1)
3086 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTexImage3D_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, depth)
3087 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTexImage3DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, depth)
3088
3089 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTexSubImage1D_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, 1, 1)
3090 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTexSubImage1DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, 1, 1)
3091
3092 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTexSubImage2D_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, 1)
3093 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTexSubImage2DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, 1)
3094
3095 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTexSubImage3D_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, depth)
3096 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTexSubImage3DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, depth)
3097
3098 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTextureImage3DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, depth)
3099 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTextureImage2DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, 1)
3100 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTextureImage1DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, 1, 1)
3101
3102 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTextureSubImage3DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, depth)
3103 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTextureSubImage2DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, 1)
3104 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glTextureSubImage1DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, 1, 1)
3105
3106 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glMultiTexImage3DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, depth)
3107 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glMultiTexImage2DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, 1)
3108 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glMultiTexImage1DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, 1, 1)
3109
3110 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glMultiTexSubImage3DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, depth)
3111 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glMultiTexSubImage2DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, 1)
3112 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glMultiTexSubImage1DEXT_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, 1, 1)
3113
3114 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glDrawPixels_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, 1)
3115 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glConvolutionFilter1D_image(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, 1, 1)
3116 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glConvolutionFilter2D_image(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, 1)
3117
3118 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glColorTable_table(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, 1, 1)
3119 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glColorSubTable_data(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, count, 1, 1)
3120
3121 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glBitmap_size(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(GL_COLOR_INDEX, GL_BITMAP, width, height, 1)
3122 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glPolygonStipple_mask(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(GL_COLOR_INDEX, GL_BITMAP, 32, 32, 1)
3123
3124 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetTexImage_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_tex_target_image_size(target, level, format, type)
3125
3126 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glStringMarkerGREMEDY_string(e, c, rt, r, nu, ne, a, p) ((string) ? (!(len) ? (strlen(static_cast<const char *>(string)) + 1) : (len)) : 0)
3127
3128 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glViewportArrayv_v(e, c, rt, r, nu, ne, a, p) (4 * (count))
3129 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glScissorArrayv_v(e, c, rt, r, nu, ne, a, p) (4 * (count))
3130 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glDepthRangeArrayv_v(e, c, rt, r, nu, ne, a, p) (2 * (count))
3131
3132 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetActiveAttrib_name(e, c, rt, r, nu, ne, a, p) ((name) ? (strlen((const char *)(name)) + 1) : -1)
3133 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetActiveAttribARB_name(e, c, rt, r, nu, ne, a, p) ((name) ? (strlen((const char *)(name)) + 1) : -1)
3134
3135 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glReadPixels_pixels(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, height, 1)
3136
3137 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glBindFragDataLocationIndexed_name(e, c, rt, r, nu, ne, a, p) (name ? (strlen((const char *)(name)) + 1) : -1)
3138 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glBindFragDataLocation_name(e, c, rt, r, nu, ne, a, p) (name ? (strlen((const char *)(name)) + 1) : -1)
3139 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glBindFragDataLocationEXT_name(e, c, rt, r, nu, ne, a, p) (name ? (strlen((const char *)(name)) + 1) : -1)
3140
3141 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glBitmap_bitmap(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(GL_COLOR_INDEX, GL_BITMAP, width, height, 1)
3142
3143 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetActiveUniform_name(e, c, rt, r, nu, ne, a, p) ((name) ? ((length) ? (*(length) + 1) : (bufSize)) : -1)
3144
3145 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetAttachedShaders_obj(e, c, rt, r, nu, ne, a, p) ((obj) ? ((count) ? *(count) : (maxCount)) : -1)
3146
3147 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetProgramInfoLog_infoLog(e, c, rt, r, nu, ne, a, p) ((infoLog) ? ((length) ? (*length + 1) : (bufSize)) : -1)
3148
3149 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetShaderSource_source(e, c, rt, r, nu, ne, a, p) ((source) ? ((length) ? (*(length) + 1) : (bufSize)) : -1)
3150
3151 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glCallLists_lists(e, c, rt, r, nu, ne, a, p) (vogl_get_gl_type_size(type) * (n))
3152
3153 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetDebugMessageLogARB_messageLog(e, c, rt, r, nu, ne, a, p) vogl_compute_message_log_size_in_bytes(count, lengths)
3154 static GLsizei vogl_compute_message_log_size_in_bytes(GLuint count, const GLsizei *lengths)
3155 {
3156     if (!lengths)
3157         return 0;
3158     GLsizei total_length = 0;
3159     for (uint i = 0; i < count; i++)
3160         total_length += lengths[i];
3161     return total_length;
3162 }
3163
3164 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glSeparableFilter2D_row(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, width, 1, 1)
3165 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glSeparableFilter2D_column(e, c, rt, r, nu, ne, a, p) vogl_get_image_size(format, type, height, 1, 1)
3166
3167 // TODO - These are all gets, so they aren't completely necessary for proper replaying because they have no side effects (except for possibly glError's).
3168 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetUniformfv_params(e, c, rt, r, nu, ne, a, p) -1
3169 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetUniformiv_params(e, c, rt, r, nu, ne, a, p) -1
3170 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetPolygonStipple_mask(e, c, rt, r, nu, ne, a, p) -1
3171 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetMapdv_v(e, c, rt, r, nu, ne, a, p) -1
3172 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetMapfv_v(e, c, rt, r, nu, ne, a, p) -1
3173 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetMapiv_v(e, c, rt, r, nu, ne, a, p) -1
3174 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetColorTable_table(e, c, rt, r, nu, ne, a, p) -1
3175 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetConvolutionFilter_image(e, c, rt, r, nu, ne, a, p) -1
3176 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetSeparableFilter_row(e, c, rt, r, nu, ne, a, p) -1
3177 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetSeparableFilter_column(e, c, rt, r, nu, ne, a, p) -1
3178 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetSeparableFilter_span(e, c, rt, r, nu, ne, a, p) -1
3179 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetHistogram_values(e, c, rt, r, nu, ne, a, p) -1
3180 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetMinmax_values(e, c, rt, r, nu, ne, a, p) -1
3181 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetCompressedTexImage_img(e, c, rt, r, nu, ne, a, p) -1
3182
3183 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glClearBufferfv_value(e, c, rt, r, nu, ne, a, p) vogl_get_clearbuffer_array_size(buffer)
3184 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glClearBufferuiv_value(e, c, rt, r, nu, ne, a, p) vogl_get_clearbuffer_array_size(buffer)
3185 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glClearBufferiv_value(e, c, rt, r, nu, ne, a, p) vogl_get_clearbuffer_array_size(buffer)
3186 static int vogl_get_clearbuffer_array_size(GLenum buffer)
3187 {
3188     if ((buffer == GL_DEPTH) || (buffer == GL_STENCIL))
3189         return 1;
3190     else if (utils::is_in_set<GLenum, GLenum>(buffer, GL_COLOR, GL_FRONT, GL_BACK, GL_LEFT, GL_RIGHT, GL_FRONT_AND_BACK))
3191         return 4;
3192
3193     vogl_warning_printf("%s: Invalid value for buffer parameter passed to glClearBufferfv: 0x%04X\n", VOGL_FUNCTION_NAME, buffer);
3194     return -1;
3195 }
3196
3197 #if 0
3198 // TODO/investigate
3199 (vogltrace) Debug:
3200 glTexImage3DEXT
3201 (vogltrace) Debug:
3202 glColorTableSGI
3203 (vogltrace) Debug:
3204 glColorSubTableEXT
3205 (vogltrace) Debug:
3206 glColorTableEXT
3207 (vogltrace) Debug:
3208 glXChooseFBConfig
3209 #endif
3210
3211 //----------------------------------------------------------------------------------------------------------------------
3212 // Custom return parameter array size helper macros
3213 //----------------------------------------------------------------------------------------------------------------------
3214 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXGetFBConfigs(dpy, screen, nelements) (nelements ? *nelements : 1)
3215 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXChooseFBConfig(dpy, screen, attrib_list, nelements) (nelements ? *nelements : 1)
3216 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXChooseVisual(dpy, screen, nelements) 1
3217 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXGetVisualFromFBConfig(dpy, config) 1
3218 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXGetCurrentDisplay() 1
3219 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXGetCurrentDisplayEXT() 1
3220 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXEnumerateVideoCaptureDevicesNV(dpy, screen, nelements) (nelements ? *nelements : 1)
3221 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXEnumerateVideoDevicesNV(dpy, screen, nelements) (nelements ? *nelements : 1)
3222
3223 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetString(name) (result ? (strlen(reinterpret_cast<const char *>(result)) + 1) : 0)
3224 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glGetStringi(name, idx) (result ? (strlen(reinterpret_cast<const char *>(result)) + 1) : 0)
3225 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXGetClientString(dpy, name) (result ? (strlen(reinterpret_cast<const char *>(result)) + 1) : 0)
3226 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXQueryExtensionsString(dpy, name) (result ? (strlen(reinterpret_cast<const char *>(result)) + 1) : 0)
3227 #define DEF_FUNCTION_RETURN_PARAM_COMPUTE_ARRAY_SIZE_FUNC_glXQueryServerString(dpy, screen, name) (result ? (strlen(reinterpret_cast<const char *>(result)) + 1) : 0)
3228
3229 //----------------------------------------------------------------------------------------------------------------------
3230 static void vogl_print_hex(const void *p, uint64_t size, uint64_t type_size)
3231 {
3232     if (!p)
3233     {
3234         vogl_log_printf("<null>\n");
3235         return;
3236     }
3237
3238     const uint8_t *ptr = reinterpret_cast<const uint8_t *>(p);
3239     if ((type_size == 2) && (!(size & 1)))
3240     {
3241         int c = 0;
3242         vogl_log_printf("[ ");
3243         for (uint64_t i = 0; i < size; i += 2)
3244         {
3245             if (i)
3246                 vogl_log_printf(", ");
3247             vogl_log_printf("0x%04X", *reinterpret_cast<const uint16_t *>(ptr + i));
3248
3249             if ((++c & 7) == 7)
3250                 vogl_log_printf("\n");
3251         }
3252         vogl_log_printf(" ]");
3253     }
3254     else if ((type_size == 4) && (!(size & 3)))
3255     {
3256         int c = 0;
3257         vogl_log_printf("[ ");
3258         for (uint64_t i = 0; i < size; i += 4)
3259         {
3260             if (i)
3261                 vogl_log_printf(", ");
3262             vogl_log_printf("0x%08X", *reinterpret_cast<const uint32_t *>(ptr + i));
3263
3264             if ((++c & 7) == 7)
3265                 vogl_log_printf("\n");
3266         }
3267         vogl_log_printf(" ]");
3268     }
3269     else if ((type_size == 8) && (!(size & 7)))
3270     {
3271         int c = 0;
3272         vogl_log_printf("[ ");
3273         for (uint64_t i = 0; i < size; i += 8)
3274         {
3275             if (i)
3276                 vogl_log_printf(", ");
3277             vogl_log_printf("0x%" PRIX64, *reinterpret_cast<const uint64_t *>(ptr + i));
3278
3279             if ((++c & 7) == 7)
3280                 vogl_log_printf("\n");
3281         }
3282         vogl_log_printf(" ]");
3283     }
3284     else
3285     {
3286         int c = 0;
3287         if (size > 15)
3288             vogl_log_printf("\n");
3289
3290         vogl_log_printf("[ ");
3291         for (uint64_t i = 0; i < size; i++)
3292         {
3293             if (i)
3294                 vogl_log_printf(", ");
3295             vogl_log_printf("%02X", ptr[i]);
3296
3297             if ((++c & 63) == 63)
3298                 vogl_log_printf("\n");
3299         }
3300         vogl_log_printf(" ]");
3301     }
3302 }
3303
3304 //----------------------------------------------------------------------------------------------------------------------
3305 static void vogl_print_string(const char *pStr, uint64_t total_size)
3306 {
3307     for (uint64_t i = 0; i < total_size; i++)
3308     {
3309         uint8_t c = reinterpret_cast<const uint8_t *>(pStr)[i];
3310         if (c == '\n')
3311             vogl_log_printf("\n");
3312         else
3313         {
3314             if ((c < 32) || (c > 127))
3315                 c = '.';
3316             vogl_log_printf("%c", c);
3317         }
3318
3319         if ((i & 511) == 511)
3320             vogl_log_printf(" \\\n");
3321     }
3322 }
3323
3324 //----------------------------------------------------------------------------------------------------------------------
3325 template <typename T>
3326 static inline void vogl_dump_value_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pDesc, uint param_index, const char *pParam_name, const char *pType, vogl_ctype_t type, const T &val)
3327 {
3328     VOGL_NOTE_UNUSED(pContext);
3329
3330     int size = sizeof(T);
3331
3332     if (g_vogl_process_gl_ctypes[type].m_size != size)
3333         vogl_error_printf("%s: size mismatch on ctype %u\n", VOGL_FUNCTION_NAME, type);
3334
3335     if (serializer.is_in_begin())
3336     {
3337         serializer.add_param(param_index, type, &val, sizeof(val));
3338     }
3339
3340     if (g_dump_gl_calls_flag)
3341     {
3342         vogl_log_printf("%s: %s %s, ctype: %s, size: %i: ", pDesc, pType, pParam_name, g_vogl_process_gl_ctypes[type].m_pName, size);
3343
3344         if (Loki::TypeTraits<T>::isPointer)
3345         {
3346             vogl_log_printf("OPAQUE POINTER TYPE");
3347         }
3348         else if (size <= 0)
3349         {
3350             vogl_log_printf("OPAQUE TYPE");
3351         }
3352         else
3353         {
3354             vogl_print_hex(&val, size, size);
3355
3356             if (((type == VOGL_GLFLOAT) || (type == VOGL_GLCLAMPF)) && (size == sizeof(float)))
3357             {
3358                 float flt_val = *reinterpret_cast<const float *>(&val);
3359                 vogl_log_printf(" %f", flt_val);
3360             }
3361             else if (((type == VOGL_GLDOUBLE) || (type == VOGL_GLCLAMPD)) && (size == sizeof(double)))
3362             {
3363                 double dbl_val = *reinterpret_cast<const double *>(&val);
3364                 vogl_log_printf(" %f", dbl_val);
3365             }
3366             else if ((type == VOGL_GLENUM) && (size == sizeof(int)))
3367             {
3368                 GLenum enum_val = *reinterpret_cast<const GLenum *>(&val);
3369                 const char *pName = g_gl_enums.find_name(enum_val);
3370                 if (pName)
3371                     vogl_log_printf(" %s", pName);
3372             }
3373         }
3374
3375         vogl_log_printf("\n");
3376     }
3377 }
3378
3379 //----------------------------------------------------------------------------------------------------------------------
3380 template <typename T>
3381 static inline void vogl_dump_ptr_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pDesc, uint param_index, const char *pParam_name, const char *pType, vogl_ctype_t type, const T &val)
3382 {
3383     VOGL_NOTE_UNUSED(pContext);
3384
3385     VOGL_ASSUME(Loki::TypeTraits<T>::isPointer);
3386     int size = sizeof(T);
3387
3388     if (g_vogl_process_gl_ctypes[type].m_size != size)
3389         vogl_error_printf("%s: size mismatch on ctype %u\n", VOGL_FUNCTION_NAME, type);
3390
3391     if (serializer.is_in_begin())
3392     {
3393         serializer.add_param(param_index, type, &val, sizeof(val));
3394     }
3395
3396     if (g_dump_gl_calls_flag)
3397     {
3398         vogl_log_printf("%s: %s %s, ctype: %s, size: %i, ptr: 0x%" PRIX64 "\n", pDesc, pType, pParam_name, g_vogl_process_gl_ctypes[type].m_pName, size, cast_val_to_uint64(val));
3399     }
3400 }
3401
3402 //----------------------------------------------------------------------------------------------------------------------
3403 template <class T>
3404 static inline void vogl_dump_ref_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pDesc, uint param_index, const char *pParam_name, const char *pType, vogl_ctype_t type, const T *pObj)
3405 {
3406     VOGL_NOTE_UNUSED(pContext);
3407
3408     if (g_vogl_process_gl_ctypes[type].m_size != sizeof(const T *))
3409         vogl_error_printf("%s: size mismatch on ctype %u\n", VOGL_FUNCTION_NAME, type);
3410
3411     int obj_size = gl_ctype_sizeof<T>::size;
3412
3413     vogl_ctype_t pointee_type = g_vogl_process_gl_ctypes[type].m_pointee_ctype;
3414     if (pointee_type == VOGL_INVALID_CTYPE)
3415     {
3416         vogl_error_printf("%s: Type %u doesn't have a pointee ctype\n", VOGL_FUNCTION_NAME, type);
3417         return;
3418     }
3419
3420     if (g_vogl_process_gl_ctypes[pointee_type].m_size != obj_size)
3421         vogl_error_printf("%s: size mismatch on pointee ctype %u\n", VOGL_FUNCTION_NAME, type);
3422
3423     if (serializer.is_in_begin())
3424     {
3425         serializer.add_param(param_index, type, &pObj, sizeof(pObj));
3426
3427         if ((pObj) && (obj_size > 0))
3428         {
3429             serializer.add_ref_client_memory(param_index, pointee_type, pObj, obj_size);
3430         }
3431     }
3432
3433     if (g_dump_gl_calls_flag)
3434     {
3435         vogl_log_printf("%s: %s %s, ptr: 0x%" PRIX64 ", ctype: %s, pointee_ctype: %s, pointee_size: %i: ", pDesc, pType, pParam_name, cast_val_to_uint64(pObj), g_vogl_process_gl_ctypes[type].m_pName, g_vogl_process_gl_ctypes[pointee_type].m_pName, obj_size);
3436         if (!pObj)
3437         {
3438             vogl_log_printf("NULL");
3439         }
3440         else if (obj_size <= 0)
3441         {
3442             vogl_log_printf("OPAQUE TYPE");
3443         }
3444         else
3445         {
3446             vogl_print_hex(pObj, obj_size, obj_size);
3447         }
3448
3449         vogl_log_printf("\n");
3450     }
3451 }
3452
3453 //----------------------------------------------------------------------------------------------------------------------
3454 template <class T>
3455 static inline void vogl_dump_array_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pDesc, uint param_index, const char *pParam_name, const char *pType, vogl_ctype_t type, const T *pArray, int64_t size)
3456 {
3457     VOGL_NOTE_UNUSED(pContext);
3458
3459     int64_t obj_size = gl_ctype_sizeof<T>::size;
3460     int64_t total_size = obj_size * math::maximum<int64_t>(size, 0);
3461
3462     vogl_ctype_t pointee_type = g_vogl_process_gl_ctypes[type].m_pointee_ctype;
3463     if (((type == VOGL_CONST_VOID_PTR) || (type == VOGL_CONST_GLVOID_PTR) || (type == VOGL_GLVOID_PTR)) && (size > 0))
3464     {
3465         obj_size = 1;
3466         total_size = size;
3467     }
3468     else
3469     {
3470         if (pointee_type == VOGL_INVALID_CTYPE)
3471         {
3472             vogl_error_printf("%s: Type %u doesn't have a pointee ctype\n", VOGL_FUNCTION_NAME, type);
3473             return;
3474         }
3475
3476         if (g_vogl_process_gl_ctypes[pointee_type].m_size != obj_size)
3477             vogl_error_printf("%s: Size mismatch on ctype %u\n", VOGL_FUNCTION_NAME, type);
3478     }
3479
3480     bool pointee_is_ptr = g_vogl_process_gl_ctypes[pointee_type].m_is_pointer;
3481
3482     if (serializer.is_in_begin())
3483     {
3484         serializer.add_param(param_index, type, &pArray, sizeof(pArray));
3485
3486         if ((pArray) && (size > 0) && (obj_size > 0))
3487         {
3488             serializer.add_array_client_memory(param_index, pointee_type, size, pArray, total_size);
3489         }
3490     }
3491
3492     if (g_dump_gl_calls_flag)
3493     {
3494         vogl_log_printf("%s: %s %s, ptr: 0x%" PRIX64 ", ctype: %s, pointee_ctype: %s, size: %" PRIi64 ", pointee_size: %" PRIi64 ", total size: %" PRIi64 ": ", pDesc, pType, pParam_name, cast_val_to_uint64(pArray), g_vogl_process_gl_ctypes[type].m_pName, g_vogl_process_gl_ctypes[pointee_type].m_pName,
3495                        size, obj_size, total_size);
3496         if (!pArray)
3497         {
3498             vogl_log_printf("NULL");
3499         }
3500         else if (size <= 0)
3501         {
3502             vogl_log_printf("UNKNOWN SIZE");
3503         }
3504         else if (obj_size <= 0)
3505         {
3506             vogl_log_printf("OPAQUE TYPE");
3507         }
3508         else
3509         {
3510             if (pointee_is_ptr)
3511             {
3512                 vogl_log_printf("POINTEE IS POINTER: \n");
3513             }
3514
3515             vogl_print_hex(pArray, total_size, obj_size);
3516
3517             switch (pointee_type)
3518             {
3519                 case VOGL_GLUBYTE:
3520                 case VOGL_GLBYTE:
3521                 case VOGL_GLCHAR:
3522                 case VOGL_GLCHARARB:
3523                 {
3524                     vogl_log_printf("\nAs string: \"");
3525                     vogl_print_string(*(const char **)&pArray, total_size);
3526                     vogl_log_printf("\"");
3527                     break;
3528                 }
3529                 default:
3530                 {
3531                     break;
3532                 }
3533             }
3534         }
3535
3536         vogl_log_printf("\n");
3537     }
3538 }
3539
3540 //----------------------------------------------------------------------------------------------------------------------
3541 // Return parameters
3542 //----------------------------------------------------------------------------------------------------------------------
3543 template <class T>
3544 static inline void vogl_dump_return_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pType, vogl_ctype_t type, int64_t size, const T &val)
3545 {
3546     VOGL_NOTE_UNUSED(size);
3547
3548     VOGL_ASSUME(!Loki::TypeTraits<T>::isPointer);
3549
3550     vogl_dump_value_param(pContext, serializer, "RETURN_VALUE", VOGL_RETURN_PARAM_INDEX, "result", pType, type, val);
3551 }
3552
3553 static inline void vogl_dump_return_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pType, vogl_ctype_t type, int64_t size, unsigned char *pPtr)
3554 {
3555     VOGL_NOTE_UNUSED(size);
3556
3557     size_t array_size = pPtr ? (strlen(reinterpret_cast<const char *>(pPtr)) + 1) : 0;
3558     vogl_dump_array_param(pContext, serializer, "RETURN_UCHAR_PTR", VOGL_RETURN_PARAM_INDEX, "result", pType, type, pPtr, array_size);
3559 }
3560
3561 static inline void vogl_dump_return_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pType, vogl_ctype_t type, int64_t size, const GLubyte *pPtr)
3562 {
3563     VOGL_NOTE_UNUSED(size);
3564
3565     size_t array_size = pPtr ? (strlen(reinterpret_cast<const char *>(pPtr)) + 1) : 0;
3566     vogl_dump_array_param(pContext, serializer, "RETURN_GLUBYTE_PTR", VOGL_RETURN_PARAM_INDEX, "result", pType, type, pPtr, array_size);
3567 }
3568
3569 static inline void vogl_dump_return_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pType, vogl_ctype_t type, int64_t size, void *pPtr)
3570 {
3571     VOGL_NOTE_UNUSED(size);
3572
3573     // opaque data
3574     vogl_dump_ptr_param(pContext, serializer, "RETURN_VOID_PTR", VOGL_RETURN_PARAM_INDEX, "result", pType, type, pPtr);
3575 }
3576
3577 static inline void vogl_dump_return_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pType, vogl_ctype_t type, int64_t size, unsigned int *pPtr)
3578 {
3579     vogl_dump_array_param(pContext, serializer, "RETURN_UINT_PTR", VOGL_RETURN_PARAM_INDEX, "result", pType, type, pPtr, size);
3580 }
3581
3582 static inline void vogl_dump_return_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pType, vogl_ctype_t type, int64_t size, long unsigned int *pPtr)
3583 {
3584     vogl_dump_array_param(pContext, serializer, "RETUURN_LUINT_PTR", VOGL_RETURN_PARAM_INDEX, "result", pType, type, pPtr, size);
3585 }
3586
3587 static inline void vogl_dump_return_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pType, vogl_ctype_t type, int64_t size, XVisualInfo *pPtr)
3588 {
3589     VOGL_NOTE_UNUSED(size);
3590
3591     vogl_dump_ref_param(pContext, serializer, "RETURN_XVISUALINFO_PTR", VOGL_RETURN_PARAM_INDEX, "result", pType, type, pPtr);
3592 }
3593
3594 static inline void vogl_dump_return_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pType, vogl_ctype_t type, int64_t size, GLXFBConfig *pPtr)
3595 {
3596     VOGL_NOTE_UNUSED(size);
3597
3598     vogl_dump_ref_param(pContext, serializer, "RETURN_GLXFBCONFIG_PTR", VOGL_RETURN_PARAM_INDEX, "result", pType, type, pPtr);
3599 }
3600
3601 static inline void vogl_dump_return_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pType, vogl_ctype_t type, int64_t size, GLXContext pPtr)
3602 {
3603     VOGL_NOTE_UNUSED(size);
3604
3605     // opaque data
3606     vogl_dump_ptr_param(pContext, serializer, "RETURN_GLXCONTEXT", VOGL_RETURN_PARAM_INDEX, "result", pType, type, pPtr);
3607 }
3608
3609 static inline void vogl_dump_return_param(vogl_context *pContext, vogl_entrypoint_serializer &serializer, const char *pType, vogl_ctype_t type, int64_t size, _XDisplay *pPtr)
3610 {
3611     VOGL_NOTE_UNUSED(size);
3612
3613     // opaque data
3614     vogl_dump_ptr_param(pContext, serializer, "RETURN_XDISPLAY_PTR", VOGL_RETURN_PARAM_INDEX, "result", pType, type, pPtr);
3615 }
3616
3617 //----------------------------------------------------------------------------------------------------------------------
3618 // vogl_should_serialize_call
3619 //----------------------------------------------------------------------------------------------------------------------
3620 static inline bool vogl_should_serialize_call(gl_entrypoint_id_t func, vogl_context *pContext)
3621 {
3622     bool is_in_display_list = pContext && pContext->is_composing_display_list();
3623     bool is_listable = g_vogl_entrypoint_descs[func].m_is_listable;
3624     bool is_whitelisted = g_vogl_entrypoint_descs[func].m_whitelisted_for_displaylists;
3625
3626     if ((is_in_display_list) && (is_listable) && (!is_whitelisted))
3627     {
3628         vogl_error_printf("%s: Called GL func %s is not currently supported in display lists! The replay will diverge.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[func].m_pName);
3629     }
3630
3631     // When we're writing a trace we ALWAYS want to serialize, even if the func is not listable (so we can at least process the trace, etc.)
3632     if (g_vogl_trace_writer.is_opened())
3633         return true;
3634
3635     return is_in_display_list && is_whitelisted;
3636 }
3637
3638 //----------------------------------------------------------------------------------------------------------------------
3639 // vogl_write_packet_to_trace
3640 //----------------------------------------------------------------------------------------------------------------------
3641 static inline void vogl_write_packet_to_trace(vogl_trace_packet &packet)
3642 {
3643     if (!g_vogl_trace_writer.is_opened())
3644         return;
3645
3646     scoped_mutex lock(g_vogl_trace_mutex);
3647
3648     // The trace got closed on another thread while we where serializing - this is OK I guess.
3649     // This can happen when control+c is pressed.
3650     if (g_vogl_trace_writer.is_opened())
3651     {
3652         bool success = g_vogl_trace_writer.write_packet(packet);
3653
3654         if (success)
3655         {
3656             if ((g_flush_files_after_each_call) ||
3657                 ((g_flush_files_after_each_swap) && (packet.get_entrypoint_id() == VOGL_ENTRYPOINT_glXSwapBuffers)))
3658             {
3659                 g_vogl_trace_writer.flush();
3660
3661                 if (g_vogl_pLog_stream)
3662                     g_vogl_pLog_stream->flush();
3663             }
3664         }
3665         else
3666         {
3667             vogl_error_printf("%s: Failed writing to trace file! Exiting app.\n", VOGL_FUNCTION_NAME);
3668
3669             // TODO: Add common termination handler func, or just stop writing to the trace? or call abort()
3670             exit(EXIT_FAILURE);
3671         }
3672     }
3673 }
3674
3675 //----------------------------------------------------------------------------------------------------------------------
3676 // Declare gl/glx internal wrapper functions, all start with "vogl_" (so we can safely get the address of our internal
3677 // wrappers, avoiding global symbol naming conflicts that I started to see on test apps when I started building with -fPIC)
3678 //----------------------------------------------------------------------------------------------------------------------
3679 // func begin
3680 #define DEF_PROTO_EXPORTED(ret, name, args, params)    \
3681     static ret VOGL_GLUER(vogl_, name) args           \
3682     {                                                  \
3683         if (vogl_func_is_nulled(VOGL_ENTRYPOINT_##name)) \
3684         {                                              \
3685             return (ret)0;                             \
3686         }                                              \
3687         ret result;
3688 #define DEF_PROTO_EXPORTED_VOID(ret, name, args, params) \
3689     static ret VOGL_GLUER(vogl_, name) args             \
3690     {                                                    \
3691         if (vogl_func_is_nulled(VOGL_ENTRYPOINT_##name))   \
3692             return;
3693 #define DEF_PROTO_INTERNAL(ret, name, args, params)    \
3694     static ret VOGL_GLUER(vogl_, name) args           \
3695     {                                                  \
3696         if (vogl_func_is_nulled(VOGL_ENTRYPOINT_##name)) \
3697         {                                              \
3698             return (ret)0;                             \
3699         }                                              \
3700         ret result;
3701 #define DEF_PROTO_INTERNAL_VOID(ret, name, args, params) \
3702     static ret VOGL_GLUER(vogl_, name) args             \
3703     {                                                    \
3704         if (vogl_func_is_nulled(VOGL_ENTRYPOINT_##name))   \
3705             return;
3706
3707 #define DEF_FUNCTION_BEGIN(exported, category, ret, ret_type_enum, num_params, name, args, params) exported(ret, name, args, params)
3708 #define DEF_FUNCTION_BEGIN_VOID(exported, category, ret, ret_type_enum, num_params, name, args, params) exported(ret, name, args, params)
3709
3710 // TODO: When serialization is off, the array size determination code is still active (which could involve useless GL calls and in the worst case could trigger errors when/if this code is buggy).
3711 // A completely different code path through the wrapper should be used.
3712
3713 // func init (after the optional custom function prolog)
3714 #define VOGL_MASTER_FUNCTION_PROLOG(name, params)                                                                                                                                                                                                  \
3715     if (g_dump_gl_calls_flag)                                                                                                                                                                                                                     \
3716     {                                                                                                                                                                                                                                             \
3717         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "" PRIX64 "\n", #name, vogl_get_current_kernel_thread_id());                                                                                                                                                           \
3718     }                                                                                                                                                                                                                                             \
3719     vogl_thread_local_data *pTLS_data = vogl_entrypoint_prolog(VOGL_ENTRYPOINT_##name);                                                                                                                                                              \
3720     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)                                                                                                                                                                      \
3721     {                                                                                                                                                                                                                                             \
3722         vogl_warning_printf("%s: GL call detected while libvogltrace was itself making a GL call to func %s! This call will not be traced.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName); \
3723         return g_vogl_actual_gl_entrypoints.m_##name params;                                                                                                                                                                                       \
3724     }                                                                                                                                                                                                                                             \
3725     vogl_context *pContext = pTLS_data->m_pContext;                                                                                                                                                                                                \
3726     vogl_entrypoint_serializer &trace_serializer = pTLS_data->m_serializer;                                                                                                                                                                        \
3727     if (vogl_should_serialize_call(VOGL_ENTRYPOINT_##name, pContext))                                                                                                                                                                               \
3728     {                                                                                                                                                                                                                                             \
3729         if (!trace_serializer.begin(VOGL_ENTRYPOINT_##name, pContext))                                                                                                                                                                             \
3730         {                                                                                                                                                                                                                                         \
3731             vogl_warning_printf("%s: Reentrant wrapper call detected!\n", VOGL_FUNCTION_NAME);                                                                                                                                                   \
3732             return g_vogl_actual_gl_entrypoints.m_##name params;                                                                                                                                                                                   \
3733         }                                                                                                                                                                                                                                         \
3734     }
3735
3736 #define VOGL_MASTER_FUNCTION_PROLOG_VOID(name, params)                                                                                                                                                                                             \
3737     if (g_dump_gl_calls_flag)                                                                                                                                                                                                                     \
3738     {                                                                                                                                                                                                                                             \
3739         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "\n", #name, vogl_get_current_kernel_thread_id());                                                                                                                                                           \
3740     }                                                                                                                                                                                                                                             \
3741     vogl_thread_local_data *pTLS_data = vogl_entrypoint_prolog(VOGL_ENTRYPOINT_##name);                                                                                                                                                              \
3742     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)                                                                                                                                                                      \
3743     {                                                                                                                                                                                                                                             \
3744         vogl_warning_printf("%s: GL call detected while libvogltrace was itself making a GL call to func %s! This call will not be traced.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName); \
3745         g_vogl_actual_gl_entrypoints.m_##name params;                                                                                                                                                                                              \
3746         return;                                                                                                                                                                                                                                   \
3747     }                                                                                                                                                                                                                                             \
3748     vogl_context *pContext = pTLS_data->m_pContext;                                                                                                                                                                                                \
3749     vogl_entrypoint_serializer &trace_serializer = pTLS_data->m_serializer;                                                                                                                                                                        \
3750     if (vogl_should_serialize_call(VOGL_ENTRYPOINT_##name, pContext))                                                                                                                                                                               \
3751     {                                                                                                                                                                                                                                             \
3752         if (!trace_serializer.begin(VOGL_ENTRYPOINT_##name, pContext))                                                                                                                                                                             \
3753         {                                                                                                                                                                                                                                         \
3754             vogl_warning_printf("%s: Reentrant wrapper call detected!\n", VOGL_FUNCTION_NAME);                                                                                                                                                   \
3755             g_vogl_actual_gl_entrypoints.m_##name params;                                                                                                                                                                                          \
3756             return;                                                                                                                                                                                                                               \
3757         }                                                                                                                                                                                                                                         \
3758     }
3759
3760 #define DEF_FUNCTION_INIT(exported, category, ret, ret_type_enum, num_params, name, args, params) VOGL_MASTER_FUNCTION_PROLOG(name, params)
3761 #define DEF_FUNCTION_INIT_VOID(exported, category, ret, ret_type_enum, num_params, name, args, params) VOGL_MASTER_FUNCTION_PROLOG_VOID(name, params)
3762
3763 // func params
3764 #define DEF_FUNCTION_INPUT_VALUE_PARAM(idx, spectype, type, type_enum, param) vogl_dump_value_param(pContext, trace_serializer, "INPUT_VALUE", idx, #param, #type, type_enum, param);
3765 #define DEF_FUNCTION_INPUT_REFERENCE_PARAM(idx, spectype, type, type_enum, param) vogl_dump_ref_param(pContext, trace_serializer, "INPUT_REF", idx, #param, #type, type_enum, param);
3766 #define DEF_FUNCTION_INPUT_ARRAY_PARAM(idx, spectype, type, type_enum, param, size) vogl_dump_array_param(pContext, trace_serializer, "INPUT_ARRAY", idx, #param, #type, type_enum, param, size);
3767
3768 #define DEF_FUNCTION_OUTPUT_REFERENCE_PARAM(idx, spectype, type, type_enum, param) vogl_dump_ref_param(pContext, trace_serializer, "OUTPUT_REF", idx, #param, #type, type_enum, param);
3769 #define DEF_FUNCTION_OUTPUT_ARRAY_PARAM(idx, spectype, type, type_enum, param, size) vogl_dump_array_param(pContext, trace_serializer, "OUTPUT_ARRAY", idx, #param, #type, type_enum, param, size);
3770
3771 #define DEF_FUNCTION_RETURN_PARAM(spectype, type, type_enum, size) vogl_dump_return_param(pContext, trace_serializer, #type, type_enum, size, result);
3772
3773 #define DEF_FUNCTION_CALL_GL(exported, category, ret, ret_type_enum, num_params, name, args, params) \
3774     if (trace_serializer.is_in_begin())                                                              \
3775         trace_serializer.set_gl_begin_rdtsc(utils::RDTSC());                                         \
3776     result = g_vogl_actual_gl_entrypoints.m_##name params;                                            \
3777     if (trace_serializer.is_in_begin())                                                              \
3778         trace_serializer.set_gl_end_rdtsc(utils::RDTSC());
3779
3780 #define DEF_FUNCTION_CALL_GL_VOID(exported, category, ret, ret_type_enum, num_params, name, args, params) \
3781     if (trace_serializer.is_in_begin())                                                                   \
3782         trace_serializer.set_gl_begin_rdtsc(utils::RDTSC());                                              \
3783     g_vogl_actual_gl_entrypoints.m_##name params;                                                          \
3784     if (trace_serializer.is_in_begin())                                                                   \
3785         trace_serializer.set_gl_end_rdtsc(utils::RDTSC());
3786
3787 // Be careful modifying these END macros. They must be compatible with the logic in the glXSwapBuffers GL end prolog!
3788 // func end (after the optional custom function epilog)
3789 #define DEF_FUNCTION_END(exported, category, ret, ret_type_enum, num_params, name, args, params)                \
3790     if (g_dump_gl_calls_flag)                                                                                   \
3791     {                                                                                                           \
3792         vogl_message_printf("** END %s res=%s 0x%" PRIX64 "\n", #name, #ret, cast_val_to_uint64(result));                  \
3793     }                                                                                                           \
3794     if (trace_serializer.is_in_begin())                                                                         \
3795     {                                                                                                           \
3796         trace_serializer.end();                                                                                 \
3797         vogl_write_packet_to_trace(trace_serializer.get_packet());                                               \
3798         if (pContext)                                                                                           \
3799             pContext->add_packet_to_current_display_list(VOGL_ENTRYPOINT_##name, trace_serializer.get_packet()); \
3800     }                                                                                                           \
3801     return result;                                                                                              \
3802     }
3803
3804 #define DEF_FUNCTION_END_VOID(exported, category, ret, ret_type_enum, num_params, name, args, params)           \
3805     if (g_dump_gl_calls_flag)                                                                                   \
3806     {                                                                                                           \
3807         vogl_message_printf("** END %s\n", #name);                                                               \
3808     }                                                                                                           \
3809     if (trace_serializer.is_in_begin())                                                                         \
3810     {                                                                                                           \
3811         trace_serializer.end();                                                                                 \
3812         vogl_write_packet_to_trace(trace_serializer.get_packet());                                               \
3813         if (pContext)                                                                                           \
3814             pContext->add_packet_to_current_display_list(VOGL_ENTRYPOINT_##name, trace_serializer.get_packet()); \
3815     }                                                                                                           \
3816     }
3817
3818 //----------------------------------------------------------------------------------------------------------------------
3819 // gl/glx override functions
3820 //----------------------------------------------------------------------------------------------------------------------
3821 static __GLXextFuncPtr vogl_get_proc_address_helper(const GLubyte *procName)
3822 {
3823     if (!procName)
3824         return NULL;
3825
3826     if (g_dump_gl_calls_flag)
3827     {
3828         vogl_printf("glXGetProcAddress: \"%s\"\n", procName);
3829     }
3830
3831     if (!GL_ENTRYPOINT(glXGetProcAddress))
3832         return NULL;
3833
3834     // FIXME: We need to make this per-context on OSX (under Linux it doesn't matter, because all GL funcs are statically exported anyway as symbols by the SO)
3835     __GLXextFuncPtr pActual_entrypoint = GL_ENTRYPOINT(glXGetProcAddress)(procName);
3836     if (!pActual_entrypoint)
3837         return NULL;
3838
3839     for (uint i = 0; i < VOGL_NUM_ENTRYPOINTS; ++i)
3840     {
3841         if (vogl_strcmp(reinterpret_cast<const char *>(procName), g_vogl_entrypoint_descs[i].m_pName) == 0)
3842         {
3843             if (g_vogl_entrypoint_descs[i].m_pWrapper_func)
3844             {
3845                 if (!g_vogl_entrypoint_descs[i].m_is_whitelisted)
3846                 {
3847                     // TODO: Only print this message once
3848                     vogl_warning_printf("%s: App has queried the address of non-whitelisted GL func %s (this will only be a problem if this func. is actually called, and will reported during tracing and at exit)\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[i].m_pName);
3849                 }
3850
3851                 return reinterpret_cast<__GLXextFuncPtr>(g_vogl_entrypoint_descs[i].m_pWrapper_func);
3852             }
3853             else
3854                 break;
3855         }
3856     }
3857
3858     return pActual_entrypoint;
3859 }
3860
3861 //----------------------------------------------------------------------------------------------------------------------
3862 // Custom function handler implementations
3863 //----------------------------------------------------------------------------------------------------------------------
3864 #define DEF_FUNCTION_CUSTOM_HANDLER_glInternalTraceCommandRAD(exported, category, ret, ret_type_enum, num_params, name, args, params)
3865 static void vogl_glInternalTraceCommandRAD(GLuint cmd, GLuint size, const GLubyte *data)
3866 {
3867     if (g_dump_gl_calls_flag)
3868     {
3869         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
3870     }
3871
3872     if (g_vogl_trace_writer.is_opened())
3873     {
3874         vogl_entrypoint_serializer serializer(VOGL_ENTRYPOINT_glInternalTraceCommandRAD, NULL);
3875
3876         uint64_t cur_rdtsc = utils::RDTSC();
3877         serializer.set_gl_begin_end_rdtsc(cur_rdtsc, cur_rdtsc + 1);
3878
3879         serializer.add_param(0, VOGL_GLUINT, &cmd, sizeof(cmd));
3880         serializer.add_param(1, VOGL_GLUINT, &size, sizeof(size));
3881         serializer.add_param(2, VOGL_CONST_GLUBYTE_PTR, &data, sizeof(data));
3882
3883         switch (cmd)
3884         {
3885             case cITCRDemarcation:
3886             {
3887                 break;
3888             }
3889             case cITCRKeyValueMap:
3890             {
3891                 if ((size == sizeof(key_value_map)) && (data))
3892                 {
3893                     // Directly jam in the key value map, so it properly serializes as readable JSON.
3894                     serializer.get_key_value_map() = *reinterpret_cast<const key_value_map *>(data);
3895                 }
3896                 else
3897                 {
3898                     vogl_error_printf("%s: data pointer is NULL, or invalid key_value_map size %u\n", VOGL_FUNCTION_NAME, size);
3899                     VOGL_ASSERT_ALWAYS;
3900                 }
3901                 break;
3902             }
3903             default:
3904             {
3905                 vogl_error_printf("%s: Unknown trace command type %u\n", VOGL_FUNCTION_NAME, cmd);
3906                 VOGL_ASSERT_ALWAYS;
3907                 break;
3908             }
3909         }
3910
3911         serializer.end();
3912         vogl_write_packet_to_trace(serializer.get_packet());
3913     }
3914
3915     if (g_dump_gl_calls_flag)
3916     {
3917         vogl_message_printf("** END %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
3918     }
3919 }
3920
3921 //----------------------------------------------------------------------------------------------------------------------
3922 // TODO: Merge this with vogl_glXGetProcAddressARB to avoid duplicated code
3923 #define DEF_FUNCTION_CUSTOM_HANDLER_glXGetProcAddress(exported, category, ret, ret_type_enum, num_params, name, args, params)
3924 static __GLXextFuncPtr vogl_glXGetProcAddress(const GLubyte *procName)
3925 {
3926     uint64_t begin_rdtsc = utils::RDTSC();
3927
3928     if (g_dump_gl_calls_flag)
3929     {
3930         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
3931     }
3932
3933     vogl_thread_local_data *pTLS_data = vogl_entrypoint_prolog(VOGL_ENTRYPOINT_glXGetProcAddress);
3934     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)
3935     {
3936         vogl_warning_printf("%s: GL call detected while libvogltrace was itself making a GL call to func %s! This call will not be traced.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName);
3937         return GL_ENTRYPOINT(glXGetProcAddress)(procName);
3938     }
3939
3940     uint64_t gl_begin_rdtsc = utils::RDTSC();
3941     __GLXextFuncPtr ptr = vogl_get_proc_address_helper(procName);
3942     uint64_t gl_end_rdtsc = utils::RDTSC();
3943
3944     if (g_vogl_trace_writer.is_opened())
3945     {
3946         vogl_entrypoint_serializer serializer(VOGL_ENTRYPOINT_glXGetProcAddress, g_context_manager.get_current());
3947         serializer.set_begin_rdtsc(begin_rdtsc);
3948         serializer.set_gl_begin_end_rdtsc(gl_begin_rdtsc, gl_end_rdtsc);
3949         serializer.add_param(0, VOGL_CONST_GLUBYTE_PTR, &procName, sizeof(procName));
3950         if (procName)
3951         {
3952             size_t len = strlen(reinterpret_cast<const char *>(procName)) + 1;
3953             serializer.add_array_client_memory(0, VOGL_GLUBYTE, len, procName, len);
3954         }
3955         serializer.add_return_param(VOGL_GLXEXTFUNCPTR, &ptr, sizeof(ptr));
3956         serializer.end();
3957         vogl_write_packet_to_trace(serializer.get_packet());
3958     }
3959
3960     if (g_dump_gl_calls_flag)
3961     {
3962         vogl_message_printf("** END %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
3963     }
3964
3965     return ptr;
3966 }
3967
3968 //----------------------------------------------------------------------------------------------------------------------
3969 #define DEF_FUNCTION_CUSTOM_HANDLER_glXGetProcAddressARB(exported, category, ret, ret_type_enum, num_params, name, args, params)
3970 static __GLXextFuncPtr vogl_glXGetProcAddressARB(const GLubyte *procName)
3971 {
3972     uint64_t begin_rdtsc = utils::RDTSC();
3973
3974     if (g_dump_gl_calls_flag)
3975     {
3976         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
3977     }
3978
3979     vogl_thread_local_data *pTLS_data = vogl_entrypoint_prolog(VOGL_ENTRYPOINT_glXGetProcAddressARB);
3980     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)
3981     {
3982         vogl_warning_printf("%s: GL call detected while libvogltrace was itself making a GL call to func %s! This call will not be traced.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName);
3983         return GL_ENTRYPOINT(glXGetProcAddressARB)(procName);
3984     }
3985
3986     uint64_t gl_begin_rdtsc = utils::RDTSC();
3987     __GLXextFuncPtr ptr = vogl_get_proc_address_helper(procName);
3988     uint64_t gl_end_rdtsc = utils::RDTSC();
3989
3990     if (g_vogl_trace_writer.is_opened())
3991     {
3992         vogl_entrypoint_serializer serializer(VOGL_ENTRYPOINT_glXGetProcAddressARB, g_context_manager.get_current());
3993         serializer.set_begin_rdtsc(begin_rdtsc);
3994         serializer.set_gl_begin_end_rdtsc(gl_begin_rdtsc, gl_end_rdtsc);
3995         serializer.add_param(0, VOGL_CONST_GLUBYTE_PTR, &procName, sizeof(procName));
3996         if (procName)
3997         {
3998             size_t len = strlen(reinterpret_cast<const char *>(procName)) + 1;
3999             serializer.add_array_client_memory(0, VOGL_GLUBYTE, len, procName, len);
4000         }
4001         serializer.add_return_param(VOGL_GLXEXTFUNCPTR, &ptr, sizeof(ptr));
4002         serializer.end();
4003         vogl_write_packet_to_trace(serializer.get_packet());
4004     }
4005
4006     if (g_dump_gl_calls_flag)
4007     {
4008         vogl_message_printf("** END %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
4009     }
4010
4011     return ptr;
4012 }
4013
4014 //----------------------------------------------------------------------------------------------------------------------
4015 static void vogl_add_make_current_key_value_fields(const Display *dpy, GLXDrawable drawable, Bool result, vogl_context *pVOGL_context, vogl_entrypoint_serializer &serializer)
4016 {
4017     if ((result) && (pVOGL_context))
4018     {
4019         vogl_scoped_gl_error_absorber gl_error_absorber(pVOGL_context);
4020         VOGL_NOTE_UNUSED(gl_error_absorber);
4021
4022         GLint cur_viewport[4];
4023         GL_ENTRYPOINT(glGetIntegerv)(GL_VIEWPORT, cur_viewport);
4024
4025         serializer.add_key_value(string_hash("viewport_x"), cur_viewport[0]);
4026         serializer.add_key_value(string_hash("viewport_y"), cur_viewport[1]);
4027         serializer.add_key_value(string_hash("viewport_width"), cur_viewport[2]);
4028         serializer.add_key_value(string_hash("viewport_height"), cur_viewport[3]);
4029     }
4030
4031     if ((pVOGL_context) && (pVOGL_context->get_window_width() < 0))
4032     {
4033         if ((dpy) && (drawable) && (result))
4034         {
4035             Window root;
4036             int x, y;
4037             unsigned int width, height, border_width, depth;
4038             if (XGetGeometry(const_cast<Display *>(dpy), drawable, &root, &x, &y, &width, &height, &border_width, &depth) != False)
4039             {
4040                 pVOGL_context->set_window_dimensions(width, height);
4041
4042                 serializer.add_key_value(string_hash("win_width"), width);
4043                 serializer.add_key_value(string_hash("win_height"), height);
4044
4045                 if (g_dump_gl_calls_flag)
4046                 {
4047                     vogl_log_printf("** Current window dimensions: %ix%i\n", width, height);
4048                 }
4049             }
4050         }
4051     }
4052 }
4053
4054 #if 0
4055 //----------------------------------------------------------------------------------------------------------------------
4056 // HACK HACK - for NS2 experiment in AMD
4057 #define DEF_FUNCTION_CUSTOM_HANDLER_glTexStorage2D(exported, category, ret, ret_type_enum, num_params, name, args, params)
4058 static void vogl_glTexStorage2D(        GLenum target,
4059                                 GLsizei levels,
4060                                 GLenum internalformat,
4061                                 GLsizei width,
4062                                 GLsizei height)
4063 {
4064         GL_ENTRYPOINT(glTexParameteri)(target, GL_TEXTURE_BASE_LEVEL, 0);
4065         GL_ENTRYPOINT(glTexParameteri)(target, GL_TEXTURE_MAX_LEVEL, levels - 1);
4066
4067         if (target == GL_TEXTURE_2D)
4068         {
4069                 for (uint i = 0; i < levels; i++)
4070                 {
4071                         uint w = math::maximum<uint>(width >> i, 1);
4072                         uint h = math::maximum<uint>(height >> i, 1);
4073                         vogl::vector<uint8> pixels(w * h * 4);
4074                         GL_ENTRYPOINT(glTexImage2D)(GL_TEXTURE_2D, i, internalformat, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels.get_ptr());
4075                 }
4076         }
4077 }
4078 #endif
4079
4080 //----------------------------------------------------------------------------------------------------------------------
4081 #define DEF_FUNCTION_CUSTOM_HANDLER_glXMakeCurrent(exported, category, ret, ret_type_enum, num_params, name, args, params)
4082 static Bool vogl_glXMakeCurrent(const Display *dpy, GLXDrawable drawable, GLXContext context)
4083 {
4084     uint64_t begin_rdtsc = utils::RDTSC();
4085
4086     if (g_dump_gl_calls_flag)
4087     {
4088         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
4089     }
4090
4091     vogl_thread_local_data *pTLS_data = vogl_entrypoint_prolog(VOGL_ENTRYPOINT_glXMakeCurrent);
4092     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)
4093     {
4094         vogl_warning_printf("%s: GL call detected while libvogltrace was itself making a GL call to func %s! This call will not be traced.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName);
4095         return GL_ENTRYPOINT(glXMakeCurrent)(dpy, drawable, context);
4096     }
4097
4098     if (g_dump_gl_calls_flag)
4099     {
4100         vogl_log_printf("** glXMakeCurrent TID: 0x%" PRIX64 " dpy: 0x%" PRIX64 " drawable: 0x%" PRIX64 " context: 0x%" PRIX64 "\n", vogl_get_current_kernel_thread_id(), cast_val_to_uint64(dpy), cast_val_to_uint64(drawable), cast_val_to_uint64(context));
4101     }
4102
4103     vogl_context *pCur_context = g_context_manager.get_current();
4104
4105     vogl_context *pNew_context = context ? g_context_manager.lookup_vogl_context(context) : NULL;
4106     if ((context) && (!pNew_context))
4107     {
4108         vogl_error_printf("%s: Unknown context handle 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(context));
4109     }
4110
4111     if (pCur_context)
4112     {
4113         if (pCur_context != pNew_context)
4114             pCur_context->on_release_current_prolog();
4115         else
4116             vogl_warning_printf("%s: Context 0x%" PRIX64 " is already current (redundant call)\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(context));
4117     }
4118
4119     uint64_t gl_begin_rdtsc = utils::RDTSC();
4120     Bool result = GL_ENTRYPOINT(glXMakeCurrent)(dpy, drawable, context);
4121     uint64_t gl_end_rdtsc = utils::RDTSC();
4122
4123     if ((result) && (pCur_context != pNew_context))
4124     {
4125         if (pCur_context)
4126             g_context_manager.release_current();
4127
4128         if (context)
4129         {
4130             vogl_context *p = g_context_manager.make_current(context);
4131             VOGL_ASSERT(p == pNew_context);
4132             VOGL_NOTE_UNUSED(p);
4133
4134             pNew_context->set_display(dpy);
4135             pNew_context->set_drawable(drawable);
4136             pNew_context->set_read_drawable(drawable);
4137         }
4138     }
4139
4140     if (g_dump_gl_calls_flag)
4141     {
4142         vogl_log_printf("** glXMakeCurrent result: %i\n", result);
4143     }
4144
4145     if (g_vogl_trace_writer.is_opened())
4146     {
4147         vogl_entrypoint_serializer serializer(VOGL_ENTRYPOINT_glXMakeCurrent, pCur_context);
4148         serializer.set_begin_rdtsc(begin_rdtsc);
4149         serializer.set_gl_begin_end_rdtsc(gl_begin_rdtsc, gl_end_rdtsc);
4150         serializer.add_param(0, VOGL_CONST_DISPLAY_PTR, &dpy, sizeof(dpy));
4151         serializer.add_param(1, VOGL_GLXDRAWABLE, &drawable, sizeof(drawable));
4152         serializer.add_param(2, VOGL_GLXCONTEXT, &context, sizeof(context));
4153         serializer.add_return_param(VOGL_BOOL, &result, sizeof(result));
4154
4155         vogl_add_make_current_key_value_fields(dpy, drawable, result, pNew_context, serializer);
4156
4157         serializer.end();
4158         vogl_write_packet_to_trace(serializer.get_packet());
4159     }
4160
4161     if (g_dump_gl_calls_flag)
4162     {
4163         vogl_message_printf("** END %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
4164     }
4165
4166     static bool s_added_atexit = false;
4167     if (!s_added_atexit)
4168     {
4169             // atexit routines are called in the reverse order in which they were registered. We would like
4170                 //  our vogl_atexit() routine to be called before anything else (Ie C++ destructors, etc.) So we
4171                 //  put atexit at the end of vogl_global_init() and another at the end of glXMakeCurrent.
4172         atexit(vogl_atexit);
4173         s_added_atexit = true;
4174     }
4175     
4176     return result;
4177 }
4178
4179 //----------------------------------------------------------------------------------------------------------------------
4180 #define DEF_FUNCTION_CUSTOM_HANDLER_glXMakeContextCurrent(exported, category, ret, ret_type_enum, num_params, name, args, params)
4181 static Bool vogl_glXMakeContextCurrent(const Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext context)
4182 {
4183     uint64_t begin_rdtsc = utils::RDTSC();
4184
4185     if (g_dump_gl_calls_flag)
4186     {
4187         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
4188     }
4189
4190     vogl_thread_local_data *pTLS_data = vogl_entrypoint_prolog(VOGL_ENTRYPOINT_glXMakeContextCurrent);
4191     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)
4192     {
4193         vogl_warning_printf("%s: GL call detected while libvogltrace was itself making a GL call to func %s! This call will not be traced.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName);
4194         return GL_ENTRYPOINT(glXMakeContextCurrent)(dpy, draw, read, context);
4195     }
4196
4197     if (g_dump_gl_calls_flag)
4198     {
4199         vogl_log_printf("** glXMakeContextCurrent TID: 0x%" PRIX64 " dpy: 0x%" PRIX64 " draw: 0x%" PRIX64 " read: 0x%" PRIX64 " context: 0x%" PRIX64 "\n", vogl_get_current_kernel_thread_id(), cast_val_to_uint64(dpy), cast_val_to_uint64(draw), cast_val_to_uint64(read), cast_val_to_uint64(context));
4200     }
4201
4202     vogl_context *pCur_context = g_context_manager.get_current();
4203
4204     vogl_context *pNew_context = context ? g_context_manager.lookup_vogl_context(context) : NULL;
4205     if ((context) && (!pNew_context))
4206     {
4207         vogl_error_printf("%s: Unknown coontext handle 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(context));
4208     }
4209
4210     if (pCur_context)
4211     {
4212         if (pCur_context != pNew_context)
4213             pCur_context->on_release_current_prolog();
4214         else
4215             vogl_warning_printf("%s: Context 0x%" PRIX64 " is already current (redundant call)\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(context));
4216     }
4217
4218     uint64_t gl_begin_rdtsc = utils::RDTSC();
4219     Bool result = GL_ENTRYPOINT(glXMakeContextCurrent)(dpy, draw, read, context);
4220     uint64_t gl_end_rdtsc = utils::RDTSC();
4221
4222     if ((result) && (pCur_context != pNew_context))
4223     {
4224         if (pCur_context)
4225             g_context_manager.release_current();
4226
4227         if (context)
4228         {
4229             vogl_context *p = g_context_manager.make_current(context);
4230             VOGL_ASSERT(p == pNew_context);
4231             VOGL_NOTE_UNUSED(p);
4232
4233             pNew_context->set_display(dpy);
4234             pNew_context->set_drawable(draw);
4235             pNew_context->set_read_drawable(read);
4236         }
4237     }
4238
4239     if (g_dump_gl_calls_flag)
4240     {
4241         vogl_log_printf("** glXMakeCurrent result: %i\n", result);
4242     }
4243
4244     if (g_vogl_trace_writer.is_opened())
4245     {
4246         vogl_entrypoint_serializer serializer(VOGL_ENTRYPOINT_glXMakeContextCurrent, pCur_context);
4247         serializer.set_begin_rdtsc(begin_rdtsc);
4248         serializer.set_gl_begin_end_rdtsc(gl_begin_rdtsc, gl_end_rdtsc);
4249         serializer.add_param(0, VOGL_CONST_DISPLAY_PTR, &dpy, sizeof(dpy));
4250         serializer.add_param(1, VOGL_GLXDRAWABLE, &draw, sizeof(draw));
4251         serializer.add_param(2, VOGL_GLXDRAWABLE, &read, sizeof(read));
4252         serializer.add_param(3, VOGL_GLXCONTEXT, &context, sizeof(context));
4253         serializer.add_return_param(VOGL_BOOL, &result, sizeof(result));
4254
4255         vogl_add_make_current_key_value_fields(dpy, draw, result, pNew_context, serializer);
4256
4257         serializer.end();
4258         vogl_write_packet_to_trace(serializer.get_packet());
4259     }
4260
4261     if (g_dump_gl_calls_flag)
4262     {
4263         vogl_message_printf("** END %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
4264     }
4265
4266     return result;
4267 }
4268
4269 //----------------------------------------------------------------------------------------------------------------------
4270 static bool vogl_screen_capture_callback(uint width, uint height, uint pitch, size_t size, GLenum pixel_format, GLenum pixel_type, const void *pImage, void *pOpaque, uint64_t frame_index)
4271 {
4272     vogl_context *pContext = static_cast<vogl_context *>(pOpaque);
4273
4274     if ((!pContext) || (!width) || (!height))
4275     {
4276         VOGL_ASSERT_ALWAYS;
4277         return false;
4278     }
4279
4280     if ((pixel_format != GL_RGB) || (pixel_type != GL_UNSIGNED_BYTE) || (pitch != width * 3))
4281     {
4282         VOGL_ASSERT_ALWAYS;
4283         return false;
4284     }
4285
4286     if (g_command_line_params.get_value_as_bool("vogl_dump_png_screenshots"))
4287     {
4288         size_t png_size = 0;
4289         void *pPNG_data = tdefl_write_image_to_png_file_in_memory_ex(pImage, width, height, 3, &png_size, 1, true);
4290
4291         dynamic_string screenshot_filename(cVarArg, "%s__%08" PRIx64 "_%08" PRIu64 ".png", g_command_line_params.get_value_as_string("vogl_screenshot_prefix", 0, "screenshot").get_ptr(), cast_val_to_uint64(pContext->get_context_handle()), cast_val_to_uint64(frame_index));
4292         if (!file_utils::write_buf_to_file(screenshot_filename.get_ptr(), pPNG_data, png_size))
4293         {
4294             console::error("Failed writing PNG screenshot to file %s\n", screenshot_filename.get_ptr());
4295         }
4296
4297         mz_free(pPNG_data);
4298     }
4299     else if (g_command_line_params.get_value_as_bool("vogl_dump_jpeg_screenshots"))
4300     {
4301         int jpeg_quality = g_command_line_params.get_value_as_int("vogl_jpeg_quality", 0, 80, 1, 100);
4302
4303         long unsigned int jpeg_size = 0;
4304         unsigned char *pJPEG_data = NULL;
4305
4306         tjhandle _jpegCompressor = tjInitCompress();
4307
4308         int status = tjCompress2(_jpegCompressor, (unsigned char *)pImage, width, pitch, height, TJPF_RGB,
4309                                  &pJPEG_data, &jpeg_size, TJSAMP_422, jpeg_quality,
4310                                  TJFLAG_FASTDCT | TJFLAG_BOTTOMUP);
4311
4312         tjDestroy(_jpegCompressor);
4313
4314         if (status == 0)
4315         {
4316             dynamic_string screenshot_filename(cVarArg, "%s_%08" PRIx64 "_%08" PRIu64 ".jpg",
4317                 g_command_line_params.get_value_as_string("vogl_screenshot_prefix", 0, "screenshot").get_ptr(),
4318                 cast_val_to_uint64(pContext->get_context_handle()),
4319                 cast_val_to_uint64(frame_index));
4320             if (!file_utils::write_buf_to_file(screenshot_filename.get_ptr(), pJPEG_data, jpeg_size))
4321             {
4322                 console::error("Failed writing JPEG screenshot to file %s\n", screenshot_filename.get_ptr());
4323             }
4324         }
4325
4326         tjFree(pJPEG_data);
4327     }
4328
4329     if (g_command_line_params.get_value_as_bool("vogl_dump_backbuffer_hashes") || g_command_line_params.get_value_as_bool("vogl_hash_backbuffer"))
4330     {
4331         uint64_t backbuffer_crc64;
4332
4333         if (g_command_line_params.get_value_as_bool("vogl_sum_hashing"))
4334         {
4335             backbuffer_crc64 = calc_sum64(static_cast<const uint8 *>(pImage), size);
4336         }
4337         else
4338         {
4339             backbuffer_crc64 = calc_crc64(CRC64_INIT, static_cast<const uint8 *>(pImage), size);
4340         }
4341
4342         console::printf("Frame %" PRIu64 " hash: 0x%016" PRIX64 "\n", cast_val_to_uint64(frame_index), backbuffer_crc64);
4343
4344         dynamic_string backbuffer_hash_file;
4345         if (g_command_line_params.get_value_as_string(backbuffer_hash_file, "vogl_dump_backbuffer_hashes"))
4346         {
4347             FILE *pFile = vogl_fopen(backbuffer_hash_file.get_ptr(), "a");
4348             if (!pFile)
4349                 console::error("Failed writing to backbuffer hash file %s\n", backbuffer_hash_file.get_ptr());
4350             else
4351             {
4352                 vogl_fprintf(pFile, "0x%016" PRIX64 "\n", backbuffer_crc64);
4353                 vogl_fclose(pFile);
4354             }
4355         }
4356     }
4357     return true;
4358 }
4359
4360 //----------------------------------------------------------------------------------------------------------------------
4361 static void vogl_tick_screen_capture(vogl_context *pVOGL_context)
4362 {
4363     if (!pVOGL_context)
4364         return;
4365
4366     uint width = pVOGL_context->get_window_width();
4367     uint height = pVOGL_context->get_window_height();
4368     if ((!width) || (!height))
4369         return;
4370
4371     bool grab_backbuffer = g_command_line_params.get_value_as_bool("vogl_dump_backbuffer_hashes") || g_command_line_params.get_value_as_bool("vogl_hash_backbuffer") ||
4372                            g_command_line_params.get_value_as_bool("vogl_dump_jpeg_screenshots") || g_command_line_params.get_value_as_bool("vogl_dump_png_screenshots");
4373     if (!grab_backbuffer)
4374         return;
4375
4376     vogl_scoped_gl_error_absorber gl_error_absorber(pVOGL_context);
4377     VOGL_NOTE_UNUSED(gl_error_absorber);
4378
4379     if (!pVOGL_context->get_framebuffer_capturer().is_initialized())
4380     {
4381         if (!pVOGL_context->get_framebuffer_capturer().init(2, vogl_screen_capture_callback, pVOGL_context, GL_RGB, GL_UNSIGNED_BYTE))
4382         {
4383             vogl_error_printf("%s: Failed initializing context's vogl_framebuffer_capturer!\n", VOGL_FUNCTION_NAME);
4384             return;
4385         }
4386     }
4387
4388     if (!pVOGL_context->get_framebuffer_capturer().capture(width, height, 0, GL_BACK, pVOGL_context->get_frame_index()))
4389     {
4390         vogl_error_printf("%s: vogl_framebuffer_capturer::capture() failed!\n", VOGL_FUNCTION_NAME);
4391         return;
4392     }
4393 }
4394
4395 //----------------------------------------------------------------------------------------------------------------------
4396 static vogl_gl_state_snapshot *vogl_snapshot_state(const Display *dpy, GLXDrawable drawable, vogl_context *pCur_context)
4397 {
4398     VOGL_NOTE_UNUSED(dpy);
4399     VOGL_NOTE_UNUSED(drawable);
4400
4401     // pCur_context may be NULL!
4402
4403     if (!GL_ENTRYPOINT(glXGetCurrentContext) || !GL_ENTRYPOINT(glXGetCurrentDisplay) ||
4404         !GL_ENTRYPOINT(glXGetCurrentDrawable) || !GL_ENTRYPOINT(glXGetCurrentReadDrawable))
4405         return NULL;
4406
4407     timed_scope ts(VOGL_FUNCTION_NAME);
4408
4409     g_context_manager.lock();
4410
4411     if (vogl_check_gl_error())
4412         vogl_error_printf("%s: A GL error occured sometime before this function was called\n", VOGL_FUNCTION_NAME);
4413
4414     const Display *orig_dpy = GL_ENTRYPOINT(glXGetCurrentDisplay)();
4415     GLXDrawable orig_drawable = GL_ENTRYPOINT(glXGetCurrentDrawable)();
4416     GLXDrawable orig_read_drawable = GL_ENTRYPOINT(glXGetCurrentReadDrawable)();
4417     GLXContext orig_context = GL_ENTRYPOINT(glXGetCurrentContext)();
4418
4419     if (!orig_dpy)
4420         orig_dpy = dpy;
4421
4422     vogl_check_gl_error();
4423
4424     vogl_gl_state_snapshot *pSnapshot = vogl_new(vogl_gl_state_snapshot);
4425
4426     const glxcontext_map &contexts = g_context_manager.get_context_map();
4427
4428     // TODO: Find a better way of determining which window dimensions to use.
4429     // No context is current, let's just find the biggest window.
4430     uint win_width = 0;
4431     uint win_height = 0;
4432     if (pCur_context)
4433     {
4434         win_width = pCur_context->get_window_width();
4435         win_height = pCur_context->get_window_height();
4436     }
4437     else
4438     {
4439         glxcontext_map::const_iterator it;
4440         for (it = contexts.begin(); it != contexts.end(); ++it)
4441         {
4442             vogl_context *pVOGL_context = it->second;
4443             if ((pVOGL_context->get_window_width() > (int)win_width) || (pVOGL_context->get_window_height() > (int)win_height))
4444             {
4445                 win_width = pVOGL_context->get_window_width();
4446                 win_height = pVOGL_context->get_window_height();
4447             }
4448         }
4449     }
4450
4451     vogl_message_printf("%s: Beginning capture: width %u, height %u\n", VOGL_FUNCTION_NAME, win_width, win_height);
4452
4453     if (!pSnapshot->begin_capture(win_width, win_height, pCur_context ? (uint64_t)pCur_context->get_context_handle() : 0, 0, g_vogl_trace_writer.get_cur_gl_call_counter(), true))
4454     {
4455         vogl_error_printf("%s: Failed beginning capture\n", VOGL_FUNCTION_NAME);
4456
4457         vogl_delete(pSnapshot);
4458         pSnapshot = NULL;
4459
4460         g_context_manager.unlock();
4461
4462         return NULL;
4463     }
4464
4465     vogl_printf("%s: Capturing %u context(s)\n", VOGL_FUNCTION_NAME, contexts.size());
4466
4467     glxcontext_map::const_iterator it;
4468     for (it = contexts.begin(); it != contexts.end(); ++it)
4469     {
4470         GLXContext glx_context = it->first;
4471         vogl_context *pVOGL_context = it->second;
4472
4473         if (pVOGL_context->get_deleted_flag())
4474         {
4475             vogl_error_printf("%s: Context 0x%" PRIX64 " has been deleted but is the root of a sharelist group - this scenario is not yet supported for state snapshotting.\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(glx_context));
4476             break;
4477         }
4478
4479         if (pVOGL_context->get_has_been_made_current())
4480         {
4481             if (!GL_ENTRYPOINT(glXMakeCurrent)(pVOGL_context->get_display(), pVOGL_context->get_drawable(), glx_context))
4482             {
4483                 vogl_error_printf("%s: Failed switching to trace context 0x%" PRIX64 ". (This context may be current on another thread.) Capture failed!\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(glx_context));
4484                 break;
4485             }
4486
4487             if (pVOGL_context->get_total_mapped_buffers())
4488             {
4489                 vogl_error_printf("%s: Trace context 0x%" PRIX64 " has %u buffer(s) mapped across a call to glXSwapBuffers(), which is not currently supported!\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(glx_context), pVOGL_context->get_total_mapped_buffers());
4490                 break;
4491             }
4492
4493             if (pVOGL_context->is_composing_display_list())
4494             {
4495                 vogl_error_printf("%s: Trace context 0x%" PRIX64 " is currently composing a display list across a call to glXSwapBuffers()!\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(glx_context));
4496                 break;
4497             }
4498
4499             if (pVOGL_context->get_in_gl_begin())
4500             {
4501                 vogl_error_printf("%s: Trace context 0x%" PRIX64 " is currently in a glBegin() across a call to glXSwapBuffers(), which is not supported!\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(glx_context));
4502                 break;
4503             }
4504         }
4505
4506         vogl_capture_context_params &capture_context_params = pVOGL_context->get_capture_context_params();
4507
4508         if (!pSnapshot->capture_context(pVOGL_context->get_context_desc(), pVOGL_context->get_context_info(), pVOGL_context->get_handle_remapper(), capture_context_params))
4509         {
4510             vogl_error_printf("%s: Failed capturing trace context 0x%" PRIX64 ", capture failed\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(glx_context));
4511             break;
4512         }
4513     }
4514
4515     if ((it == contexts.end()) && (pSnapshot->end_capture()))
4516     {
4517         vogl_printf("%s: Capture succeeded\n", VOGL_FUNCTION_NAME);
4518     }
4519     else
4520     {
4521         vogl_printf("%s: Capture failed\n", VOGL_FUNCTION_NAME);
4522
4523         vogl_delete(pSnapshot);
4524         pSnapshot = NULL;
4525     }
4526
4527     if (orig_dpy)
4528     {
4529         GL_ENTRYPOINT(glXMakeContextCurrent)(orig_dpy, orig_drawable, orig_read_drawable, orig_context);
4530     }
4531
4532     vogl_check_gl_error();
4533
4534     g_context_manager.unlock();
4535
4536     return pSnapshot;
4537 }
4538
4539 //----------------------------------------------------------------------------------------------------------------------
4540 // vogl_end_capture
4541 // Important: This could be called at signal time!
4542 //----------------------------------------------------------------------------------------------------------------------
4543 static void vogl_end_capture(bool inside_signal_handler)
4544 {
4545     VOGL_FUNC_TRACER
4546
4547     VOGL_NOTE_UNUSED(inside_signal_handler);
4548
4549     vogl_debug_printf("%s\n", VOGL_FUNCTION_NAME);
4550
4551     g_context_manager.lock();
4552
4553     vogl_context *pVOGL_context = g_context_manager.get_current();
4554
4555     if (pVOGL_context)
4556     {
4557         pVOGL_context->get_framebuffer_capturer().flush();
4558
4559         if (inside_signal_handler)
4560         {
4561             vogl_thread_local_data *pTLS_data = vogl_get_thread_local_data();
4562             if ((pTLS_data) && (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID))
4563             {
4564                 vogl_error_printf("%s: Signal handler called while the tracer was calling OpenGL in func %s!\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName);
4565
4566                 if (g_vogl_trace_writer.is_opened())
4567                 {
4568                     vogl_entrypoint_serializer &trace_serializer = pTLS_data->m_serializer;
4569                     if (trace_serializer.is_in_begin())
4570                     {
4571                         vogl_error_printf("%s: Attempting to flush final trace packet\n", VOGL_FUNCTION_NAME);
4572
4573                         trace_serializer.end();
4574
4575                         vogl_write_packet_to_trace(trace_serializer.get_packet());
4576                     }
4577                 }
4578             }
4579         }
4580     }
4581
4582     g_context_manager.unlock();
4583
4584     scoped_mutex lock(g_vogl_trace_mutex);
4585
4586     if (g_vogl_trace_writer.is_opened())
4587     {
4588         dynamic_string filename(g_vogl_trace_writer.get_filename());
4589         
4590         vogl_flush_compilerinfo_to_trace_file();
4591         vogl_flush_machineinfo_to_trace_file();
4592         vogl_flush_backtrace_to_trace_file();
4593
4594         if (!g_vogl_trace_writer.close())
4595         {
4596             vogl_error_printf("%s: Failed closing trace file!\n", VOGL_FUNCTION_NAME);
4597
4598             if (g_vogl_pCapture_status_callback)
4599                 (*g_vogl_pCapture_status_callback)(NULL, g_vogl_pCapture_status_opaque);
4600         }
4601         else
4602         {
4603             if (g_vogl_pCapture_status_callback)
4604                 (*g_vogl_pCapture_status_callback)(filename.get_ptr(), g_vogl_pCapture_status_opaque);
4605         }
4606
4607         if (g_pJSON_node_pool)
4608         {
4609             uint64_t total_bytes_freed = static_cast<uint64_t>(g_pJSON_node_pool->free_unused_blocks());
4610             vogl_debug_printf("%s: Freed %" PRIu64 " bytes from the JSON object pool (%" PRIu64 " bytes remaining)\n", VOGL_FUNCTION_NAME, total_bytes_freed, static_cast<uint64_t>(g_pJSON_node_pool->get_total_heap_bytes()));
4611         }
4612     }
4613
4614     g_vogl_frames_remaining_to_capture = 0;
4615
4616     g_vogl_pCapture_status_callback = NULL;
4617     g_vogl_pCapture_status_opaque = NULL;
4618 }
4619
4620 //----------------------------------------------------------------------------------------------------------------------
4621 static bool vogl_write_snapshot_to_trace(const char *pTrace_filename, const Display *dpy, GLXDrawable drawable, vogl_context *pVOGL_context)
4622 {
4623     VOGL_FUNC_TRACER
4624     vogl_message_printf("%s: Snapshot begin\n", VOGL_FUNCTION_NAME);
4625
4626     // pVOGL_context may be NULL here!
4627
4628     vogl_unique_ptr<vogl_gl_state_snapshot> pSnapshot(vogl_snapshot_state(dpy, drawable, pVOGL_context));
4629     if (!pSnapshot.get())
4630     {
4631         vogl_error_printf("%s: Failed snapshotting GL/GLX state!\n", VOGL_FUNCTION_NAME);
4632
4633         VOGL_FUNC_TRACER
4634         vogl_end_capture();
4635         return false;
4636     }
4637
4638     pSnapshot->set_frame_index(0);
4639
4640     if (!g_vogl_trace_writer.open(pTrace_filename, NULL, true, false))
4641     {
4642         vogl_error_printf("%s: Failed creating trace file \"%s\"!\n", VOGL_FUNCTION_NAME, pTrace_filename);
4643
4644         VOGL_FUNC_TRACER
4645         vogl_end_capture();
4646         return false;
4647     }
4648
4649     vogl_archive_blob_manager &trace_archive = *g_vogl_trace_writer.get_trace_archive();
4650
4651     vogl_message_printf("%s: Serializing snapshot data to JSON document\n", VOGL_FUNCTION_NAME);
4652
4653     // TODO: This can take a lot of memory, probably better off to split the snapshot into separate smaller binary json or whatever files stored directly in the archive.
4654     json_document doc;
4655     if (!pSnapshot->serialize(*doc.get_root(), trace_archive, &g_vogl_process_gl_ctypes))
4656     {
4657         vogl_error_printf("%s: Failed serializing GL state snapshot!\n", VOGL_FUNCTION_NAME);
4658
4659         VOGL_FUNC_TRACER
4660         vogl_end_capture();
4661         return false;
4662     }
4663
4664     pSnapshot.reset();
4665
4666     vogl_message_printf("%s: Serializing JSON document to UBJ\n", VOGL_FUNCTION_NAME);
4667
4668     uint8_vec binary_snapshot_data;
4669     vogl::vector<char> snapshot_data;
4670
4671     // TODO: This can take a lot of memory
4672     doc.binary_serialize(binary_snapshot_data);
4673
4674     vogl_message_printf("%s: Compressing UBJ data and adding to trace archive\n", VOGL_FUNCTION_NAME);
4675
4676     dynamic_string binary_snapshot_id(trace_archive.add_buf_compute_unique_id(binary_snapshot_data.get_ptr(), binary_snapshot_data.size(), "binary_state_snapshot", VOGL_BINARY_JSON_EXTENSION));
4677     if (binary_snapshot_id.is_empty())
4678     {
4679         vogl_error_printf("%s: Failed adding binary GL snapshot file to output blob manager!\n", VOGL_FUNCTION_NAME);
4680
4681         VOGL_FUNC_TRACER
4682         vogl_end_capture();
4683         return false;
4684     }
4685
4686     binary_snapshot_data.clear();
4687
4688     snapshot_data.clear();
4689
4690 #if 0
4691         // TODO: This requires too much temp memory!
4692         doc.serialize(snapshot_data, true, 0, false);
4693
4694         dynamic_string snapshot_id(trace_archive.add_buf_compute_unique_id(snapshot_data.get_ptr(), snapshot_data.size(), "state_snapshot", VOGL_TEXT_JSON_EXTENSION));
4695         if (snapshot_id.is_empty())
4696         {
4697                 vogl_error_printf("%s: Failed adding binary GL snapshot file to output blob manager!\n", VOGL_FUNCTION_NAME);
4698                 g_vogl_trace_writer.deinit();
4699                 return false;
4700         }
4701 #endif
4702
4703     key_value_map snapshot_key_value_map;
4704     snapshot_key_value_map.insert("command_type", "state_snapshot");
4705     snapshot_key_value_map.insert("binary_id", binary_snapshot_id);
4706
4707 #if 0
4708         // TODO: This requires too much temp memory!
4709         snapshot_key_value_map.insert("id", snapshot_id);
4710 #endif
4711
4712     vogl_ctypes &trace_gl_ctypes = g_vogl_process_gl_ctypes;
4713     if (!vogl_write_glInternalTraceCommandRAD(g_vogl_trace_writer.get_stream(), &trace_gl_ctypes, cITCRKeyValueMap, sizeof(snapshot_key_value_map), reinterpret_cast<const GLubyte *>(&snapshot_key_value_map)))
4714     {
4715         VOGL_FUNC_TRACER
4716         vogl_end_capture();
4717         vogl_error_printf("%s: Failed writing to trace file!\n", VOGL_FUNCTION_NAME);
4718         return false;
4719     }
4720
4721     if (!vogl_write_glInternalTraceCommandRAD(g_vogl_trace_writer.get_stream(), &trace_gl_ctypes, cITCRDemarcation, 0, NULL))
4722     {
4723         VOGL_FUNC_TRACER
4724         vogl_end_capture();
4725         vogl_error_printf("%s: Failed writing to trace file!\n", VOGL_FUNCTION_NAME);
4726         return false;
4727     }
4728
4729     doc.clear(false);
4730
4731     if (g_pJSON_node_pool)
4732     {
4733         uint64_t total_bytes_freed = static_cast<uint64_t>(g_pJSON_node_pool->free_unused_blocks());
4734         vogl_debug_printf("%s: Freed %" PRIu64 " bytes from the JSON object pool (%" PRIu64 " bytes remaining)\n", VOGL_FUNCTION_NAME, total_bytes_freed, static_cast<uint64_t>(g_pJSON_node_pool->get_total_heap_bytes()));
4735     }
4736
4737     vogl_message_printf("%s: Snapshot complete\n", VOGL_FUNCTION_NAME);
4738
4739     return true;
4740 }
4741
4742 //----------------------------------------------------------------------------------------------------------------------
4743 static void vogl_capture_status_callback_func(const char *pFilename, void *pOpaque)
4744 {
4745     // only for testing
4746     VOGL_ASSERT(pOpaque == (void *)1);
4747     vogl_message_printf("%s: Filename \"%s\", opaque: %p\n", VOGL_FUNCTION_NAME, pFilename ? pFilename : "<null>", pOpaque);
4748 }
4749
4750 //----------------------------------------------------------------------------------------------------------------------
4751 static bool vogl_check_for_trigger_file(const char *pBase_name, dynamic_string &filename)
4752 {
4753     filename = pBase_name;
4754     if (!file_utils::does_file_exist(filename.get_ptr()))
4755     {
4756         dynamic_string path_to_check(g_command_line_params.get_value_as_string_or_empty("vogl_tracepath"));
4757         if (path_to_check.is_empty())
4758             path_to_check = file_utils::get_pathname(g_command_line_params.get_value_as_string_or_empty("vogl_tracefile").get_ptr());
4759
4760         if (path_to_check.is_empty())
4761             return false;
4762
4763         file_utils::combine_path(filename, path_to_check.get_ptr(), pBase_name);
4764         if (!file_utils::does_file_exist(filename.get_ptr()))
4765             return false;
4766     }
4767
4768     return true;
4769 }
4770
4771 //----------------------------------------------------------------------------------------------------------------------
4772 static void vogl_check_for_capture_stop_file()
4773 {
4774     if (!vogl_is_capturing())
4775         return;
4776
4777     dynamic_string stop_filename;
4778     if (!vogl_check_for_trigger_file(VOGL_STOP_CAPTURE_FILENAME, stop_filename))
4779         return;
4780
4781     file_utils::delete_file(stop_filename.get_ptr());
4782
4783     vogl_stop_capturing();
4784 }
4785
4786 //----------------------------------------------------------------------------------------------------------------------
4787 static void vogl_check_for_capture_trigger_file()
4788 {
4789     {
4790         scoped_mutex lock(g_vogl_trace_mutex);
4791         if ((g_vogl_frames_remaining_to_capture) || (g_vogl_trace_writer.is_opened()))
4792             return;
4793     }
4794
4795     dynamic_string trigger_filename;
4796     if (!vogl_check_for_trigger_file(VOGL_TRIGGER_CAPTURE_FILENAME, trigger_filename))
4797         return;
4798
4799     vogl_sleep(100);
4800
4801     // Lamely try to protected against racing - a better method is probably inotify: http://www.thegeekstuff.com/2010/04/inotify-c-program-example/
4802     for (;;)
4803     {
4804         uint64_t size;
4805         file_utils::get_file_size(trigger_filename.get_ptr(), size);
4806
4807         vogl_sleep(100);
4808
4809         uint64_t size1;
4810         file_utils::get_file_size(trigger_filename.get_ptr(), size1);
4811
4812         if (size == size1)
4813             break;
4814
4815         vogl_sleep(250);
4816     }
4817
4818     uint total_frames = 1;
4819     dynamic_string path;
4820     dynamic_string base_name;
4821
4822     dynamic_string_array lines;
4823     if (!file_utils::read_text_file(trigger_filename.get_ptr(), lines, file_utils::cRTFTrim | file_utils::cRTFIgnoreEmptyLines))
4824     {
4825         vogl_error_printf("%s: Failed reading from trigger file \"%s\", using default parameters to trigger a capture.\n", VOGL_FUNCTION_NAME, trigger_filename.get_ptr());
4826         lines.clear();
4827     }
4828
4829     if (lines.size() >= 1)
4830     {
4831         if (lines[0] == "all")
4832             total_frames = cUINT32_MAX;
4833         else
4834             total_frames = string_to_uint(lines[0].get_ptr(), 1);
4835
4836         if (lines.size() >= 2)
4837         {
4838             path = lines[1];
4839
4840             if (lines.size() >= 3)
4841                 base_name = lines[2];
4842         }
4843     }
4844
4845     file_utils::delete_file(trigger_filename.get_ptr());
4846
4847     bool success = vogl_capture_on_next_swap(total_frames, path.get_ptr(), base_name.get_ptr(), vogl_capture_status_callback_func, (void *)1);
4848
4849     if (!success)
4850         vogl_error_printf("%s: Failed enabling capture mode\n", VOGL_FUNCTION_NAME);
4851     else
4852         vogl_message_printf("%s: Successfully enabled capture mode, will capture up to %u frame(s), override path \"%s\", override base_name \"%s\"\n", VOGL_FUNCTION_NAME, total_frames, path.get_ptr(), base_name.get_ptr());
4853 }
4854
4855 //----------------------------------------------------------------------------------------------------------------------
4856 //  vogl_tick_capture
4857 //----------------------------------------------------------------------------------------------------------------------
4858 static void vogl_tick_capture(const Display *dpy, GLXDrawable drawable, vogl_context *pVOGL_context)
4859 {
4860     VOGL_FUNC_TRACER
4861
4862     // pVOGL_context may be NULL here!
4863
4864     vogl_check_for_capture_stop_file();
4865     vogl_check_for_capture_trigger_file();
4866
4867     scoped_mutex lock(g_vogl_trace_mutex);
4868
4869     if ((g_vogl_total_frames_to_capture) && (!g_vogl_frames_remaining_to_capture))
4870     {
4871         g_vogl_frames_remaining_to_capture = g_vogl_total_frames_to_capture;
4872         g_vogl_total_frames_to_capture = 0;
4873     }
4874
4875     if (g_vogl_stop_capturing)
4876     {
4877         g_vogl_stop_capturing = false;
4878
4879         if (g_vogl_trace_writer.is_opened())
4880         {
4881             VOGL_FUNC_TRACER
4882             vogl_end_capture();
4883             return;
4884         }
4885     }
4886
4887     if (!g_vogl_frames_remaining_to_capture)
4888         return;
4889
4890     if (!g_vogl_trace_writer.is_opened())
4891     {
4892         dynamic_string trace_path(g_command_line_params.get_value_as_string_or_empty("vogl_tracepath"));
4893         if (trace_path.is_empty())
4894             trace_path = "/tmp";
4895         if (!g_vogl_capture_path.is_empty())
4896             trace_path = g_vogl_capture_path;
4897
4898         time_t t = time(NULL);
4899         struct tm ltm = *localtime(&t);
4900
4901         dynamic_string trace_basename("capture");
4902         if (!g_vogl_capture_basename.is_empty())
4903             trace_basename = g_vogl_capture_basename;
4904
4905         dynamic_string filename(cVarArg, "%s_%04d_%02d_%02d_%02d_%02d_%02d.bin", trace_basename.get_ptr(), ltm.tm_year + 1900, ltm.tm_mon + 1, ltm.tm_mday, ltm.tm_hour, ltm.tm_min, ltm.tm_sec);
4906
4907         dynamic_string full_trace_filename;
4908         file_utils::combine_path(full_trace_filename, trace_path.get_ptr(), filename.get_ptr());
4909
4910         if (g_vogl_frames_remaining_to_capture == cUINT32_MAX)
4911             vogl_message_printf("%s: Initiating capture of all remaining frames to file \"%s\"\n", VOGL_FUNCTION_NAME, full_trace_filename.get_ptr());
4912         else
4913             vogl_message_printf("%s: Initiating capture of up to %u frame(s) to file \"%s\"\n", VOGL_FUNCTION_NAME, g_vogl_frames_remaining_to_capture, full_trace_filename.get_ptr());
4914
4915         if (!vogl_write_snapshot_to_trace(full_trace_filename.get_ptr(), dpy, drawable, pVOGL_context))
4916         {
4917             file_utils::delete_file(full_trace_filename.get_ptr());
4918             vogl_error_printf("%s: Failed creating GL state snapshot, closing and deleting trace file\n", VOGL_FUNCTION_NAME);
4919         }
4920     }
4921     else
4922     {
4923         if (g_vogl_frames_remaining_to_capture != cUINT32_MAX)
4924             g_vogl_frames_remaining_to_capture--;
4925
4926         // See if we should stop capturing.
4927         if (!g_vogl_frames_remaining_to_capture)
4928         {
4929             VOGL_FUNC_TRACER
4930             vogl_end_capture();
4931         }
4932     }
4933 }
4934
4935 //----------------------------------------------------------------------------------------------------------------------
4936 //  vogl_glXSwapBuffersGLFuncProlog
4937 //----------------------------------------------------------------------------------------------------------------------
4938 static inline void vogl_glXSwapBuffersGLFuncProlog(const Display *dpy, GLXDrawable drawable, vogl_context *pVOGL_context, vogl_entrypoint_serializer &trace_serializer)
4939 {
4940     // pVOGL_context may be NULL here!
4941
4942     if (!GL_ENTRYPOINT(glXGetCurrentContext) || !GL_ENTRYPOINT(glXGetCurrentDisplay) ||
4943         !GL_ENTRYPOINT(glXGetCurrentDrawable) || !GL_ENTRYPOINT(glXGetCurrentReadDrawable))
4944     {
4945         return;
4946     }
4947
4948     bool override_current_context = false;
4949
4950     if ((!pVOGL_context) || ((pVOGL_context->get_display() != dpy) || (pVOGL_context->get_drawable() != drawable)))
4951     {
4952         // NVidia/AMD closed source don't seem to care if a context is current when glXSwapBuffer()'s is called. But Mesa doesn't like it.
4953         vogl_warning_printf_once("%s: No context is current, or the current context's display/drawable don't match the provided display/drawable. Will try to find the first context which matches the provided params, but this may not work reliably.\n", VOGL_FUNCTION_NAME);
4954
4955         pVOGL_context = g_context_manager.find_context(dpy, drawable);
4956
4957         if (!pVOGL_context)
4958         {
4959             vogl_error_printf("%s: Unable to determine which GL context is associated with the indicated drawable/display! Will not be able to take a screen capture, or record context related information to the trace!\n", VOGL_FUNCTION_NAME);
4960             return;
4961         }
4962         else if (pVOGL_context->get_current_thread() != 0)
4963         {
4964             vogl_error_printf("%s: The GL context which matches the provided display/drawable is already current on another thread!\n", VOGL_FUNCTION_NAME);
4965             return;
4966         }
4967
4968         override_current_context = true;
4969     }
4970
4971     GLXContext orig_context = 0;
4972     const Display *orig_dpy = NULL;
4973     GLXDrawable orig_drawable = 0;
4974     GLXDrawable orig_read_drawable = 0;
4975     if (override_current_context)
4976     {
4977         orig_context = GL_ENTRYPOINT(glXGetCurrentContext)();
4978         orig_dpy = GL_ENTRYPOINT(glXGetCurrentDisplay)();
4979         orig_drawable = GL_ENTRYPOINT(glXGetCurrentDrawable)();
4980         orig_read_drawable = GL_ENTRYPOINT(glXGetCurrentReadDrawable)();
4981
4982         if (!orig_dpy)
4983             orig_dpy = dpy;
4984
4985         GL_ENTRYPOINT(glXMakeCurrent)(pVOGL_context->get_display(), pVOGL_context->get_drawable(), pVOGL_context->get_context_handle());
4986     }
4987
4988     Window root = 0;
4989     int x = 0, y = 0;
4990     unsigned int width = 0, height = 0, border_width = 0, depth = 0;
4991     if ((dpy) && (XGetGeometry(const_cast<Display *>(dpy), drawable, &root, &x, &y, &width, &height, &border_width, &depth) != False))
4992     {
4993         pVOGL_context->set_window_dimensions(width, height);
4994
4995         vogl_tick_screen_capture(pVOGL_context);
4996     }
4997     else
4998     {
4999         console::warning("%s: XGetGeometry() call failed!\n", VOGL_FUNCTION_NAME);
5000     }
5001
5002     if (trace_serializer.is_in_begin())
5003     {
5004         trace_serializer.add_key_value(string_hash("win_width"), pVOGL_context->get_window_width());
5005         trace_serializer.add_key_value(string_hash("win_height"), pVOGL_context->get_window_height());
5006     }
5007
5008     if (g_dump_gl_calls_flag)
5009     {
5010         vogl_log_printf("** Current window dimensions: %ix%i\n", pVOGL_context->get_window_width(), pVOGL_context->get_window_height());
5011     }
5012
5013     pVOGL_context->inc_frame_index();
5014
5015     if ((override_current_context) && (orig_dpy))
5016     {
5017         GL_ENTRYPOINT(glXMakeContextCurrent)(orig_dpy, orig_drawable, orig_read_drawable, orig_context);
5018     }
5019 }
5020
5021 //----------------------------------------------------------------------------------------------------------------------
5022 #define DEF_FUNCTION_CUSTOM_HANDLER_glXSwapBuffers(exported, category, ret, ret_type_enum, num_params, name, args, params)
5023 static void vogl_glXSwapBuffers(const Display *dpy, GLXDrawable drawable)
5024 {
5025     uint64_t begin_rdtsc = utils::RDTSC();
5026
5027     if (g_dump_gl_calls_flag)
5028     {
5029         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
5030     }
5031
5032     vogl_thread_local_data *pTLS_data = vogl_entrypoint_prolog(VOGL_ENTRYPOINT_glXSwapBuffers);
5033     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)
5034     {
5035         vogl_warning_printf("%s: GL call detected while libvogltrace was itself making a GL call to func %s! This call will not be traced.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName);
5036         return GL_ENTRYPOINT(glXSwapBuffers)(dpy, drawable);
5037     }
5038
5039     // Use a local serializer because the call to glXSwapBuffer()'s will make GL calls if something like the Steam Overlay is active.
5040     vogl_entrypoint_serializer serializer;
5041
5042     if (g_vogl_trace_writer.is_opened())
5043     {
5044         serializer.begin(VOGL_ENTRYPOINT_glXSwapBuffers, g_context_manager.get_current());
5045         serializer.add_param(0, VOGL_CONST_DISPLAY_PTR, &dpy, sizeof(dpy));
5046         serializer.add_param(1, VOGL_GLXDRAWABLE, &drawable, sizeof(drawable));
5047     }
5048
5049     vogl_glXSwapBuffersGLFuncProlog(dpy, drawable, pTLS_data->m_pContext, serializer);
5050
5051     uint64_t gl_begin_rdtsc = utils::RDTSC();
5052
5053     // Call the driver directly, bypassing our GL/GLX wrappers which set m_calling_driver_entrypoint_id. The Steam Overlay may call us back!
5054     DIRECT_GL_ENTRYPOINT(glXSwapBuffers)(dpy, drawable);
5055
5056     uint64_t gl_end_rdtsc = utils::RDTSC();
5057
5058     if (g_vogl_trace_writer.is_opened())
5059     {
5060         serializer.set_begin_rdtsc(begin_rdtsc);
5061         serializer.set_gl_begin_end_rdtsc(gl_begin_rdtsc, gl_end_rdtsc);
5062         serializer.end();
5063         vogl_write_packet_to_trace(serializer.get_packet());
5064     }
5065
5066     vogl_tick_capture(dpy, drawable, pTLS_data->m_pContext);
5067
5068     if (g_dump_gl_calls_flag)
5069     {
5070         vogl_log_printf("** glXSwapBuffers TID: 0x%" PRIX64 " Display: 0x%" PRIX64 " drawable: 0x%" PRIX64 "\n", vogl_get_current_kernel_thread_id(), cast_val_to_uint64(dpy), cast_val_to_uint64(drawable));
5071     }
5072
5073     if (g_dump_gl_calls_flag)
5074     {
5075         vogl_message_printf("** END %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
5076     }
5077
5078     if (g_command_line_params.has_key("vogl_exit_after_x_frames") && (pTLS_data->m_pContext))
5079     {
5080         uint64_t max_num_frames = g_command_line_params.get_value_as_uint64("vogl_exit_after_x_frames");
5081         uint64_t cur_num_frames = pTLS_data->m_pContext->get_frame_index();
5082
5083         if (cur_num_frames >= max_num_frames)
5084         {
5085             vogl_message_printf("Number of frames specified by --vogl_exit_after_x_frames param reached, forcing trace to close and calling exit()\n");
5086
5087             vogl_end_capture();
5088
5089             // Exit the app
5090             exit(0);
5091         }
5092     }
5093 }
5094
5095 //----------------------------------------------------------------------------------------------------------------------
5096 #define DEF_FUNCTION_CUSTOM_HANDLER_glXCreateContextAttribsARB(exported, category, ret, ret_type_enum, num_params, name, args, params)
5097 static GLXContext vogl_glXCreateContextAttribsARB(const Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list)
5098 {
5099     uint64_t begin_rdtsc = utils::RDTSC();
5100
5101     if (g_dump_gl_calls_flag)
5102     {
5103         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
5104     }
5105
5106     vogl_thread_local_data *pTLS_data = vogl_entrypoint_prolog(VOGL_ENTRYPOINT_glXCreateContextAttribsARB);
5107     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)
5108     {
5109         vogl_warning_printf("%s: GL call detected while libvogltrace was itself making a GL call to func %s! This call will not be traced.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName);
5110         return GL_ENTRYPOINT(glXCreateContextAttribsARB)(dpy, config, share_context, direct, attrib_list);
5111     }
5112
5113     vogl_context_attribs context_attribs;
5114
5115     if (g_command_line_params.get_value_as_bool("vogl_force_debug_context"))
5116     {
5117         vogl_warning_printf("%s: Forcing debug context\n", VOGL_FUNCTION_NAME);
5118
5119         context_attribs.init(attrib_list);
5120         if (!context_attribs.has_key(GLX_CONTEXT_FLAGS_ARB))
5121             context_attribs.add_key(GLX_CONTEXT_FLAGS_ARB, 0);
5122
5123         int context_flags_value_ofs = context_attribs.find_value_ofs(GLX_CONTEXT_FLAGS_ARB);
5124         VOGL_ASSERT(context_flags_value_ofs >= 0);
5125         if (context_flags_value_ofs >= 0)
5126             context_attribs[context_flags_value_ofs] |= GLX_CONTEXT_DEBUG_BIT_ARB;
5127
5128         int context_major_version_ofs = context_attribs.find_value_ofs(GLX_CONTEXT_MAJOR_VERSION_ARB);
5129         int context_minor_version_ofs = context_attribs.find_value_ofs(GLX_CONTEXT_MINOR_VERSION_ARB);
5130
5131         if (context_major_version_ofs < 0)
5132         {
5133             // Don't slam up if they haven't requested a specific GL version, the driver will give us the most recent version that is backwards compatible with 1.0 (i.e. 4.3 for NVidia's current dirver).
5134         }
5135         else if (context_attribs[context_major_version_ofs] < 3)
5136         {
5137             context_attribs[context_major_version_ofs] = 3;
5138
5139             if (context_minor_version_ofs < 0)
5140                 context_attribs.add_key(GLX_CONTEXT_MINOR_VERSION_ARB, 0);
5141             else
5142                 context_attribs[context_minor_version_ofs] = 0;
5143
5144             vogl_warning_printf("%s: Forcing GL context version up to v3.0 due to debug context usage\n", VOGL_FUNCTION_NAME);
5145         }
5146
5147         attrib_list = context_attribs.get_vec().get_ptr();
5148     }
5149
5150     uint64_t gl_begin_rdtsc = utils::RDTSC();
5151     GLXContext result = GL_ENTRYPOINT(glXCreateContextAttribsARB)(dpy, config, share_context, direct, attrib_list);
5152     uint64_t gl_end_rdtsc = utils::RDTSC();
5153
5154     if (g_dump_gl_calls_flag)
5155     {
5156         vogl_log_printf("** glXCreateContextAttribsARB TID: 0x%" PRIX64 " Display: 0x%" PRIX64 " config: 0x%" PRIX64 " share_context: 0x%" PRIX64 " direct %i attrib_list: 0x%" PRIX64 ", result: 0x%" PRIX64 "\n", vogl_get_current_kernel_thread_id(), cast_val_to_uint64(dpy), cast_val_to_uint64(config), cast_val_to_uint64(share_context), (int)direct, cast_val_to_uint64(attrib_list), cast_val_to_uint64(result));
5157     }
5158
5159     if (g_vogl_trace_writer.is_opened())
5160     {
5161         vogl_entrypoint_serializer serializer(VOGL_ENTRYPOINT_glXCreateContextAttribsARB, g_context_manager.get_current());
5162         serializer.set_begin_rdtsc(begin_rdtsc);
5163         serializer.set_gl_begin_end_rdtsc(gl_begin_rdtsc, gl_end_rdtsc);
5164         serializer.add_param(0, VOGL_CONST_DISPLAY_PTR, &dpy, sizeof(dpy));
5165         serializer.add_param(1, VOGL_GLXFBCONFIG, &config, sizeof(config));
5166         serializer.add_param(2, VOGL_GLXCONTEXT, &share_context, sizeof(share_context));
5167         serializer.add_param(3, VOGL_BOOL, &direct, sizeof(direct));
5168         serializer.add_param(4, VOGL_CONST_INT_PTR, &attrib_list, sizeof(attrib_list));
5169         if (attrib_list)
5170         {
5171             uint n = vogl_determine_attrib_list_array_size(attrib_list);
5172             serializer.add_array_client_memory(4, VOGL_INT, n, attrib_list, sizeof(int) * n);
5173         }
5174         serializer.add_return_param(VOGL_GLXCONTEXT, &result, sizeof(result));
5175         serializer.end();
5176         vogl_write_packet_to_trace(serializer.get_packet());
5177     }
5178
5179     if (result)
5180     {
5181         if (share_context)
5182         {
5183             if (!g_app_uses_sharelists)
5184                 vogl_message_printf("%s: sharelist usage detected\n", VOGL_FUNCTION_NAME);
5185
5186             g_app_uses_sharelists = true;
5187         }
5188
5189         g_context_manager.lock();
5190
5191         vogl_context *pVOGL_context = g_context_manager.create_context(result);
5192         pVOGL_context->set_display(dpy);
5193         pVOGL_context->set_fb_config(config);
5194         pVOGL_context->set_sharelist_handle(share_context);
5195         pVOGL_context->set_direct(direct);
5196         pVOGL_context->set_attrib_list(attrib_list);
5197         pVOGL_context->set_created_from_attribs(true);
5198         pVOGL_context->set_creation_func(VOGL_ENTRYPOINT_glXCreateContextAttribsARB);
5199
5200         if (share_context)
5201         {
5202             vogl_context *pShare_context = g_context_manager.lookup_vogl_context(share_context);
5203
5204             if (!pShare_context)
5205                 vogl_error_printf("%s: Failed finding share context 0x%" PRIx64 " in context manager's hashmap! This handle is probably invalid.\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(share_context));
5206             else
5207             {
5208                 while (!pShare_context->is_root_context())
5209                     pShare_context = pShare_context->get_shared_state();
5210
5211                 pVOGL_context->set_shared_context(pShare_context);
5212
5213                 pShare_context->add_ref();
5214             }
5215         }
5216
5217         pVOGL_context->init();
5218
5219         g_context_manager.unlock();
5220     }
5221
5222     if (g_dump_gl_calls_flag)
5223     {
5224         vogl_message_printf("** END %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
5225     }
5226
5227     return result;
5228 }
5229
5230 //----------------------------------------------------------------------------------------------------------------------
5231 // vogl_get_fb_config_from_xvisual_info
5232 // Attempts to find the GLXFBConfig corresponding to a particular visual.
5233 // TODO: Test this more!
5234 //----------------------------------------------------------------------------------------------------------------------
5235 static GLXFBConfig *vogl_get_fb_config_from_xvisual_info(const Display *dpy, const XVisualInfo *vis)
5236 {
5237     vogl_context_attribs attribs;
5238
5239 #define GET_CONFIG(attrib)                                   \
5240     do                                                       \
5241     {                                                        \
5242         int val = 0;                                         \
5243         GL_ENTRYPOINT(glXGetConfig)(dpy, vis, attrib, &val); \
5244         if (val)                                             \
5245             attribs.add_key(attrib, val);                    \
5246     } while (0)
5247
5248     int is_rgba = 0;
5249     GL_ENTRYPOINT(glXGetConfig)(dpy, vis, GLX_RGBA, &is_rgba);
5250
5251     attribs.add_key(GLX_RENDER_TYPE, is_rgba ? GLX_RGBA_BIT : GLX_COLOR_INDEX_BIT);
5252     attribs.add_key(GLX_X_RENDERABLE, True);
5253     attribs.add_key(GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT);
5254
5255     if (!is_rgba)
5256         GET_CONFIG(GLX_BUFFER_SIZE);
5257
5258     GET_CONFIG(GLX_LEVEL);
5259
5260     GET_CONFIG(GLX_DOUBLEBUFFER);
5261     GET_CONFIG(GLX_STEREO);
5262     GET_CONFIG(GLX_AUX_BUFFERS);
5263     if (is_rgba)
5264     {
5265         GET_CONFIG(GLX_RED_SIZE);
5266         GET_CONFIG(GLX_GREEN_SIZE);
5267         GET_CONFIG(GLX_BLUE_SIZE);
5268         GET_CONFIG(GLX_ALPHA_SIZE);
5269     }
5270     GET_CONFIG(GLX_DEPTH_SIZE);
5271     GET_CONFIG(GLX_STENCIL_SIZE);
5272
5273     GET_CONFIG(GLX_TRANSPARENT_INDEX_VALUE);
5274     GET_CONFIG(GLX_TRANSPARENT_RED_VALUE);
5275     GET_CONFIG(GLX_TRANSPARENT_GREEN_VALUE);
5276     GET_CONFIG(GLX_TRANSPARENT_BLUE_VALUE);
5277     GET_CONFIG(GLX_TRANSPARENT_ALPHA_VALUE);
5278
5279     if (attribs.get_value_or_default(GLX_TRANSPARENT_INDEX_VALUE) || attribs.get_value_or_default(GLX_TRANSPARENT_RED_VALUE) ||
5280         attribs.get_value_or_default(GLX_TRANSPARENT_GREEN_VALUE) || attribs.get_value_or_default(GLX_TRANSPARENT_BLUE_VALUE) ||
5281         attribs.get_value_or_default(GLX_TRANSPARENT_ALPHA_VALUE))
5282     {
5283         GET_CONFIG(GLX_TRANSPARENT_TYPE);
5284     }
5285 #undef GET_CONFIG
5286
5287 #if 0
5288         for (uint i = 0; i < attribs.size(); i += 2)
5289         {
5290                 if (!attribs[i])
5291                         break;
5292                 printf("%s 0x%x\n", g_gl_enums.find_glx_name(attribs[i]), attribs[i + 1]);
5293         }
5294 #endif
5295
5296     int num_configs = 0;
5297     GLXFBConfig *pConfigs = GL_ENTRYPOINT(glXChooseFBConfig)(dpy, vis->screen, attribs.get_ptr(), &num_configs);
5298     return num_configs ? pConfigs : NULL;
5299 }
5300
5301 //----------------------------------------------------------------------------------------------------------------------
5302 #define DEF_FUNCTION_CUSTOM_HANDLER_glXCreateContext(exported, category, ret, ret_type_enum, num_params, name, args, params)
5303 static GLXContext vogl_glXCreateContext(const Display *dpy, const XVisualInfo *vis, GLXContext shareList, Bool direct)
5304 {
5305     uint64_t begin_rdtsc = utils::RDTSC();
5306
5307     if (g_dump_gl_calls_flag)
5308     {
5309         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
5310     }
5311
5312     vogl_thread_local_data *pTLS_data = vogl_entrypoint_prolog(VOGL_ENTRYPOINT_glXCreateContext);
5313     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)
5314     {
5315         vogl_warning_printf("%s: GL call detected while libvogltrace was itself making a GL call to func %s! This call will not be traced.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName);
5316         return GL_ENTRYPOINT(glXCreateContext)(dpy, vis, shareList, direct);
5317     }
5318
5319     if (g_command_line_params.get_value_as_bool("vogl_force_debug_context"))
5320     {
5321         vogl_warning_printf("%s: Can't enable debug contexts via glXCreateContext(), forcing call to use glXCreateContextsAttribsARB() instead\n", VOGL_FUNCTION_NAME);
5322
5323         GLXFBConfig *pConfig = vogl_get_fb_config_from_xvisual_info(dpy, vis);
5324         if (!pConfig)
5325         {
5326             vogl_error_printf("%s: Can't enable debug contexts: Unable to find the FB config that matches the passed in XVisualInfo!\n", VOGL_FUNCTION_NAME);
5327         }
5328         else
5329         {
5330             int empty_attrib_list[1] = { 0 };
5331             return vogl_glXCreateContextAttribsARB(dpy, pConfig[0], shareList, direct, empty_attrib_list);
5332         }
5333     }
5334
5335     uint64_t gl_begin_rdtsc = utils::RDTSC();
5336     GLXContext result = GL_ENTRYPOINT(glXCreateContext)(dpy, vis, shareList, direct);
5337     uint64_t gl_end_rdtsc = utils::RDTSC();
5338
5339     if (g_dump_gl_calls_flag)
5340     {
5341         vogl_log_printf("** glXCreateContext TID: 0x%" PRIX64 " dpy: 0x%" PRIX64 " vis: 0x%" PRIX64 " shareList: 0x%" PRIX64 " direct %i, result: 0x%" PRIX64 "\n", vogl_get_current_kernel_thread_id(), cast_val_to_uint64(dpy), cast_val_to_uint64(vis), cast_val_to_uint64(shareList), (int)direct, cast_val_to_uint64(result));
5342     }
5343
5344     if (g_vogl_trace_writer.is_opened())
5345     {
5346         vogl_entrypoint_serializer serializer(VOGL_ENTRYPOINT_glXCreateContext, g_context_manager.get_current());
5347         serializer.set_begin_rdtsc(begin_rdtsc);
5348         serializer.set_gl_begin_end_rdtsc(gl_begin_rdtsc, gl_end_rdtsc);
5349         serializer.add_param(0, VOGL_CONST_DISPLAY_PTR, &dpy, sizeof(dpy));
5350         serializer.add_param(1, VOGL_CONST_XVISUALINFO_PTR, &vis, sizeof(vis));
5351         serializer.add_param(2, VOGL_GLXCONTEXT, &shareList, sizeof(shareList));
5352         serializer.add_param(3, VOGL_BOOL, &direct, sizeof(direct));
5353         serializer.add_return_param(VOGL_GLXCONTEXT, &result, sizeof(result));
5354         if (vis)
5355             serializer.add_ref_client_memory(1, VOGL_XVISUALINFO, vis, sizeof(XVisualInfo));
5356         serializer.end();
5357         vogl_write_packet_to_trace(serializer.get_packet());
5358     }
5359
5360     if (result)
5361     {
5362         if (shareList)
5363         {
5364             if (!g_app_uses_sharelists)
5365                 vogl_message_printf("%s: sharelist usage detected\n", VOGL_FUNCTION_NAME);
5366
5367             g_app_uses_sharelists = true;
5368         }
5369
5370         g_context_manager.lock();
5371
5372         vogl_context *pVOGL_context = g_context_manager.create_context(result);
5373         pVOGL_context->set_display(dpy);
5374         pVOGL_context->set_xvisual_info(vis);
5375         pVOGL_context->set_sharelist_handle(shareList);
5376         pVOGL_context->set_direct(direct);
5377         pVOGL_context->set_creation_func(VOGL_ENTRYPOINT_glXCreateContext);
5378
5379         if (shareList)
5380         {
5381             vogl_context *pShare_context = g_context_manager.lookup_vogl_context(shareList);
5382
5383             if (!pShare_context)
5384                 vogl_error_printf("%s: Failed finding share context 0x%" PRIx64 " in context manager's hashmap! This handle is probably invalid.\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(shareList));
5385             else
5386             {
5387                 while (!pShare_context->is_root_context())
5388                     pShare_context = pShare_context->get_shared_state();
5389
5390                 pVOGL_context->set_shared_context(pShare_context);
5391
5392                 pShare_context->add_ref();
5393             }
5394         }
5395
5396         pVOGL_context->init();
5397
5398         g_context_manager.unlock();
5399     }
5400
5401     if (g_dump_gl_calls_flag)
5402     {
5403         vogl_message_printf("** END %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
5404     }
5405
5406     return result;
5407 }
5408
5409 //----------------------------------------------------------------------------------------------------------------------
5410 #define DEF_FUNCTION_CUSTOM_HANDLER_glXCreateNewContext(exported, category, ret, ret_type_enum, num_params, name, args, params)
5411 static GLXContext vogl_glXCreateNewContext(const Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct)
5412 {
5413     if (render_type != GLX_RGBA_TYPE)
5414     {
5415         vogl_error_printf("%s: Unsupported render type (%s)!\n", VOGL_FUNCTION_NAME, g_gl_enums.find_glx_name(render_type));
5416     }
5417
5418     if (g_command_line_params.get_value_as_bool("vogl_force_debug_context"))
5419     {
5420         vogl_warning_printf("%s: Redirecting call from glxCreateNewContext() to glxCreateContextAttribsARB because --vogl_force_debug_context was specified. Note this may fail if glXCreateWindow() was called.\n", VOGL_FUNCTION_NAME);
5421
5422         return vogl_glXCreateContextAttribsARB(dpy, config, share_list, direct, NULL);
5423     }
5424
5425     uint64_t begin_rdtsc = utils::RDTSC();
5426
5427     if (g_dump_gl_calls_flag)
5428     {
5429         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
5430     }
5431
5432     vogl_thread_local_data *pTLS_data = vogl_entrypoint_prolog(VOGL_ENTRYPOINT_glXCreateNewContext);
5433     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)
5434     {
5435         vogl_warning_printf("%s: GL call detected while libvogltrace was itself making a GL call to func %s! This call will not be traced.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName);
5436         return GL_ENTRYPOINT(glXCreateNewContext)(dpy, config, render_type, share_list, direct);
5437     }
5438
5439     uint64_t gl_begin_rdtsc = utils::RDTSC();
5440     GLXContext result = GL_ENTRYPOINT(glXCreateNewContext)(dpy, config, render_type, share_list, direct);
5441     uint64_t gl_end_rdtsc = utils::RDTSC();
5442
5443     if (g_dump_gl_calls_flag)
5444     {
5445         vogl_log_printf("** glXCreateNewContext TID: 0x%" PRIX64 " dpy: 0x%" PRIX64 " config: 0x%" PRIX64 " render_type: %i shareList: 0x%" PRIX64 " direct %i, result: 0x%" PRIX64 "\n",
5446                        vogl_get_current_kernel_thread_id(), cast_val_to_uint64(dpy), cast_val_to_uint64(config), render_type, cast_val_to_uint64(share_list), static_cast<int>(direct), cast_val_to_uint64(result));
5447     }
5448
5449     if (g_vogl_trace_writer.is_opened())
5450     {
5451         vogl_entrypoint_serializer serializer(VOGL_ENTRYPOINT_glXCreateNewContext, g_context_manager.get_current());
5452         serializer.set_begin_rdtsc(begin_rdtsc);
5453         serializer.set_gl_begin_end_rdtsc(gl_begin_rdtsc, gl_end_rdtsc);
5454         serializer.add_param(0, VOGL_CONST_DISPLAY_PTR, &dpy, sizeof(dpy));
5455         serializer.add_param(1, VOGL_GLXFBCONFIG, &config, sizeof(config));
5456         serializer.add_param(2, VOGL_INT, &render_type, sizeof(render_type));
5457         serializer.add_param(3, VOGL_GLXCONTEXT, &share_list, sizeof(share_list));
5458         serializer.add_param(4, VOGL_BOOL, &direct, sizeof(direct));
5459         serializer.add_return_param(VOGL_GLXCONTEXT, &result, sizeof(result));
5460         serializer.end();
5461         vogl_write_packet_to_trace(serializer.get_packet());
5462     }
5463
5464     if (result)
5465     {
5466         if (share_list)
5467         {
5468             if (!g_app_uses_sharelists)
5469                 vogl_message_printf("%s: sharelist usage detected\n", VOGL_FUNCTION_NAME);
5470
5471             g_app_uses_sharelists = true;
5472         }
5473
5474         g_context_manager.lock();
5475
5476         vogl_context *pVOGL_context = g_context_manager.create_context(result);
5477         pVOGL_context->set_display(dpy);
5478         pVOGL_context->set_fb_config(config);
5479         pVOGL_context->set_sharelist_handle(share_list);
5480         pVOGL_context->set_direct(direct);
5481         pVOGL_context->set_creation_func(VOGL_ENTRYPOINT_glXCreateNewContext);
5482
5483         if (share_list)
5484         {
5485             vogl_context *pShare_context = g_context_manager.lookup_vogl_context(share_list);
5486
5487             if (!pShare_context)
5488                 vogl_error_printf("%s: Failed finding share context 0x%" PRIx64 " in context manager's hashmap! This handle is probably invalid.\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(share_list));
5489             else
5490             {
5491                 while (!pShare_context->is_root_context())
5492                     pShare_context = pShare_context->get_shared_state();
5493
5494                 pVOGL_context->set_shared_context(pShare_context);
5495
5496                 pShare_context->add_ref();
5497             }
5498         }
5499
5500         pVOGL_context->init();
5501
5502         g_context_manager.unlock();
5503     }
5504
5505     if (g_dump_gl_calls_flag)
5506     {
5507         vogl_message_printf("** END %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
5508     }
5509
5510     return result;
5511 }
5512
5513 //----------------------------------------------------------------------------------------------------------------------
5514 #define DEF_FUNCTION_CUSTOM_HANDLER_glXDestroyContext(exported, category, ret, ret_type_enum, num_params, name, args, params)
5515 static void vogl_glXDestroyContext(const Display *dpy, GLXContext context)
5516 {
5517     uint64_t begin_rdtsc = utils::RDTSC();
5518
5519     if (g_dump_gl_calls_flag)
5520     {
5521         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
5522     }
5523
5524     vogl_thread_local_data *pTLS_data = vogl_entrypoint_prolog(VOGL_ENTRYPOINT_glXDestroyContext);
5525     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)
5526     {
5527         vogl_warning_printf("%s: GL call detected while libvogltrace was itself making a GL call to func %s! This call will not be traced.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName);
5528         return GL_ENTRYPOINT(glXDestroyContext)(dpy, context);
5529     }
5530
5531     if (g_dump_gl_calls_flag)
5532     {
5533         vogl_log_printf("** glXDestroyContext TID: 0x%" PRIX64 " Display: 0x%" PRIX64 " context: 0x%" PRIX64 "\n", vogl_get_current_kernel_thread_id(), cast_val_to_uint64(dpy), cast_val_to_uint64(context));
5534     }
5535
5536     vogl_context *pContext = context ? g_context_manager.lookup_vogl_context(context) : NULL;
5537     if (!pContext)
5538         vogl_error_printf("%s: glXDestroyContext() called on an unknown context handle 0x%" PRIX64 "!\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(context));
5539     else if (pContext->get_current_thread())
5540         vogl_error_printf("%s: glXDestroyContext() called on a handle 0x%" PRIX64 " that is still current on thread 0x%" PRIX64 "! This may cause the asynchronous framebuffer capturing system to miss frames!\n", VOGL_FUNCTION_NAME, cast_val_to_uint64(context), pContext->get_current_thread());
5541
5542     if (pContext)
5543     {
5544         pContext->on_destroy_prolog();
5545     }
5546
5547     uint64_t gl_begin_rdtsc = utils::RDTSC();
5548     GL_ENTRYPOINT(glXDestroyContext)(dpy, context);
5549     uint64_t gl_end_rdtsc = utils::RDTSC();
5550
5551     if (g_vogl_trace_writer.is_opened())
5552     {
5553         vogl_entrypoint_serializer serializer(VOGL_ENTRYPOINT_glXDestroyContext, g_context_manager.get_current());
5554         serializer.set_begin_rdtsc(begin_rdtsc);
5555         serializer.set_gl_begin_end_rdtsc(gl_begin_rdtsc, gl_end_rdtsc);
5556         serializer.add_param(0, VOGL_CONST_DISPLAY_PTR, &dpy, sizeof(dpy));
5557         serializer.add_param(1, VOGL_GLXCONTEXT, &context, sizeof(context));
5558         serializer.end();
5559         vogl_write_packet_to_trace(serializer.get_packet());
5560         g_vogl_trace_writer.flush();
5561     }
5562
5563     if (pContext)
5564     {
5565         g_context_manager.lock();
5566
5567         VOGL_ASSERT(!pContext->get_deleted_flag());
5568         if (!pContext->get_deleted_flag())
5569         {
5570             pContext->set_deleted_flag(true);
5571
5572             if (pContext->is_share_context())
5573             {
5574                 VOGL_ASSERT(pContext->get_ref_count() == 1);
5575             }
5576
5577             int new_ref_count = pContext->del_ref();
5578             if (new_ref_count <= 0)
5579             {
5580                 if (pContext->is_share_context())
5581                 {
5582                     vogl_context *pRoot_context = pContext->get_shared_state();
5583
5584                     pContext->set_shared_context(NULL);
5585
5586                     if (pRoot_context->del_ref() == 0)
5587                     {
5588                         VOGL_ASSERT(pRoot_context->get_deleted_flag());
5589
5590                         bool status = g_context_manager.destroy_context(pRoot_context->get_context_handle());
5591                         VOGL_ASSERT(status);
5592                         VOGL_NOTE_UNUSED(status);
5593                     }
5594                 }
5595
5596                 bool status = g_context_manager.destroy_context(context);
5597                 VOGL_ASSERT(status);
5598                 VOGL_NOTE_UNUSED(status);
5599             }
5600         }
5601
5602         g_context_manager.unlock();
5603     }
5604
5605     if (g_vogl_pLog_stream)
5606     {
5607         // TODO: Ensure this is actually thread safe (does the gcc C runtime mutex this?)
5608         g_vogl_pLog_stream->flush();
5609     }
5610
5611     if (g_dump_gl_calls_flag)
5612     {
5613         vogl_message_printf("** END %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
5614     }
5615 }
5616
5617 //----------------------------------------------------------------------------------------------------------------------
5618 #define DEF_FUNCTION_CUSTOM_HANDLER_glGetError(exported, category, ret, ret_type_enum, num_params, name, args, params)
5619 static GLenum vogl_glGetError()
5620 {
5621     uint64_t begin_rdtsc = utils::RDTSC();
5622
5623     if (g_dump_gl_calls_flag)
5624     {
5625         vogl_message_printf("** BEGIN %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
5626     }
5627
5628     vogl_thread_local_data *pTLS_data = vogl_entrypoint_prolog(VOGL_ENTRYPOINT_glGetError);
5629     if (pTLS_data->m_calling_driver_entrypoint_id != VOGL_ENTRYPOINT_INVALID)
5630     {
5631         vogl_warning_printf("%s: GL call detected while libvogltrace was itself making a GL call to func %s! This call will not be traced.\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[pTLS_data->m_calling_driver_entrypoint_id].m_pName);
5632         return GL_ENTRYPOINT(glGetError)();
5633     }
5634
5635     if (g_dump_gl_calls_flag)
5636     {
5637         vogl_log_printf("** glGetError TID: 0x%" PRIX64 "\n", vogl_get_current_kernel_thread_id());
5638     }
5639
5640     vogl_context *pContext = pTLS_data->m_pContext;
5641     // pContext may be NULL here if the user has called glGetError() incorrectly, we've already printed the error message, but we're still going to call the driver (which will probably just immediately return).
5642     // Also, even if we have a latched error for this context, we MUST call glGetError() to guarantee there are no current errors on the context (to preserve the original behavior if the tracer wasn't present).
5643
5644     uint64_t gl_begin_rdtsc = utils::RDTSC();
5645     GLenum gl_err = GL_ENTRYPOINT(glGetError)();
5646     uint64_t gl_end_rdtsc = utils::RDTSC();
5647
5648     if (g_vogl_trace_writer.is_opened())
5649     {
5650         vogl_entrypoint_serializer serializer(VOGL_ENTRYPOINT_glGetError, g_context_manager.get_current());
5651         serializer.set_begin_rdtsc(begin_rdtsc);
5652         serializer.set_gl_begin_end_rdtsc(gl_begin_rdtsc, gl_end_rdtsc);
5653         serializer.add_return_param(VOGL_GLENUM, &gl_err, sizeof(gl_err));
5654
5655         if ((pContext) && (pContext->has_latched_gl_error()))
5656         {
5657             // Record the latched error too, so the replayer knows what's going on.
5658             serializer.add_key_value("latched_gl_error", pContext->get_latched_gl_error());
5659         }
5660
5661         serializer.end();
5662         vogl_write_packet_to_trace(serializer.get_packet());
5663         g_vogl_trace_writer.flush();
5664     }
5665
5666     // See if our context's shadow has a latched GL error recorded by the tracer in an earlier call. If so, it must override this GL error.
5667     if (pContext && pContext->has_latched_gl_error())
5668     {
5669         if (gl_err != GL_NO_ERROR)
5670         {
5671             vogl_warning_printf("%s: glGetError() called with a GL error latched on the context by the tracer. Current error is 0x%04X, latched error is 0x%04X. Note the queued error will suppress the current error.\n", VOGL_FUNCTION_NAME, gl_err, pContext->get_latched_gl_error());
5672         }
5673
5674         // We're going to replace the current GL error with our previously latched error, which *would have* masked this error if the tracer wasn't present.
5675         gl_err = pContext->get_latched_gl_error();
5676
5677         pContext->clear_latched_gl_error();
5678     }
5679
5680     if (g_dump_gl_calls_flag)
5681     {
5682         vogl_message_printf("** END %s 0x%" PRIX64 "\n", VOGL_FUNCTION_NAME, vogl_get_current_kernel_thread_id());
5683     }
5684
5685     return gl_err;
5686 }
5687
5688 //----------------------------------------------------------------------------------------------------------------------
5689 // shader source code
5690 //----------------------------------------------------------------------------------------------------------------------
5691 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glShaderSourceARB(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_serialize_shader_source(trace_serializer, count, string, length);
5692 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glShaderSource(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_serialize_shader_source(trace_serializer, count, (const GLcharARB *const *)string, length);
5693 static void vogl_serialize_shader_source(vogl_entrypoint_serializer &trace_serializer, GLsizei count, const GLcharARB *const *string, const GLint *length)
5694 {
5695     if (g_dump_gl_shaders_flag)
5696     {
5697         vogl_log_printf("Source source code, %i string(s):\n", count);
5698         for (GLsizei i = 0; i < count; i++)
5699         {
5700             const char *pStr = (const char *)string[i];
5701             int str_len = 0;
5702             if (length)
5703                 str_len = length[i];
5704             else
5705                 str_len = pStr ? (vogl_strlen(pStr) + 1) : 0;
5706
5707             vogl_log_printf("\"");
5708             vogl_print_string(pStr, str_len);
5709             vogl_log_printf("\"\n");
5710         }
5711     }
5712
5713     if (trace_serializer.is_in_begin())
5714     {
5715         for (GLsizei i = 0; i < count; i++)
5716         {
5717             const char *pStr = (const char *)string[i];
5718             int str_len = 0;
5719             if (length)
5720                 str_len = length[i];
5721             else
5722                 str_len = pStr ? (vogl_strlen(pStr) + 1) : 0;
5723
5724             if ((str_len) && (pStr))
5725                 trace_serializer.add_key_value_blob(i, pStr, str_len);
5726         }
5727     }
5728 }
5729
5730 //----------------------------------------------------------------------------------------------------------------------
5731 // vogl_uses_client_side_arrays
5732 //----------------------------------------------------------------------------------------------------------------------
5733 static bool vogl_uses_client_side_arrays(vogl_context *pContext, bool indexed)
5734 {
5735     if ((!pContext) || (!pContext->get_uses_client_side_arrays()) || (pContext->is_core_profile()))
5736         return false;
5737
5738     vogl_scoped_gl_error_absorber gl_error_absorber(pContext);
5739     VOGL_NOTE_UNUSED(gl_error_absorber);
5740
5741     if (indexed)
5742     {
5743         GLuint element_array_buffer = vogl_get_bound_gl_buffer(GL_ELEMENT_ARRAY_BUFFER);
5744         if (!element_array_buffer)
5745             return true;
5746     }
5747
5748     bool used_old_style_gl_client_side_arrays = false;
5749
5750     GLint prev_client_active_texture = 0;
5751     GL_ENTRYPOINT(glGetIntegerv)(GL_CLIENT_ACTIVE_TEXTURE, &prev_client_active_texture);
5752
5753     const uint tex_coords = pContext->get_max_texture_coords();
5754
5755     for (uint i = 0; i < VOGL_NUM_CLIENT_SIDE_ARRAY_DESCS; i++)
5756     {
5757         const vogl_client_side_array_desc_t &desc = g_vogl_client_side_array_descs[i];
5758         if (i == vogl_texcoord_pointer_array_id)
5759         {
5760             for (uint tex_index = 0; tex_index < tex_coords; tex_index++)
5761             {
5762                 GL_ENTRYPOINT(glClientActiveTexture)(GL_TEXTURE0 + tex_index);
5763
5764                 GLboolean is_enabled = GL_ENTRYPOINT(glIsEnabled)(desc.m_is_enabled);
5765                 if (!is_enabled)
5766                     continue;
5767
5768                 GLint binding = 0;
5769                 GL_ENTRYPOINT(glGetIntegerv)(desc.m_get_binding, &binding);
5770                 if (binding)
5771                     continue;
5772
5773                 used_old_style_gl_client_side_arrays = true;
5774                 break;
5775             }
5776
5777             if (used_old_style_gl_client_side_arrays)
5778                 break;
5779         }
5780         else
5781         {
5782             GLboolean is_enabled = GL_ENTRYPOINT(glIsEnabled)(desc.m_is_enabled);
5783             if (!is_enabled)
5784                 continue;
5785
5786             GLint binding = 0;
5787             GL_ENTRYPOINT(glGetIntegerv)(desc.m_get_binding, &binding);
5788             if (binding)
5789                 continue;
5790
5791             used_old_style_gl_client_side_arrays = true;
5792             break;
5793         }
5794     }
5795
5796     GL_ENTRYPOINT(glClientActiveTexture)(prev_client_active_texture);
5797
5798     if (used_old_style_gl_client_side_arrays)
5799         return true;
5800
5801     uint64_t vertex_attrib_client_side_arrays = 0;
5802     VOGL_ASSUME(VOGL_MAX_SUPPORTED_GL_VERTEX_ATTRIBUTES <= sizeof(vertex_attrib_client_side_arrays) * 8);
5803
5804     for (uint i = 0; i < pContext->get_max_vertex_attribs(); i++)
5805     {
5806         GLint is_enabled = 0;
5807         GL_ENTRYPOINT(glGetVertexAttribiv)(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &is_enabled);
5808         if (!is_enabled)
5809             continue;
5810
5811         GLint cur_buf = 0;
5812         GL_ENTRYPOINT(glGetVertexAttribiv)(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &cur_buf);
5813         if (cur_buf)
5814             continue;
5815
5816         return true;
5817     }
5818
5819     return false;
5820 }
5821
5822 //----------------------------------------------------------------------------------------------------------------------
5823 // vogl_serialize_client_side_arrays_helper
5824 //----------------------------------------------------------------------------------------------------------------------
5825 static void vogl_serialize_client_side_arrays_helper(
5826     vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, const char *pFunc,
5827     GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex, bool start_end_valid, bool indexed)
5828 {
5829     VOGL_NOTE_UNUSED(mode);
5830
5831     VOGL_NOTE_UNUSED(pFunc);
5832
5833     if ((!pContext) || (pContext->is_core_profile()))
5834         return;
5835
5836     if (end < start)
5837     {
5838         vogl_error_printf("%s: end (%i) must be >= start (%i)\n", VOGL_FUNCTION_NAME, end, start);
5839         return;
5840     }
5841
5842     uint index_size = vogl_get_gl_type_size(type);
5843     if (!index_size)
5844     {
5845         vogl_error_printf("%s: Invalid type parameter 0x%08X\n", VOGL_FUNCTION_NAME, type);
5846         return;
5847     }
5848
5849     vogl_scoped_gl_error_absorber gl_error_absorber(pContext);
5850     VOGL_NOTE_UNUSED(gl_error_absorber);
5851
5852     GLuint element_array_buffer = 0;
5853     if (indexed)
5854     {
5855         element_array_buffer = vogl_get_bound_gl_buffer(GL_ELEMENT_ARRAY_BUFFER);
5856         if (!element_array_buffer)
5857         {
5858             if (!indices)
5859             {
5860                 vogl_error_printf("%s: No bound element array buffer, and indices parameter is NULL\n", VOGL_FUNCTION_NAME);
5861                 return;
5862             }
5863             else
5864             {
5865                 if (g_dump_gl_buffers_flag)
5866                 {
5867                     vogl_log_printf("Client side index data: ");
5868                     vogl_print_hex(indices, count * index_size, index_size);
5869                     vogl_log_printf("\n");
5870                 }
5871
5872                 if (trace_serializer.is_in_begin())
5873                 {
5874                     trace_serializer.add_key_value_blob(string_hash("indices"), indices, count * index_size);
5875                 }
5876             }
5877         }
5878     }
5879
5880     const gl_entrypoint_id_t cur_entrypoint = trace_serializer.get_cur_entrypoint();
5881     VOGL_NOTE_UNUSED(cur_entrypoint);
5882
5883     // TODO: Have the glSet*Pointer()'s funcs set a flag when the client uses this old shit, so we can avoid checking for it on apps that don't.
5884
5885     bool used_old_style_gl_client_side_arrays = false;
5886
5887     GLint prev_client_active_texture = 0;
5888     GL_ENTRYPOINT(glGetIntegerv)(GL_CLIENT_ACTIVE_TEXTURE, &prev_client_active_texture);
5889
5890     const uint tex_coords = pContext->get_max_texture_coords();
5891
5892     for (uint i = 0; i < VOGL_NUM_CLIENT_SIDE_ARRAY_DESCS; i++)
5893     {
5894         const vogl_client_side_array_desc_t &desc = g_vogl_client_side_array_descs[i];
5895         if (i == vogl_texcoord_pointer_array_id)
5896         {
5897             for (uint tex_index = 0; tex_index < tex_coords; tex_index++)
5898             {
5899                 GL_ENTRYPOINT(glClientActiveTexture)(GL_TEXTURE0 + tex_index);
5900
5901                 GLboolean is_enabled = GL_ENTRYPOINT(glIsEnabled)(desc.m_is_enabled);
5902                 if (!is_enabled)
5903                     continue;
5904
5905                 GLint binding = 0;
5906                 GL_ENTRYPOINT(glGetIntegerv)(desc.m_get_binding, &binding);
5907                 if (binding)
5908                     continue;
5909
5910                 used_old_style_gl_client_side_arrays = true;
5911                 break;
5912             }
5913
5914             if (used_old_style_gl_client_side_arrays)
5915                 break;
5916         }
5917         else
5918         {
5919             GLboolean is_enabled = GL_ENTRYPOINT(glIsEnabled)(desc.m_is_enabled);
5920             if (!is_enabled)
5921                 continue;
5922
5923             GLint binding = 0;
5924             GL_ENTRYPOINT(glGetIntegerv)(desc.m_get_binding, &binding);
5925             if (binding)
5926                 continue;
5927
5928             used_old_style_gl_client_side_arrays = true;
5929             break;
5930         }
5931     }
5932
5933     GL_ENTRYPOINT(glClientActiveTexture)(prev_client_active_texture);
5934
5935     uint64_t vertex_attrib_client_side_arrays = 0;
5936     VOGL_ASSUME(VOGL_MAX_SUPPORTED_GL_VERTEX_ATTRIBUTES <= sizeof(vertex_attrib_client_side_arrays) * 8);
5937
5938     for (uint i = 0; i < pContext->get_max_vertex_attribs(); i++)
5939     {
5940         GLint is_enabled = 0;
5941         GL_ENTRYPOINT(glGetVertexAttribiv)(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &is_enabled);
5942         if (!is_enabled)
5943             continue;
5944
5945         GLint cur_buf = 0;
5946         GL_ENTRYPOINT(glGetVertexAttribiv)(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &cur_buf);
5947         if (cur_buf)
5948             continue;
5949
5950         vertex_attrib_client_side_arrays |= (1ULL << i);
5951     }
5952
5953     if ((!used_old_style_gl_client_side_arrays) && (!vertex_attrib_client_side_arrays))
5954         return;
5955
5956     if (indexed)
5957     {
5958         if (!start_end_valid)
5959         {
5960             uint total_index_data_size = count * index_size;
5961
5962             // FIXME: Move index_data array to context state
5963             vogl::vector<uint8> index_data;
5964             const uint8 *pIndices_to_scan = static_cast<const uint8 *>(indices);
5965
5966             if (element_array_buffer)
5967             {
5968                 index_data.resize(total_index_data_size);
5969                 pIndices_to_scan = index_data.get_ptr();
5970
5971                 GL_ENTRYPOINT(glGetBufferSubData)(GL_ELEMENT_ARRAY_BUFFER, (GLintptr)indices, total_index_data_size, index_data.get_ptr());
5972             }
5973
5974             start = cUINT32_MAX;
5975             end = 0;
5976
5977             for (int i = 0; i < count; i++)
5978             {
5979                 uint v = 0;
5980
5981                 if (type == GL_UNSIGNED_BYTE)
5982                     v = pIndices_to_scan[i];
5983                 else if (type == GL_UNSIGNED_SHORT)
5984                     v = reinterpret_cast<const uint16 *>(pIndices_to_scan)[i];
5985                 else if (type == GL_UNSIGNED_INT)
5986                     v = reinterpret_cast<const uint32 *>(pIndices_to_scan)[i];
5987                 else
5988                 {
5989                     VOGL_ASSERT_ALWAYS;
5990                 }
5991
5992                 start = math::minimum(start, v);
5993                 end = math::maximum(end, v);
5994             }
5995         }
5996
5997         if (trace_serializer.is_in_begin())
5998         {
5999             trace_serializer.add_key_value(string_hash("start"), start);
6000             trace_serializer.add_key_value(string_hash("end"), end);
6001         }
6002     }
6003
6004     if (used_old_style_gl_client_side_arrays)
6005     {
6006         for (uint client_array_iter = 0; client_array_iter < VOGL_NUM_CLIENT_SIDE_ARRAY_DESCS; client_array_iter++)
6007         {
6008             const vogl_client_side_array_desc_t &desc = g_vogl_client_side_array_descs[client_array_iter];
6009
6010             uint n = 1;
6011             uint base_key_index = 0x1000 + client_array_iter;
6012
6013             // Special case texcoord pointers, which are accessed via the client active texture.
6014             if (client_array_iter == vogl_texcoord_pointer_array_id)
6015             {
6016                 n = tex_coords;
6017                 base_key_index = 0x2000;
6018             }
6019
6020             for (uint inner_iter = 0; inner_iter < n; inner_iter++)
6021             {
6022                 if (client_array_iter == vogl_texcoord_pointer_array_id)
6023                 {
6024                     GL_ENTRYPOINT(glClientActiveTexture)(GL_TEXTURE0 + inner_iter);
6025                 }
6026
6027                 GLboolean is_enabled = GL_ENTRYPOINT(glIsEnabled)(desc.m_is_enabled);
6028                 if (!is_enabled)
6029                     continue;
6030
6031                 GLint binding = 0;
6032                 GL_ENTRYPOINT(glGetIntegerv)(desc.m_get_binding, &binding);
6033                 if (binding)
6034                     continue;
6035
6036                 GLvoid *ptr = NULL;
6037                 GL_ENTRYPOINT(glGetPointerv)(desc.m_get_pointer, &ptr);
6038                 if (!ptr)
6039                     continue;
6040
6041                 GLint type = GL_BOOL;
6042                 if (desc.m_get_type)
6043                 {
6044                     GL_ENTRYPOINT(glGetIntegerv)(desc.m_get_type, &type);
6045                 }
6046
6047                 GLint stride = 0;
6048                 GL_ENTRYPOINT(glGetIntegerv)(desc.m_get_stride, &stride);
6049
6050                 GLint size = 1;
6051                 if (desc.m_get_size)
6052                 {
6053                     GL_ENTRYPOINT(glGetIntegerv)(desc.m_get_size, &size);
6054                 }
6055
6056                 uint type_size = vogl_get_gl_type_size(type);
6057                 if (!type_size)
6058                 {
6059                     vogl_error_printf("%s: Can't determine type size of enabled client side array set by func %s\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[desc.m_entrypoint].m_pName);
6060                     continue;
6061                 }
6062
6063                 if ((desc.m_entrypoint == VOGL_ENTRYPOINT_glIndexPointer) || (desc.m_entrypoint == VOGL_ENTRYPOINT_glIndexPointerEXT) ||
6064                     (desc.m_entrypoint == VOGL_ENTRYPOINT_glFogCoordPointer) || (desc.m_entrypoint == VOGL_ENTRYPOINT_glFogCoordPointerEXT) ||
6065                     (desc.m_entrypoint == VOGL_ENTRYPOINT_glEdgeFlagPointer) || (desc.m_entrypoint == VOGL_ENTRYPOINT_glEdgeFlagPointerEXT))
6066                 {
6067                     size = 1;
6068                 }
6069                 else if ((desc.m_entrypoint == VOGL_ENTRYPOINT_glNormalPointer) || (desc.m_entrypoint == VOGL_ENTRYPOINT_glNormalPointerEXT))
6070                 {
6071                     size = 3;
6072                 }
6073                 else if ((size < 1) || (size > 4))
6074                 {
6075                     vogl_error_printf("%s: Size of client side array set by func %s must be between 1 and 4\n", VOGL_FUNCTION_NAME, g_vogl_entrypoint_descs[desc.m_entrypoint].m_pName);
6076                     continue;
6077                 }
6078
6079                 if (!stride)
6080                     stride = type_size * size;
6081
6082                 uint first_vertex_ofs = (start + basevertex) * stride;
6083                 uint last_vertex_ofs = (end + basevertex) * stride;
6084                 uint vertex_data_size = (last_vertex_ofs + stride) - first_vertex_ofs;
6085
6086                 if (g_dump_gl_buffers_flag)
6087                 {
6088                     vogl_log_printf("Client side vertex data from %s index %u (comps: %i type_size: %i stride: %i):\n", g_vogl_entrypoint_descs[desc.m_entrypoint].m_pName, inner_iter, size, type_size, stride);
6089                     vogl_print_hex(static_cast<const uint8_t *>(ptr) + first_vertex_ofs, vertex_data_size, type_size);
6090                     vogl_log_printf("\n");
6091                 }
6092
6093                 if (trace_serializer.is_in_begin())
6094                 {
6095                     uint key_index = base_key_index + inner_iter;
6096                     trace_serializer.add_key_value_blob(static_cast<uint16>(key_index), static_cast<const uint8_t *>(ptr) + first_vertex_ofs, vertex_data_size);
6097                 }
6098             } // inner_iter
6099
6100         } // client_array_iter
6101
6102         GL_ENTRYPOINT(glClientActiveTexture)(prev_client_active_texture);
6103     } // used_old_style_gl_client_side_arrays
6104
6105     if (vertex_attrib_client_side_arrays)
6106     {
6107         for (uint i = 0; i < pContext->get_max_vertex_attribs(); i++)
6108         {
6109             if ((vertex_attrib_client_side_arrays & (1ULL << i)) == 0)
6110                 continue;
6111
6112             GLvoid *attrib_ptr = NULL;
6113             GL_ENTRYPOINT(glGetVertexAttribPointerv)(i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &attrib_ptr);
6114
6115             if (!attrib_ptr)
6116             {
6117                 vogl_error_printf("%s: Enabled vertex attribute index %i has no vertex array buffer, and attribute pointer is NULL\n", VOGL_FUNCTION_NAME, i);
6118                 continue;
6119             }
6120
6121             GLint attrib_size = 0;
6122             GL_ENTRYPOINT(glGetVertexAttribiv)(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &attrib_size);
6123
6124             GLint attrib_type = 0;
6125             GL_ENTRYPOINT(glGetVertexAttribiv)(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &attrib_type);
6126
6127             GLint attrib_stride = 0;
6128             GL_ENTRYPOINT(glGetVertexAttribiv)(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &attrib_stride);
6129
6130             uint num_comps = 4;
6131             if ((attrib_size != GL_BGRA) && (attrib_size < 1) && (attrib_size > 4))
6132             {
6133                 vogl_error_printf("%s: Enabled vertex attribute index %i has invalid size 0x%0X\n", VOGL_FUNCTION_NAME, i, attrib_size);
6134                 continue;
6135             }
6136             if ((attrib_size >= 1) && (attrib_size <= 4))
6137                 num_comps = attrib_size;
6138
6139             uint type_size = vogl_get_gl_type_size(attrib_type);
6140             if (!type_size)
6141             {
6142                 vogl_error_printf("%s: Vertex attribute index %i has unsupported type 0x%0X\n", VOGL_FUNCTION_NAME, i, attrib_type);
6143                 continue;
6144             }
6145
6146             uint stride = attrib_stride ? attrib_stride : (type_size * num_comps);
6147
6148             uint first_vertex_ofs = (start + basevertex) * stride;
6149             uint last_vertex_ofs = (end + basevertex) * stride;
6150             uint vertex_data_size = (last_vertex_ofs + stride) - first_vertex_ofs;
6151
6152             if (g_dump_gl_buffers_flag)
6153             {
6154                 vogl_log_printf("Client side vertex data for attrib %i (comps: %i type_size: %i stride: %i):\n", i, num_comps, type_size, stride);
6155
6156                 vogl_print_hex(static_cast<const uint8_t *>(attrib_ptr) + first_vertex_ofs, vertex_data_size, type_size);
6157
6158                 vogl_log_printf("\n");
6159             }
6160
6161             if (trace_serializer.is_in_begin())
6162             {
6163                 // TODO: Also send down start/end/first_vertex_ofs for debugging/verification purposes
6164                 trace_serializer.add_key_value_blob(static_cast<uint16>(i), static_cast<const uint8_t *>(attrib_ptr) + first_vertex_ofs, vertex_data_size);
6165             }
6166         }
6167     }
6168 }
6169
6170 //----------------------------------------------------------------------------------------------------------------------
6171 // glDrawRangeElements, glDrawRangeElementsBaseVertex, glDrawRangeElementsEXT
6172 //----------------------------------------------------------------------------------------------------------------------
6173 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glDrawRangeElementsBaseVertex(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_draw_range_elements_base_vertex_helper(pContext, trace_serializer, #name, mode, start, end, count, type, indices, 0, true);
6174 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glDrawRangeElements(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_draw_range_elements_base_vertex_helper(pContext, trace_serializer, #name, mode, start, end, count, type, indices, 0, true);
6175 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glDrawRangeElementsEXT(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_draw_range_elements_base_vertex_helper(pContext, trace_serializer, #name, mode, start, end, count, type, indices, 0, true);
6176 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glDrawElements(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_draw_range_elements_base_vertex_helper(pContext, trace_serializer, #name, mode, 0, 0, count, type, indices, 0, false);
6177 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glDrawElementsInstanced(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_draw_range_elements_base_vertex_helper(pContext, trace_serializer, #name, mode, 0, 0, count, type, indices, 0, false);
6178
6179 static inline void vogl_draw_range_elements_base_vertex_helper(
6180     vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, const char *pFunc,
6181     GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex, bool start_end_valid)
6182 {
6183     if (trace_serializer.is_in_begin())
6184     {
6185         vogl_serialize_client_side_arrays_helper(pContext, trace_serializer, pFunc,
6186                                                 mode, start, end, count, type, indices, basevertex, start_end_valid, true);
6187     }
6188 }
6189
6190 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glDrawArrays(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_draw_arrays_helper(pContext, trace_serializer, #name, mode, first, count);
6191 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glDrawArraysEXT(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_draw_arrays_helper(pContext, trace_serializer, #name, mode, first, count);
6192 static void vogl_draw_arrays_helper(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, const char *pFunc,
6193                                    GLenum mode, GLint first, GLsizei count)
6194 {
6195     if (trace_serializer.is_in_begin())
6196     {
6197         vogl_serialize_client_side_arrays_helper(pContext, trace_serializer, pFunc,
6198                                                 mode, first, first + count - 1, count, GL_UNSIGNED_BYTE, NULL, 0, true, false);
6199     }
6200 }
6201
6202 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glDrawArraysInstanced(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_draw_arrays_instanced_helper(pContext, trace_serializer, #name, mode, first, count, instancecount);
6203 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glDrawArraysInstancedEXT(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_draw_arrays_instanced_helper(pContext, trace_serializer, #name, mode, start, count, primcount);
6204 static void vogl_draw_arrays_instanced_helper(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, const char *pFunc,
6205                                              GLenum mode, GLint first, GLsizei count, GLsizei primcount)
6206 {
6207     VOGL_NOTE_UNUSED(primcount);
6208     if (trace_serializer.is_in_begin())
6209     {
6210         vogl_serialize_client_side_arrays_helper(pContext, trace_serializer, pFunc,
6211                                                 mode, first, first + count - 1, count, GL_UNSIGNED_BYTE, NULL, 0, true, false);
6212     }
6213 }
6214
6215 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glMultiDrawArrays(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_multi_draw_arrays_helper(pContext, trace_serializer, #name);
6216 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glMultiDrawArraysEXT(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_multi_draw_arrays_helper(pContext, trace_serializer, #name);
6217 static inline void vogl_multi_draw_arrays_helper(
6218     vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, const char *pFunc)
6219 {
6220     if (trace_serializer.is_in_begin())
6221     {
6222         if (vogl_uses_client_side_arrays(pContext, false))
6223         {
6224             vogl_warning_printf("%s: Function \"%s\" uses client side arrays, which is not currently supported. This call will not replay properly.\n", VOGL_FUNCTION_NAME, pFunc);
6225         }
6226     }
6227 }
6228
6229 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glMultiDrawElements(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_multi_draw_elements_helper(pContext, trace_serializer, #name);
6230 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glMultiDrawElementsEXT(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_multi_draw_elements_helper(pContext, trace_serializer, #name);
6231 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glMultiDrawElementsBaseVertex(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_multi_draw_elements_helper(pContext, trace_serializer, #name);
6232 static void vogl_multi_draw_elements_helper(
6233     vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, const char *pFunc)
6234 {
6235     // HACK HACK
6236     if (trace_serializer.is_in_begin())
6237     {
6238         if (vogl_uses_client_side_arrays(pContext, true))
6239         {
6240             vogl_warning_printf("%s: Function \"%s\" uses client side arrays, which is not currently supported. This call will not replay properly.\n", VOGL_FUNCTION_NAME, pFunc);
6241         }
6242     }
6243 }
6244
6245 //----------------------------------------------------------------------------------------------------------------------
6246 // String (extension manipulation)
6247 //----------------------------------------------------------------------------------------------------------------------
6248 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGetString(exported, category, ret, ret_type_enum, num_params, func_name, args, params) vogl_get_string_helper(#func_name, pContext, result, name, 0);
6249 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGetStringi(exported, category, ret, ret_type_enum, num_params, func_name, args, params) vogl_get_string_helper(#func_name, pContext, result, name, index);
6250 static inline void vogl_get_string_helper(const char *pFunc_name, vogl_context *pVOGL_context, const GLubyte *&pResult, GLenum name, GLuint index)
6251 {
6252     if (!g_disable_gl_program_binary_flag)
6253         return;
6254
6255     if ((pVOGL_context) && (pResult) && (name == GL_EXTENSIONS))
6256     {
6257         // Can't modify the driver's string directly, results in random crashes on my system. So we need to make a copy.
6258         char *pLoc = strstr((char *)pResult, "GL_ARB_get_program_binary");
6259         if (pLoc)
6260         {
6261             dynamic_string id(cVarArg, "%s_%x_%x", pFunc_name, name, index);
6262
6263             dynamic_string &ext_str = pVOGL_context->get_extension_map()[id];
6264             ext_str.set((char *)pResult);
6265
6266             int ofs = ext_str.find_left("GL_ARB_get_program_binary", true);
6267             if (ofs >= 0)
6268             {
6269                 ext_str.set_char(ofs + 3, 'X');
6270                 ext_str.set_char(ofs + 4, 'X');
6271                 ext_str.set_char(ofs + 5, 'X');
6272                 vogl_warning_printf("%s: Disabled GL_ARB_get_program_binary by changing its name to GL_XXX_get_program_binary.\n", VOGL_FUNCTION_NAME);
6273
6274                 pResult = (const GLubyte *)ext_str.get_ptr();
6275             }
6276         }
6277     }
6278 }
6279
6280 //----------------------------------------------------------------------------------------------------------------------
6281 // Buffer mapping, updating
6282 //----------------------------------------------------------------------------------------------------------------------
6283
6284 // TODO:
6285 // glMapNamedBufferEXT
6286 // glMapNamedBufferRangeEXT
6287 // glFlushMappedNamedBufferRangeEXT
6288 // glUnmapNamedBufferEXT
6289 // glGetNamedBufferSubDataEXT
6290
6291 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glNamedBufferDataEXT(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_named_buffer_data_ext_helper(pContext, trace_serializer, buffer, size, data, usage);
6292 static inline void vogl_named_buffer_data_ext_helper(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, GLuint buffer, GLsizeiptr size, const GLvoid *data, GLenum usage)
6293 {
6294     VOGL_NOTE_UNUSED(trace_serializer);
6295
6296     if (!pContext)
6297         return;
6298
6299     if (g_dump_gl_buffers_flag)
6300     {
6301         vogl_log_printf("Buffer data (size: %" PRIu64 "):\n", static_cast<uint64_t>(size));
6302         vogl_print_hex(data, size, 1);
6303         vogl_log_printf("\n");
6304     }
6305
6306     gl_buffer_desc &buf_desc = pContext->get_or_create_buffer_desc(buffer);
6307     buf_desc.m_size = size;
6308     buf_desc.m_usage = usage;
6309
6310     if (buf_desc.m_pMap)
6311     {
6312         vogl_warning_printf("%s: Setting buffer's data on an already mapped buffer, buffer will get unmapped by GL\n", VOGL_FUNCTION_NAME);
6313     }
6314
6315     buf_desc.m_pMap = NULL;
6316     buf_desc.m_map_ofs = 0;
6317     buf_desc.m_map_size = 0;
6318     buf_desc.m_map_access = 0;
6319     buf_desc.m_map_range = 0;
6320     buf_desc.m_flushed_ranges.resize(0);
6321 }
6322
6323 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glBufferData(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_buffer_data_helper(pContext, trace_serializer, target, size, data, usage);
6324 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glBufferDataARB(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_buffer_data_helper(pContext, trace_serializer, target, size, data, usage);
6325 static inline void vogl_buffer_data_helper(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage)
6326 {
6327     if (!pContext)
6328         return;
6329
6330     vogl_scoped_gl_error_absorber gl_error_absorber(pContext);
6331     VOGL_NOTE_UNUSED(gl_error_absorber);
6332
6333     GLuint buffer = vogl_get_bound_gl_buffer(target);
6334     if (!buffer)
6335     {
6336         vogl_error_printf("%s: No mapped buffer at target 0x%08X\n", VOGL_FUNCTION_NAME, target);
6337         return;
6338     }
6339
6340     vogl_named_buffer_data_ext_helper(pContext, trace_serializer, buffer, size, data, usage);
6341 }
6342
6343 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glNamedBufferSubDataEXT(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_named_buffer_subdata_ext_helper(pContext, trace_serializer, buffer, offset, size, data);
6344 static inline void vogl_named_buffer_subdata_ext_helper(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, GLuint buffer, GLintptr offset, GLsizeiptr size, const GLvoid *data)
6345 {
6346     VOGL_NOTE_UNUSED(buffer);
6347     VOGL_NOTE_UNUSED(trace_serializer);
6348     VOGL_NOTE_UNUSED(pContext);
6349
6350     if (g_dump_gl_buffers_flag)
6351     {
6352         vogl_log_printf("Buffer sub data (offset: %" PRIu64 " size: %" PRIu64 "):\n", static_cast<uint64_t>(offset), static_cast<uint64_t>(size));
6353         vogl_print_hex(data, size, 1);
6354         vogl_log_printf("\n");
6355     }
6356 }
6357
6358 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glBufferSubData(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_buffer_subdata_helper(pContext, trace_serializer, target, offset, size, data);
6359 static inline void vogl_buffer_subdata_helper(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data)
6360 {
6361     if (!pContext)
6362         return;
6363
6364     vogl_scoped_gl_error_absorber gl_error_absorber(pContext);
6365     VOGL_NOTE_UNUSED(gl_error_absorber);
6366
6367     GLuint buffer = vogl_get_bound_gl_buffer(target);
6368     if (!buffer)
6369     {
6370         vogl_error_printf("%s: No mapped buffer at target 0x%08X\n", VOGL_FUNCTION_NAME, target);
6371         return;
6372     }
6373
6374     vogl_named_buffer_subdata_ext_helper(pContext, trace_serializer, buffer, offset, size, data);
6375 }
6376
6377 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glMapBuffer(exported, category, ret, ret_type_enum, num_params, name, args, params) \
6378     GLenum orig_access = access;                                                                                          \
6379     vogl_map_buffer_gl_prolog_helper(pContext, trace_serializer, target, access);
6380 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glMapBufferARB(exported, category, ret, ret_type_enum, num_params, name, args, params) \
6381     GLenum orig_access = access;                                                                                             \
6382     vogl_map_buffer_gl_prolog_helper(pContext, trace_serializer, target, access);
6383 static inline void vogl_map_buffer_gl_prolog_helper(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, GLenum target, GLenum &access)
6384 {
6385     VOGL_NOTE_UNUSED(target);
6386     VOGL_NOTE_UNUSED(pContext);
6387
6388     if (trace_serializer.is_in_begin() || g_dump_gl_buffers_flag)
6389     {
6390         if (access == GL_WRITE_ONLY)
6391         {
6392             access = GL_READ_WRITE;
6393         }
6394     }
6395 }
6396
6397 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glMapBuffer(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_map_buffer_gl_epilog_helper(pContext, target, orig_access, result);
6398 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glMapBufferARB(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_map_buffer_gl_epilog_helper(pContext, target, orig_access, result);
6399 static inline void vogl_map_buffer_gl_epilog_helper(vogl_context *pContext, GLenum target, GLenum access, GLvoid *pPtr)
6400 {
6401     if (!pContext)
6402         return;
6403
6404     if (!pPtr)
6405     {
6406         vogl_warning_printf("%s: Map failed!\n", VOGL_FUNCTION_NAME);
6407         return;
6408     }
6409
6410     vogl_scoped_gl_error_absorber gl_error_absorber(pContext);
6411     VOGL_NOTE_UNUSED(gl_error_absorber);
6412
6413     GLuint buffer = vogl_get_bound_gl_buffer(target);
6414     if (!buffer)
6415     {
6416         vogl_error_printf("%s: No mapped buffer at target 0x%08X\n", VOGL_FUNCTION_NAME, target);
6417         return;
6418     }
6419
6420     gl_buffer_desc &buf_desc = pContext->get_or_create_buffer_desc(buffer);
6421     if (buf_desc.m_pMap)
6422     {
6423         vogl_warning_printf("%s: Buffer 0x%08X is already mapped!\n", VOGL_FUNCTION_NAME, buffer);
6424         return;
6425     }
6426
6427     // We need to sync with the underlying GL here to retreive the actual, true size of the mapped buffer in case an error occurred earlier and we didn't actually record the true size of the buffer.
6428     GLint64 actual_buf_size = buf_desc.m_size;
6429     GL_ENTRYPOINT(glGetBufferParameteri64v)(target, GL_BUFFER_SIZE, &actual_buf_size);
6430
6431     buf_desc.m_size = actual_buf_size;
6432     buf_desc.m_pMap = pPtr;
6433     buf_desc.m_map_ofs = 0;
6434     buf_desc.m_map_size = actual_buf_size;
6435     buf_desc.m_map_access = access;
6436     buf_desc.m_map_range = false;
6437 }
6438
6439 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glMapBufferRange(exported, category, ret, ret_type_enum, num_params, name, args, params) \
6440     GLbitfield orig_access = access;                                                                                           \
6441     vogl_map_buffer_range_gl_prolog_helper(pContext, trace_serializer, target, offset, length, access);
6442 static inline void vogl_map_buffer_range_gl_prolog_helper(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield &access)
6443 {
6444     VOGL_NOTE_UNUSED(length);
6445     VOGL_NOTE_UNUSED(offset);
6446     VOGL_NOTE_UNUSED(target);
6447     VOGL_NOTE_UNUSED(pContext);
6448
6449     if (trace_serializer.is_in_begin() || g_dump_gl_buffers_flag)
6450     {
6451         if (access & GL_MAP_WRITE_BIT)
6452         {
6453             // They are going to write, so we need to be able to read the data.
6454             // TODO: Warn user that we're going to not invalidate, which is definitely going to slow the GL driver down with CPU/GPU syncs.
6455             access &= ~GL_MAP_INVALIDATE_RANGE_BIT;
6456             access &= ~GL_MAP_INVALIDATE_BUFFER_BIT;
6457             access &= ~GL_MAP_UNSYNCHRONIZED_BIT;
6458             access |= GL_MAP_READ_BIT;
6459         }
6460     }
6461 }
6462
6463 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glMapBufferRange(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_map_buffer_range_gl_epilog_helper(pContext, target, offset, length, orig_access, result);
6464 static inline void vogl_map_buffer_range_gl_epilog_helper(vogl_context *pContext, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield &access, GLvoid *pPtr)
6465 {
6466     if (!pContext)
6467         return;
6468
6469     if (!pPtr)
6470     {
6471         vogl_warning_printf("%s: Map failed!\n", VOGL_FUNCTION_NAME);
6472         return;
6473     }
6474
6475     vogl_scoped_gl_error_absorber gl_error_absorber(pContext);
6476     VOGL_NOTE_UNUSED(gl_error_absorber);
6477
6478     GLuint buffer = vogl_get_bound_gl_buffer(target);
6479     if (!buffer)
6480     {
6481         vogl_error_printf("%s: No mapped buffer at target 0x%08X\n", VOGL_FUNCTION_NAME, target);
6482         return;
6483     }
6484
6485     gl_buffer_desc &buf_desc = pContext->get_or_create_buffer_desc(buffer);
6486     if (buf_desc.m_pMap)
6487     {
6488         vogl_error_printf("%s: Buffer 0x%08X is already mapped!\n", VOGL_FUNCTION_NAME, buffer);
6489         return;
6490     }
6491
6492     if (length > buf_desc.m_size)
6493     {
6494         vogl_warning_printf("%s: passed in length parameter (%" PRIi64 ") is larger the buffer 0x%08X's recorded size (%" PRIi64 ")!\n",
6495                            VOGL_FUNCTION_NAME, static_cast<int64_t>(length), buffer, buf_desc.m_size);
6496
6497         GLint64 actual_buf_size = buf_desc.m_size;
6498         GL_ENTRYPOINT(glGetBufferParameteri64v)(target, GL_BUFFER_SIZE, &actual_buf_size);
6499         buf_desc.m_size = actual_buf_size;
6500     }
6501
6502     buf_desc.m_pMap = pPtr;
6503     buf_desc.m_map_ofs = offset;
6504     buf_desc.m_map_size = length;
6505     buf_desc.m_map_access = access;
6506     buf_desc.m_map_range = true;
6507 }
6508
6509 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glFlushMappedBufferRange(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_flush_mapped_buffer_range(pContext, target, offset, length);
6510 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glFlushMappedBufferRangeAPPLE(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_flush_mapped_buffer_range(pContext, target, offset, size);
6511 static inline void vogl_flush_mapped_buffer_range(vogl_context *pContext, GLenum target, GLintptr offset, GLsizeiptr length)
6512 {
6513     if (!pContext)
6514         return;
6515
6516     vogl_scoped_gl_error_absorber gl_error_absorber(pContext);
6517     VOGL_NOTE_UNUSED(gl_error_absorber);
6518
6519     GLuint buffer = vogl_get_bound_gl_buffer(target);
6520     if (!buffer)
6521     {
6522         vogl_error_printf("%s: No mapped buffer at target 0x%08X\n", VOGL_FUNCTION_NAME, target);
6523         return;
6524     }
6525
6526     gl_buffer_desc &buf_desc = pContext->get_or_create_buffer_desc(buffer);
6527     if (!buf_desc.m_pMap)
6528     {
6529         vogl_error_printf("%s: Buffer 0x%08X is not currently mapped!\n", VOGL_FUNCTION_NAME, buffer);
6530         return;
6531     }
6532
6533     if ((offset + length) > buf_desc.m_map_size)
6534     {
6535         vogl_warning_printf("%s: passed in offset (%" PRIi64 ") and/or length (%" PRIi64 ") parameters are out of range vs. buffer 0x%08X's recorded map size (%" PRIi64 ")!\n",
6536                            VOGL_FUNCTION_NAME, static_cast<int64_t>(offset), static_cast<int64_t>(length), buffer, buf_desc.m_map_size);
6537     }
6538
6539     buf_desc.m_flushed_ranges.push_back(gl_buffer_desc::flushed_range(offset, length));
6540 }
6541
6542 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glUnmapBuffer(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_unmap_buffer_helper(pContext, trace_serializer, target);
6543 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glUnmapBufferARB(exported, category, ret, ret_type_enum, num_params, name, args, params) vogl_unmap_buffer_helper(pContext, trace_serializer, target);
6544 static inline void vogl_unmap_buffer_helper(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, GLenum target)
6545 {
6546     if (!pContext)
6547         return;
6548
6549     vogl_scoped_gl_error_absorber gl_error_absorber(pContext);
6550     VOGL_NOTE_UNUSED(gl_error_absorber);
6551
6552     GLuint buffer = vogl_get_bound_gl_buffer(target);
6553     if (!buffer)
6554     {
6555         vogl_error_printf("%s: No mapped buffer at target 0x%08X\n", VOGL_FUNCTION_NAME, target);
6556         return;
6557     }
6558
6559     gl_buffer_desc &buf_desc = pContext->get_or_create_buffer_desc(buffer);
6560     if (!buf_desc.m_pMap)
6561     {
6562         vogl_error_printf("%s: Buffer 0x%08X is not currently mapped!\n", VOGL_FUNCTION_NAME, buffer);
6563         return;
6564     }
6565
6566     bool writable_map = false;
6567     bool explicit_flush = false;
6568     if (buf_desc.m_map_range)
6569     {
6570         writable_map = (buf_desc.m_map_access & GL_MAP_WRITE_BIT) != 0;
6571         explicit_flush = (buf_desc.m_map_access & GL_MAP_FLUSH_EXPLICIT_BIT) != 0;
6572     }
6573     else
6574     {
6575         writable_map = (buf_desc.m_map_access != GL_READ_ONLY);
6576     }
6577
6578     if (trace_serializer.is_in_begin())
6579     {
6580         trace_serializer.add_key_value(string_hash("map_access"), buf_desc.m_map_access);
6581         trace_serializer.add_key_value(string_hash("map_range"), buf_desc.m_map_range);
6582         trace_serializer.add_key_value(string_hash("explicit_flush"), explicit_flush);
6583         trace_serializer.add_key_value(string_hash("writable_map"), writable_map);
6584     }
6585
6586     if (writable_map)
6587     {
6588         if (explicit_flush)
6589         {
6590             if (!buf_desc.m_flushed_ranges.size())
6591             {
6592                 vogl_warning_printf("%s: Mapped buffer at target 0x%08X was mapped with GL_MAP_FLUSH_EXPLICIT_BIT, but no ranges where actually flushed\n", VOGL_FUNCTION_NAME, target);
6593             }
6594
6595             if (g_dump_gl_buffers_flag)
6596             {
6597                 for (uint i = 0; i < buf_desc.m_flushed_ranges.size(); i++)
6598                 {
6599                     vogl_log_printf("Flushed buffer data (ofs: %" PRIu64 " size: %" PRIu64 "):\n", buf_desc.m_flushed_ranges[i].m_ofs, buf_desc.m_flushed_ranges[i].m_size);
6600                     vogl_print_hex(static_cast<const uint8_t *>(buf_desc.m_pMap) + buf_desc.m_flushed_ranges[i].m_ofs, buf_desc.m_flushed_ranges[i].m_size, 1);
6601                     vogl_log_printf("\n");
6602                 }
6603             }
6604
6605             if (trace_serializer.is_in_begin())
6606             {
6607                 trace_serializer.add_key_value(string_hash("flushed_ranges"), buf_desc.m_flushed_ranges.size());
6608                 for (uint i = 0; i < buf_desc.m_flushed_ranges.size(); i++)
6609                 {
6610                     int key_index = i * 4;
6611                     trace_serializer.add_key_value(key_index, buf_desc.m_flushed_ranges[i].m_ofs);
6612                     trace_serializer.add_key_value(key_index + 1, buf_desc.m_flushed_ranges[i].m_size);
6613                     // TODO
6614                     VOGL_ASSERT(buf_desc.m_flushed_ranges[i].m_size <= cUINT32_MAX);
6615                     trace_serializer.add_key_value_blob(key_index + 2, static_cast<const uint8_t *>(buf_desc.m_pMap) + buf_desc.m_flushed_ranges[i].m_ofs, static_cast<uint>(buf_desc.m_flushed_ranges[i].m_size));
6616                 }
6617             }
6618         }
6619         else
6620         {
6621             if (g_dump_gl_buffers_flag)
6622             {
6623                 vogl_log_printf("Flushed buffer data (ofs: %" PRIu64 " size: %" PRIu64 "):\n", cast_val_to_uint64(buf_desc.m_map_ofs), cast_val_to_uint64(buf_desc.m_map_size));
6624                 vogl_print_hex(static_cast<const uint8_t *>(buf_desc.m_pMap), buf_desc.m_map_size, 1);
6625                 vogl_log_printf("\n");
6626             }
6627
6628             if (trace_serializer.is_in_begin())
6629             {
6630                 trace_serializer.add_key_value(0, buf_desc.m_map_ofs);
6631                 trace_serializer.add_key_value(1, buf_desc.m_map_size);
6632                 // TODO
6633                 VOGL_ASSERT(buf_desc.m_map_size <= cUINT32_MAX);
6634                 trace_serializer.add_key_value_blob(2, static_cast<const uint8_t *>(buf_desc.m_pMap), static_cast<uint>(buf_desc.m_map_size));
6635             }
6636         }
6637     }
6638
6639     buf_desc.m_pMap = NULL;
6640     buf_desc.m_map_ofs = 0;
6641     buf_desc.m_map_size = 0;
6642     buf_desc.m_map_access = 0;
6643     buf_desc.m_flushed_ranges.resize(0);
6644 }
6645
6646 //----------------------------------------------------------------------------------------------------------------------
6647 // glCreateProgram/glCreateProgramARB function epilog
6648 //----------------------------------------------------------------------------------------------------------------------
6649 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glCreateProgram(e, c, rt, r, nu, ne, a, p) \
6650     if (pContext)                                                                  \
6651         vogl_create_program_helper(pContext, result);
6652 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glCreateProgramObjectARB(e, c, rt, r, nu, ne, a, p) \
6653     if (pContext)                                                                           \
6654         vogl_create_program_helper(pContext, result);
6655 static inline void vogl_create_program_helper(vogl_context *pContext, GLuint handle)
6656 {
6657     VOGL_NOTE_UNUSED(pContext);
6658     VOGL_NOTE_UNUSED(handle);
6659
6660 #if 0
6661         if (handle)
6662         {
6663                 vogl_scoped_gl_error_absorber gl_error_absorber(pContext);
6664                 VOGL_NOTE_UNUSED(gl_error_absorber);
6665
6666                 // Ensure program bins are always retrievable
6667                 GL_ENTRYPOINT(glProgramParameteri)(handle, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
6668         }
6669 #endif
6670 }
6671
6672 //----------------------------------------------------------------------------------------------------------------------
6673 // glProgramParameteri GL prolog
6674 //----------------------------------------------------------------------------------------------------------------------
6675 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glProgramParameteri(e, c, rt, r, nu, ne, a, p) \
6676     if (pContext)                                                                    \
6677         vogl_program_parameteri_prolog(pContext, program, pname, value);
6678 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glProgramParameteriARB(e, c, rt, r, nu, ne, a, p) \
6679     if (pContext)                                                                       \
6680         vogl_program_parameteri_prolog(pContext, program, pname, value);
6681 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glProgramParameteriEXT(e, c, rt, r, nu, ne, a, p) \
6682     if (pContext)                                                                       \
6683         vogl_program_parameteri_prolog(pContext, program, pname, value);
6684 static void vogl_program_parameteri_prolog(vogl_context *pContext, GLuint program, GLenum pname, GLint &value)
6685 {
6686     VOGL_NOTE_UNUSED(pContext);
6687     VOGL_NOTE_UNUSED(program);
6688     VOGL_NOTE_UNUSED(pname);
6689     VOGL_NOTE_UNUSED(value);
6690
6691 #if 0
6692         if ((pname == GL_PROGRAM_BINARY_RETRIEVABLE_HINT) && (value == GL_FALSE))
6693         {
6694                 vogl_warning_printf("%s: Client is trying to set program %u's GL_PROGRAM_BINARY_RETRIEVABLE_HINT to GL_FALSE, the tracer is overriding this to GL_TRUE\n", VOGL_FUNCTION_NAME, program);
6695
6696                 value = GL_TRUE;
6697         }
6698 #endif
6699 }
6700
6701 //----------------------------------------------------------------------------------------------------------------------
6702 // glBindAttribLocationARB/glBindAttribLocation function epilog
6703 //----------------------------------------------------------------------------------------------------------------------
6704 //#define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glBindAttribLocationARB(e, c, rt, r, nu, ne, a, p) if (pContext) pContext->bind_attrib_location(programObj, index, name);
6705 //#define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glBindAttribLocation(e, c, rt, r, nu, ne, a, p) if (pContext) pContext->bind_attrib_location(program, index, reinterpret_cast<const char *>(name));
6706
6707 //----------------------------------------------------------------------------------------------------------------------
6708 // glDeleteProgramsARB/glDeleteProgram function epilog
6709 //----------------------------------------------------------------------------------------------------------------------
6710 //#define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glDeleteProgramsARB(e, c, rt, r, nu, ne, a, p) if (pContext) pContext->delete_program_attrib_locations(n, programs);
6711 //#define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glDeleteProgram(e, c, rt, r, nu, ne, a, p) if (pContext) pContext->delete_program_attrib_locations(1, &program);
6712
6713 //----------------------------------------------------------------------------------------------------------------------
6714 // vogl_dump_program_outputs
6715 //----------------------------------------------------------------------------------------------------------------------
6716 static void vogl_dump_program_outputs(json_node &doc_root, vogl_context *pContext, GLuint program)
6717 {
6718     if (pContext->get_context_info().supports_extension("GL_ARB_program_interface_query") &&
6719         GL_ENTRYPOINT(glGetProgramInterfaceiv) && GL_ENTRYPOINT(glGetProgramResourceName) && GL_ENTRYPOINT(glGetProgramResourceiv))
6720     {
6721         GLint num_active_outputs = 0;
6722         GL_ENTRYPOINT(glGetProgramInterfaceiv)(program, GL_PROGRAM_OUTPUT, GL_ACTIVE_RESOURCES, &num_active_outputs);
6723         pContext->peek_and_drop_gl_error();
6724
6725         doc_root.add_key_value("total_active_outputs", num_active_outputs);
6726
6727         json_node &outputs_object = doc_root.add_array("active_outputs");
6728         for (int i = 0; i < num_active_outputs; i++)
6729         {
6730             GLchar name[256];
6731             GLsizei name_len = 0;
6732             GL_ENTRYPOINT(glGetProgramResourceName)(program, GL_PROGRAM_OUTPUT, i, sizeof(name), &name_len, name);
6733             pContext->peek_and_drop_gl_error();
6734
6735             const GLenum props_to_query[5] = { GL_LOCATION, GL_LOCATION_INDEX, GL_TYPE, GL_ARRAY_SIZE, GL_IS_PER_PATCH }; // TODO: GL_LOCATION_COMPONENT
6736             GLint props[5] = { 0, 0, 0, 0, 0 };
6737             GL_ENTRYPOINT(glGetProgramResourceiv)(program, GL_PROGRAM_OUTPUT, i, VOGL_ARRAY_SIZE(props_to_query), props_to_query, VOGL_ARRAY_SIZE(props), NULL, props);
6738             pContext->peek_and_drop_gl_error();
6739
6740             json_node &output_node = outputs_object.add_object();
6741             output_node.add_key_value("index", i);
6742             output_node.add_key_value("name", reinterpret_cast<const char *>(name));
6743             output_node.add_key_value("location", props[0]);
6744             output_node.add_key_value("location_index", props[1]);
6745             output_node.add_key_value("type", props[2]);
6746             output_node.add_key_value("array_size", props[3]);
6747             output_node.add_key_value("is_per_patch", props[4]);
6748             //output_node.add_key_value("location_component", props[5]);
6749         }
6750     }
6751 }
6752
6753 //----------------------------------------------------------------------------------------------------------------------
6754 // glLinkProgramARB function epilog
6755 //----------------------------------------------------------------------------------------------------------------------
6756 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glLinkProgramARB(e, c, rt, r, nu, ne, a, p) vogl_link_program_arb(pContext, trace_serializer, programObj);
6757 static inline void vogl_link_program_arb(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, GLhandleARB programObj)
6758 {
6759     if (!pContext)
6760         return;
6761
6762     vogl_scoped_gl_error_absorber gl_error_absorber(pContext);
6763     VOGL_NOTE_UNUSED(gl_error_absorber);
6764
6765     GLint link_status = 0;
6766     GL_ENTRYPOINT(glGetObjectParameterivARB)(programObj, GL_OBJECT_LINK_STATUS_ARB, &link_status);
6767     pContext->peek_and_drop_gl_error();
6768
6769     if (trace_serializer.is_in_begin())
6770     {
6771         json_document doc;
6772         json_node &doc_root = *doc.get_root();
6773         doc_root.add_key_value("program", programObj);
6774         doc_root.add_key_value("link_status", link_status);
6775         doc_root.add_key_value("func_id", VOGL_ENTRYPOINT_glLinkProgramARB);
6776
6777         GLint active_attributes = 0;
6778         GL_ENTRYPOINT(glGetObjectParameterivARB)(programObj, GL_OBJECT_ACTIVE_ATTRIBUTES_ARB, &active_attributes);
6779         pContext->peek_and_drop_gl_error();
6780
6781         doc_root.add_key_value("total_active_attributes", active_attributes);
6782
6783         if (active_attributes)
6784         {
6785             json_node &attribs_object = doc_root.add_array("active_attribs");
6786
6787             for (int i = 0; i < active_attributes; i++)
6788             {
6789                 GLint size = 0;
6790                 GLenum type = 0;
6791                 GLcharARB name[256];
6792
6793                 GL_ENTRYPOINT(glGetActiveAttribARB)(programObj, i, sizeof(name), NULL, &size, &type, name);
6794                 pContext->peek_and_drop_gl_error();
6795
6796                 if ((!name[0]) || ((name[0] == 'g') && (name[1] == 'l') && (name[2] == '_')))
6797                     continue;
6798
6799                 GLint location = GL_ENTRYPOINT(glGetAttribLocationARB)(programObj, name);
6800                 pContext->peek_and_drop_gl_error();
6801
6802                 if (location < 0)
6803                     continue;
6804
6805                 json_node &attrib_node = attribs_object.add_object();
6806                 attrib_node.add_key_value("index", i);
6807                 attrib_node.add_key_value("name", reinterpret_cast<const char *>(name));
6808                 attrib_node.add_key_value("location", location);
6809             }
6810         }
6811
6812         GLint active_uniforms = 0;
6813         GL_ENTRYPOINT(glGetObjectParameterivARB)(programObj, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &active_uniforms);
6814         pContext->peek_and_drop_gl_error();
6815
6816         doc_root.add_key_value("total_active_uniforms", active_uniforms);
6817
6818         if (active_uniforms)
6819         {
6820             json_node &uniforms_object = doc_root.add_array("active_uniforms");
6821
6822             for (int i = 0; i < active_uniforms; i++)
6823             {
6824                 GLsizei length = 0;
6825                 GLint size = 0;
6826                 GLenum type = 0;
6827                 GLcharARB name[256];
6828
6829                 GL_ENTRYPOINT(glGetActiveUniformARB)(programObj, i, sizeof(name), &length, &size, &type, name);
6830                 pContext->peek_and_drop_gl_error();
6831
6832                 if ((!name[0]) || (!length) || ((name[0] == 'g') && (name[1] == 'l') && (name[2] == '_')))
6833                     continue;
6834
6835                 GLint location = GL_ENTRYPOINT(glGetUniformLocationARB)(programObj, name);
6836                 pContext->peek_and_drop_gl_error();
6837
6838                 if (location < 0)
6839                     continue;
6840
6841                 json_node &uniform_node = uniforms_object.add_object();
6842                 uniform_node.add_key_value("index", i);
6843                 uniform_node.add_key_value("name", reinterpret_cast<const char *>(name));
6844                 uniform_node.add_key_value("location", location);
6845                 uniform_node.add_key_value("size", size);
6846                 uniform_node.add_key_value("type", type);
6847             }
6848         }
6849
6850         vogl_dump_program_outputs(doc_root, pContext, programObj);
6851
6852         trace_serializer.add_key_value_json_document("metadata", doc);
6853     }
6854
6855     if (programObj)
6856     {
6857         if ((link_status) || (!pContext->has_linked_program_snapshot(programObj)))
6858         {
6859             if (!pContext->add_linked_program_snapshot(programObj))
6860                 vogl_error_printf("%s: Failed snapshotting program into link-time program shadow table, program 0x%X\n", VOGL_FUNCTION_NAME, programObj);
6861         }
6862     }
6863 }
6864
6865 //----------------------------------------------------------------------------------------------------------------------
6866 // glLinkProgram/glProgramBinary function epilog
6867 //----------------------------------------------------------------------------------------------------------------------
6868 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glLinkProgram(e, c, rt, r, nu, ne, a, p) vogl_link_program_epilog_helper(pContext, trace_serializer, program, VOGL_ENTRYPOINT_##ne, GL_NONE, NULL, 0);
6869 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glProgramBinary(e, c, rt, r, nu, ne, a, p) vogl_link_program_epilog_helper(pContext, trace_serializer, program, VOGL_ENTRYPOINT_##ne, binaryFormat, binary, length);
6870 static inline void vogl_link_program_epilog_helper(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, GLuint program, gl_entrypoint_id_t id, GLenum binary_format, const GLvoid *pBinary, GLsizei binary_length)
6871 {
6872     if (!pContext)
6873         return;
6874
6875     vogl_scoped_gl_error_absorber gl_error_absorber(pContext);
6876     VOGL_NOTE_UNUSED(gl_error_absorber);
6877
6878     GLint link_status = 0;
6879     GL_ENTRYPOINT(glGetProgramiv)(program, GL_LINK_STATUS, &link_status);
6880     pContext->peek_and_drop_gl_error();
6881
6882     if (trace_serializer.is_in_begin())
6883     {
6884         json_document doc;
6885         json_node &doc_root = *doc.get_root();
6886         doc_root.add_key_value("program", program);
6887         doc_root.add_key_value("link_status", link_status);
6888         doc_root.add_key_value("func_id", static_cast<uint32>(id));
6889
6890         // Active uniform blocks
6891         GLint active_uniform_blocks = 0;
6892         GL_ENTRYPOINT(glGetProgramiv)(program, GL_ACTIVE_UNIFORM_BLOCKS, &active_uniform_blocks);
6893         pContext->peek_and_drop_gl_error();
6894
6895         doc_root.add_key_value("active_uniform_blocks", active_uniform_blocks);
6896
6897         // Active attributes
6898         GLint active_attributes = 0;
6899         GL_ENTRYPOINT(glGetProgramiv)(program, GL_ACTIVE_ATTRIBUTES, &active_attributes);
6900         pContext->peek_and_drop_gl_error();
6901
6902         doc_root.add_key_value("total_active_attributes", active_attributes);
6903
6904         if (active_attributes)
6905         {
6906             json_node &attribs_object = doc_root.add_array("active_attribs");
6907
6908             for (int i = 0; i < active_attributes; i++)
6909             {
6910                 GLint size = 0;
6911                 GLenum type = 0;
6912                 GLchar name[256];
6913
6914                 GL_ENTRYPOINT(glGetActiveAttrib)(program, i, sizeof(name), NULL, &size, &type, name);
6915                 pContext->peek_and_drop_gl_error();
6916
6917                 if ((!name[0]) || ((name[0] == 'g') && (name[1] == 'l') && (name[2] == '_')))
6918                     continue;
6919
6920                 GLint location = GL_ENTRYPOINT(glGetAttribLocation)(program, name);
6921                 pContext->peek_and_drop_gl_error();
6922
6923                 if (location < 0)
6924                     continue;
6925
6926                 json_node &attrib_node = attribs_object.add_object();
6927                 attrib_node.add_key_value("index", i);
6928                 attrib_node.add_key_value("name", reinterpret_cast<const char *>(name));
6929                 attrib_node.add_key_value("location", location);
6930             }
6931         }
6932
6933         // Active uniforms
6934         GLint active_uniforms = 0;
6935         GL_ENTRYPOINT(glGetProgramiv)(program, GL_ACTIVE_UNIFORMS, &active_uniforms);
6936         doc_root.add_key_value("total_active_uniforms", active_uniforms);
6937
6938         if (active_uniforms)
6939         {
6940             json_node &uniforms_object = doc_root.add_array("active_uniforms");
6941
6942             for (int i = 0; i < active_uniforms; i++)
6943             {
6944                 GLsizei length = 0;
6945                 GLint size = 0;
6946                 GLenum type = 0;
6947                 GLchar name[256];
6948
6949                 GL_ENTRYPOINT(glGetActiveUniform)(program, i, sizeof(name), &length, &size, &type, name);
6950                 pContext->peek_and_drop_gl_error();
6951
6952                 if ((!name[0]) || (!length) || ((name[0] == 'g') && (name[1] == 'l') && (name[2] == '_')))
6953                     continue;
6954
6955                 GLint location = GL_ENTRYPOINT(glGetUniformLocation)(program, name);
6956                 pContext->peek_and_drop_gl_error();
6957
6958                 if (location < 0)
6959                     continue;
6960
6961                 json_node &uniform_node = uniforms_object.add_object();
6962                 uniform_node.add_key_value("index", i);
6963                 uniform_node.add_key_value("name", reinterpret_cast<const char *>(name));
6964                 uniform_node.add_key_value("location", location);
6965                 uniform_node.add_key_value("size", size);
6966                 uniform_node.add_key_value("type", type);
6967             }
6968         }
6969
6970         // Program outputs
6971         vogl_dump_program_outputs(doc_root, pContext, program);
6972
6973         // Transform feedback
6974         GLint mode = GL_NONE;
6975         GL_ENTRYPOINT(glGetProgramiv)(program, GL_TRANSFORM_FEEDBACK_BUFFER_MODE, &mode);
6976         pContext->peek_and_drop_gl_error();
6977
6978         doc_root.add_key_value("transform_feedback_mode", g_gl_enums.find_gl_name(mode));
6979
6980         GLint num_varyings = 0;
6981         GL_ENTRYPOINT(glGetProgramiv)(program, GL_TRANSFORM_FEEDBACK_VARYINGS, &num_varyings);
6982         pContext->peek_and_drop_gl_error();
6983
6984         doc_root.add_key_value("transform_feedback_num_varyings", num_varyings);
6985
6986         if (num_varyings)
6987         {
6988             json_node &transform_feedback_varyings = doc_root.add_array("transform_feedback_varyings");
6989
6990             for (GLint i = 0; i < num_varyings; i++)
6991             {
6992                 GLchar name[512];
6993                 GLsizei length = 0, size = 0;
6994                 GLenum type = GL_NONE;
6995
6996                 GL_ENTRYPOINT(glGetTransformFeedbackVarying)(program, i, sizeof(name), &length, &size, &type, name);
6997                 pContext->peek_and_drop_gl_error();
6998
6999                 json_node &uniform_node = transform_feedback_varyings.add_object();
7000                 uniform_node.add_key_value("index", i);
7001                 uniform_node.add_key_value("name", reinterpret_cast<const char *>(name));
7002                 uniform_node.add_key_value("size", size);
7003                 uniform_node.add_key_value("type", type);
7004             }
7005         }
7006
7007         // Add JSON document to packet
7008         trace_serializer.add_key_value_json_document("metadata", doc);
7009     }
7010
7011     if (program)
7012     {
7013         if ((link_status) || (!pContext->has_linked_program_snapshot(program)))
7014         {
7015             if (id == VOGL_ENTRYPOINT_glProgramBinary)
7016             {
7017                 if (!pContext->add_linked_program_snapshot(program, binary_format, pBinary, binary_length))
7018                 {
7019                     vogl_error_printf("%s: Failed snapshotting binary program into link-time program shadow table, program 0x%X\n", VOGL_FUNCTION_NAME, program);
7020                 }
7021             }
7022             else
7023             {
7024                 if (!pContext->add_linked_program_snapshot(program))
7025                 {
7026                     vogl_error_printf("%s: Failed snapshotting program into link-time program shadow table, program 0x%X\n", VOGL_FUNCTION_NAME, program);
7027                 }
7028             }
7029         }
7030     }
7031 }
7032
7033 #define DEF_FUNCTION_CUSTOM_FUNC_PROLOG_glProgramBinary(e, c, rt, r, nu, ne, a, p) vogl_glProgramBinary_prolog(program, binaryFormat, binary, length);
7034 static inline void vogl_glProgramBinary_prolog(GLuint program, GLenum binaryFormat, const void *&pBinary, GLsizei &length)
7035 {
7036     VOGL_NOTE_UNUSED(binaryFormat);
7037
7038     if (g_disable_gl_program_binary_flag)
7039     {
7040         vogl_debug_printf("%s: Tracer is forcing a bogus program binary for program %d\n", VOGL_FUNCTION_NAME, program);
7041
7042         // These will intentionally cause the program binary to not link, forcing the application to use shader strings instead of binaries.
7043         pBinary = &g_dummy_program;
7044         length = 1;
7045     }
7046 }
7047
7048 //----------------------------------------------------------------------------------------------------------------------
7049 // glTransformFeedbackVaryings func epilog
7050 //----------------------------------------------------------------------------------------------------------------------
7051 #define DEF_FUNCTION_CUSTOM_FUNC_EPILOG_glTransformFeedbackVaryings(e, c, rt, r, nu, ne, a, p) \
7052     if (pContext)                                                                              \
7053         vogl_transform_feedback_varyings(pContext, trace_serializer, count, varyings);
7054 static inline void vogl_transform_feedback_varyings(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, GLsizei count, GLchar *const *pVaryings)
7055 {
7056     VOGL_NOTE_UNUSED(pContext);
7057
7058     if ((!count) || (!pVaryings) || (!trace_serializer.is_in_begin()))
7059         return;
7060
7061     dynamic_string varying;
7062     for (int i = 0; i < count; i++)
7063     {
7064         varying.empty();
7065         if (pVaryings[i])
7066             varying = reinterpret_cast<const char *>(pVaryings[i]);
7067
7068         trace_serializer.add_key_value(i, varying);
7069     }
7070 }
7071
7072 //----------------------------------------------------------------------------------------------------------------------
7073 // ARB program shadowing
7074 //----------------------------------------------------------------------------------------------------------------------
7075 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenProgramsARB(e, c, rt, r, nu, ne, a, p) \
7076     if (pContext)                                                                 \
7077         pContext->peek_and_record_gl_error();
7078 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenProgramsARB(e, c, rt, r, nu, ne, a, p) \
7079     if (pContext && !pContext->peek_and_record_gl_error())                        \
7080         pContext->gen_arb_programs(n, programs);
7081
7082 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteProgramsARB(e, c, rt, r, nu, ne, a, p) \
7083     if (pContext)                                                                    \
7084         pContext->peek_and_record_gl_error();
7085 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteProgramsARB(e, c, rt, r, nu, ne, a, p) \
7086     if (pContext && !pContext->peek_and_record_gl_error())                           \
7087         pContext->del_arb_programs(n, programs);
7088
7089 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindProgramARB(e, c, rt, r, nu, ne, a, p) \
7090     if (pContext)                                                                 \
7091         pContext->peek_and_record_gl_error();
7092 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindProgramARB(e, c, rt, r, nu, ne, a, p) \
7093     if (pContext && !pContext->peek_and_record_gl_error())                        \
7094         pContext->bind_arb_program(target, program);
7095
7096 //----------------------------------------------------------------------------------------------------------------------
7097 // renderbuffer shadowing
7098 //----------------------------------------------------------------------------------------------------------------------
7099 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenRenderbuffers(e, c, rt, r, nu, ne, a, p) \
7100     if (pContext)                                                                   \
7101         pContext->peek_and_record_gl_error();
7102 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenRenderbuffers(e, c, rt, r, nu, ne, a, p) \
7103     if (pContext && !pContext->peek_and_record_gl_error())                          \
7104         pContext->gen_render_buffers(n, renderbuffers);
7105
7106 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenRenderbuffersEXT(e, c, rt, r, nu, ne, a, p) \
7107     if (pContext)                                                                      \
7108         pContext->peek_and_record_gl_error();
7109 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenRenderbuffersEXT(e, c, rt, r, nu, ne, a, p) \
7110     if (pContext && !pContext->peek_and_record_gl_error())                             \
7111         pContext->gen_render_buffers(n, renderbuffers);
7112
7113 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteRenderbuffers(e, c, rt, r, nu, ne, a, p) \
7114     if (pContext)                                                                      \
7115         pContext->peek_and_record_gl_error();
7116 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteRenderbuffers(e, c, rt, r, nu, ne, a, p) \
7117     if (pContext && !pContext->peek_and_record_gl_error())                             \
7118         pContext->del_render_buffers(n, renderbuffers);
7119
7120 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteRenderbuffersEXT(e, c, rt, r, nu, ne, a, p) \
7121     if (pContext)                                                                         \
7122         pContext->peek_and_record_gl_error();
7123 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteRenderbuffersEXT(e, c, rt, r, nu, ne, a, p) \
7124     if (pContext && !pContext->peek_and_record_gl_error())                                \
7125         pContext->del_render_buffers(n, renderbuffers);
7126 //----------------------------------------------------------------------------------------------------------------------
7127 // Buffer shadowing
7128 //----------------------------------------------------------------------------------------------------------------------
7129 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenBuffers(e, c, rt, r, nu, ne, a, p) \
7130     if (pContext)                                                             \
7131         pContext->peek_and_record_gl_error();
7132 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenBuffers(e, c, rt, r, nu, ne, a, p) \
7133     if (pContext && !pContext->peek_and_record_gl_error())                    \
7134         pContext->gen_buffers(n, buffers);
7135
7136 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenBuffersARB(e, c, rt, r, nu, ne, a, p) \
7137     if (pContext)                                                                \
7138         pContext->peek_and_record_gl_error();
7139 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenBuffersARB(e, c, rt, r, nu, ne, a, p) \
7140     if (pContext && !pContext->peek_and_record_gl_error())                       \
7141         pContext->gen_buffers(n, buffers);
7142
7143 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindBuffer(e, c, rt, r, nu, ne, a, p) \
7144     if (pContext)                                                             \
7145         pContext->peek_and_record_gl_error();
7146 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindBuffer(e, c, rt, r, nu, ne, a, p)                                           \
7147     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7148         pContext->bind_buffer(target, buffer);
7149
7150 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindBufferBase(e, c, rt, r, nu, ne, a, p) \
7151     if (pContext)                                                                 \
7152         pContext->peek_and_record_gl_error();
7153 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindBufferBase(e, c, rt, r, nu, ne, a, p)                                       \
7154     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7155         pContext->bind_buffer(target, buffer);
7156
7157 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindBufferBaseEXT(e, c, rt, r, nu, ne, a, p) \
7158     if (pContext)                                                                    \
7159         pContext->peek_and_record_gl_error();
7160 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindBufferBaseEXT(e, c, rt, r, nu, ne, a, p)                                    \
7161     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7162         pContext->bind_buffer(target, buffer);
7163
7164 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindBufferBaseNV(e, c, rt, r, nu, ne, a, p) \
7165     if (pContext)                                                                   \
7166         pContext->peek_and_record_gl_error();
7167 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindBufferBaseNV(e, c, rt, r, nu, ne, a, p)                                     \
7168     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7169         pContext->bind_buffer(target, buffer);
7170
7171 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindBufferRange(e, c, rt, r, nu, ne, a, p) \
7172     if (pContext)                                                                  \
7173         pContext->peek_and_record_gl_error();
7174 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindBufferRange(e, c, rt, r, nu, ne, a, p)                                      \
7175     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7176         pContext->bind_buffer(target, buffer);
7177
7178 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindBufferRangeEXT(e, c, rt, r, nu, ne, a, p) \
7179     if (pContext)                                                                     \
7180         pContext->peek_and_record_gl_error();
7181 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindBufferRangeEXT(e, c, rt, r, nu, ne, a, p)                                   \
7182     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7183         pContext->bind_buffer(target, buffer);
7184
7185 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindBufferRangeNV(e, c, rt, r, nu, ne, a, p) \
7186     if (pContext)                                                                    \
7187         pContext->peek_and_record_gl_error();
7188 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindBufferRangeNV(e, c, rt, r, nu, ne, a, p)                                    \
7189     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7190         pContext->bind_buffer(target, buffer);
7191
7192 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindBufferARB(e, c, rt, r, nu, ne, a, p) \
7193     if (pContext)                                                                \
7194         pContext->peek_and_record_gl_error();
7195 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindBufferARB(e, c, rt, r, nu, ne, a, p)                                        \
7196     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7197         pContext->bind_buffer(target, buffer);
7198
7199 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteBuffers(e, c, rt, r, nu, ne, a, p) \
7200     if (pContext)                                                                \
7201         pContext->peek_and_record_gl_error();
7202 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteBuffers(e, c, rt, r, nu, ne, a, p)                                        \
7203     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7204         pContext->delete_buffers(n, buffers);
7205
7206 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteBuffersARB(e, c, rt, r, nu, ne, a, p) \
7207     if (pContext)                                                                   \
7208         pContext->peek_and_record_gl_error();
7209 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteBuffersARB(e, c, rt, r, nu, ne, a, p)                                     \
7210     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7211         pContext->delete_buffers(n, buffers);
7212
7213 //----------------------------------------------------------------------------------------------------------------------
7214 // Texture handle shadowing
7215 //----------------------------------------------------------------------------------------------------------------------
7216 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenTextures(e, c, rt, r, nu, ne, a, p) \
7217     if (pContext)                                                              \
7218         pContext->peek_and_record_gl_error();
7219 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenTextures(e, c, rt, r, nu, ne, a, p) \
7220     if (pContext && !pContext->peek_and_record_gl_error())                     \
7221         pContext->gen_textures(n, textures);
7222
7223 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenTexturesEXT(e, c, rt, r, nu, ne, a, p) \
7224     if (pContext)                                                                 \
7225         pContext->peek_and_record_gl_error();
7226 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenTexturesEXT(e, c, rt, r, nu, ne, a, p) \
7227     if (pContext && !pContext->peek_and_record_gl_error())                        \
7228         pContext->gen_textures(n, textures);
7229
7230 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteTextures(e, c, rt, r, nu, ne, a, p) \
7231     if (pContext)                                                                 \
7232         pContext->peek_and_record_gl_error();
7233 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteTextures(e, c, rt, r, nu, ne, a, p) \
7234     if (pContext && !pContext->peek_and_record_gl_error())                        \
7235         pContext->del_textures(n, textures);
7236
7237 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteTexturesEXT(e, c, rt, r, nu, ne, a, p) \
7238     if (pContext)                                                                    \
7239         pContext->peek_and_record_gl_error();
7240 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteTexturesEXT(e, c, rt, r, nu, ne, a, p) \
7241     if (pContext && !pContext->peek_and_record_gl_error())                           \
7242         pContext->del_textures(n, textures);
7243
7244 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindTexture(e, c, rt, r, nu, ne, a, p) \
7245     if (pContext)                                                              \
7246         pContext->peek_and_record_gl_error();
7247 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindTexture(e, c, rt, r, nu, ne, a, p)                                          \
7248     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7249         pContext->bind_texture(target, texture);
7250
7251 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindTextureEXT(e, c, rt, r, nu, ne, a, p) \
7252     if (pContext)                                                                 \
7253         pContext->peek_and_record_gl_error();
7254 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindTextureEXT(e, c, rt, r, nu, ne, a, p)                                       \
7255     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7256         pContext->bind_texture(target, texture);
7257
7258 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindMultiTextureEXT(e, c, rt, r, nu, ne, a, p) \
7259     if (pContext)                                                                      \
7260         pContext->peek_and_record_gl_error();
7261 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindMultiTextureEXT(e, c, rt, r, nu, ne, a, p)                                  \
7262     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7263         pContext->bind_texture(target, texture);
7264
7265 //----------------------------------------------------------------------------------------------------------------------
7266 // Framebuffer handle shadowing
7267 //----------------------------------------------------------------------------------------------------------------------
7268 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenFramebuffers(e, c, rt, r, nu, ne, a, p) \
7269     if (pContext)                                                                  \
7270         pContext->peek_and_record_gl_error();
7271 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenFramebuffers(e, c, rt, r, nu, ne, a, p) \
7272     if (pContext && !pContext->peek_and_record_gl_error())                         \
7273         pContext->gen_framebuffers(n, framebuffers);
7274
7275 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenFramebuffersEXT(e, c, rt, r, nu, ne, a, p) \
7276     if (pContext)                                                                     \
7277         pContext->peek_and_record_gl_error();
7278 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenFramebuffersEXT(e, c, rt, r, nu, ne, a, p) \
7279     if (pContext && !pContext->peek_and_record_gl_error())                            \
7280         pContext->gen_framebuffers(n, framebuffers);
7281
7282 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteFramebuffers(e, c, rt, r, nu, ne, a, p) \
7283     if (pContext)                                                                     \
7284         pContext->peek_and_record_gl_error();
7285 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteFramebuffers(e, c, rt, r, nu, ne, a, p) \
7286     if (pContext && !pContext->peek_and_record_gl_error())                            \
7287         pContext->del_framebuffers(n, framebuffers);
7288
7289 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteFramebuffersEXT(e, c, rt, r, nu, ne, a, p) \
7290     if (pContext)                                                                        \
7291         pContext->peek_and_record_gl_error();
7292 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteFramebuffersEXT(e, c, rt, r, nu, ne, a, p) \
7293     if (pContext && !pContext->peek_and_record_gl_error())                               \
7294         pContext->del_framebuffers(n, framebuffers);
7295
7296 //----------------------------------------------------------------------------------------------------------------------
7297 // VAO handle shadowing
7298 //----------------------------------------------------------------------------------------------------------------------
7299 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenVertexArrays(e, c, rt, r, nu, ne, a, p) \
7300     if (pContext)                                                                  \
7301         pContext->peek_and_record_gl_error();
7302 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenVertexArrays(e, c, rt, r, nu, ne, a, p) \
7303     if (pContext && !pContext->peek_and_record_gl_error())                         \
7304         pContext->gen_vertexarrays(n, arrays);
7305
7306 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteVertexArrays(e, c, rt, r, nu, ne, a, p) \
7307     if (pContext)                                                                     \
7308         pContext->peek_and_record_gl_error();
7309 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteVertexArrays(e, c, rt, r, nu, ne, a, p) \
7310     if (pContext && !pContext->peek_and_record_gl_error())                            \
7311         pContext->del_vertexarrays(n, arrays);
7312
7313 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBindVertexArray(e, c, rt, r, nu, ne, a, p) \
7314     if (pContext)                                                                  \
7315         pContext->peek_and_record_gl_error();
7316 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBindVertexArray(e, c, rt, r, nu, ne, a, p)                                      \
7317     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7318         pContext->bind_vertexarray(array);
7319
7320 //----------------------------------------------------------------------------------------------------------------------
7321 // Sync handle shadowing
7322 //----------------------------------------------------------------------------------------------------------------------
7323 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glFenceSync(e, c, rt, r, nu, ne, a, p) \
7324     if (pContext)                                                            \
7325         pContext->peek_and_record_gl_error();
7326 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glFenceSync(e, c, rt, r, nu, ne, a, p) \
7327     if (pContext && !pContext->peek_and_record_gl_error())                   \
7328         pContext->gen_sync(result);
7329
7330 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteSync(e, c, rt, r, nu, ne, a, p) \
7331     if (pContext)                                                             \
7332         pContext->peek_and_record_gl_error();
7333 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteSync(e, c, rt, r, nu, ne, a, p) \
7334     if (pContext && !pContext->peek_and_record_gl_error())                    \
7335         pContext->del_sync(sync);
7336
7337 //----------------------------------------------------------------------------------------------------------------------
7338 // Sampler object handle shadowing
7339 //----------------------------------------------------------------------------------------------------------------------
7340 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenSamplers(e, c, rt, r, nu, ne, a, p) \
7341     if (pContext)                                                              \
7342         pContext->peek_and_record_gl_error();
7343 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenSamplers(e, c, rt, r, nu, ne, a, p) \
7344     if (pContext && !pContext->peek_and_record_gl_error())                     \
7345         pContext->gen_samplers(count, samplers);
7346
7347 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteSamplers(e, c, rt, r, nu, ne, a, p) \
7348     if (pContext)                                                                 \
7349         pContext->peek_and_record_gl_error();
7350 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteSamplers(e, c, rt, r, nu, ne, a, p) \
7351     if (pContext && !pContext->peek_and_record_gl_error())                        \
7352         pContext->del_samplers(count, samplers);
7353
7354 //----------------------------------------------------------------------------------------------------------------------
7355 // Query handle shadowing
7356 //----------------------------------------------------------------------------------------------------------------------
7357 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenQueries(e, c, rt, r, nu, ne, a, p) \
7358     if (pContext)                                                             \
7359         pContext->peek_and_record_gl_error();
7360 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenQueries(e, c, rt, r, nu, ne, a, p) \
7361     if (pContext && !pContext->peek_and_record_gl_error())                    \
7362         pContext->gen_queries(n, ids);
7363
7364 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenQueriesARB(e, c, rt, r, nu, ne, a, p) \
7365     if (pContext)                                                                \
7366         pContext->peek_and_record_gl_error();
7367 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenQueriesARB(e, c, rt, r, nu, ne, a, p) \
7368     if (pContext && !pContext->peek_and_record_gl_error())                       \
7369         pContext->gen_queries(n, ids);
7370
7371 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteQueries(e, c, rt, r, nu, ne, a, p) \
7372     if (pContext)                                                                \
7373         pContext->peek_and_record_gl_error();
7374 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteQueries(e, c, rt, r, nu, ne, a, p) \
7375     if (pContext && !pContext->peek_and_record_gl_error())                       \
7376         pContext->del_queries(n, ids);
7377
7378 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteQueriesARB(e, c, rt, r, nu, ne, a, p) \
7379     if (pContext)                                                                   \
7380         pContext->peek_and_record_gl_error();
7381 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteQueriesARB(e, c, rt, r, nu, ne, a, p) \
7382     if (pContext && !pContext->peek_and_record_gl_error())                          \
7383         pContext->del_queries(n, ids);
7384
7385 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBeginQuery(e, c, rt, r, nu, ne, a, p) \
7386     if (pContext)                                                             \
7387         pContext->peek_and_record_gl_error();
7388 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBeginQuery(e, c, rt, r, nu, ne, a, p)                                           \
7389     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7390         pContext->begin_query(target, id);
7391
7392 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBeginQueryARB(e, c, rt, r, nu, ne, a, p) \
7393     if (pContext)                                                                \
7394         pContext->peek_and_record_gl_error();
7395 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBeginQueryARB(e, c, rt, r, nu, ne, a, p)                                        \
7396     if (pContext && !pContext->peek_and_record_gl_error() && (pContext->get_current_display_list_mode() != GL_COMPILE)) \
7397         pContext->begin_query(target, id);
7398
7399 //----------------------------------------------------------------------------------------------------------------------
7400 // Display list shadowing
7401 //----------------------------------------------------------------------------------------------------------------------
7402 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glGenLists(e, c, rt, r, nu, ne, a, p) \
7403     if (pContext)                                                           \
7404         pContext->peek_and_record_gl_error();
7405 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glGenLists(e, c, rt, r, nu, ne, a, p) \
7406     if (pContext && !pContext->peek_and_record_gl_error())                  \
7407         pContext->gen_lists(result, range);
7408
7409 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glDeleteLists(e, c, rt, r, nu, ne, a, p) \
7410     if (pContext)                                                              \
7411         pContext->peek_and_record_gl_error();
7412 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glDeleteLists(e, c, rt, r, nu, ne, a, p) \
7413     if (pContext && !pContext->peek_and_record_gl_error())                     \
7414         pContext->del_lists(list, range);
7415
7416 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glNewList(e, c, rt, r, nu, ne, a, p) \
7417     if (pContext)                                                          \
7418         pContext->peek_and_record_gl_error();
7419 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glNewList(e, c, rt, r, nu, ne, a, p) \
7420     if (pContext && !pContext->peek_and_record_gl_error())                 \
7421         pContext->new_list(list, mode);
7422
7423 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glEndList(e, c, rt, r, nu, ne, a, p) \
7424     if (pContext)                                                          \
7425         pContext->peek_and_record_gl_error();
7426 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glEndList(e, c, rt, r, nu, ne, a, p) \
7427     if (pContext && !pContext->peek_and_record_gl_error())                 \
7428         pContext->end_list();
7429
7430 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glXUseXFont(e, c, rt, r, nu, ne, a, p) \
7431     if (pContext)                                                            \
7432         pContext->peek_and_record_gl_error();
7433
7434 //----------------------------------------------------------------------------------------------------------------------
7435 // glBegin/glEnd shadowing
7436 //----------------------------------------------------------------------------------------------------------------------
7437 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glBegin(e, c, rt, r, nu, ne, a, p) \
7438     if (pContext)                                                        \
7439         pContext->peek_and_record_gl_error();
7440 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glBegin(e, c, rt, r, nu, ne, a, p) \
7441     if (pContext)                                                        \
7442         pContext->set_in_gl_begin(true);
7443
7444 //#define DEF_FUNCTION_CUSTOM_GL_PROLOG_glEnd(e, c, rt, r, nu, ne, a, p)
7445 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glEnd(e, c, rt, r, nu, ne, a, p) \
7446     if (pContext)                                                      \
7447         pContext->set_in_gl_begin(false);
7448
7449 //----------------------------------------------------------------------------------------------------------------------
7450 // Program/shader shadowing
7451 //----------------------------------------------------------------------------------------------------------------------
7452 #define DEF_FUNCTION_CUSTOM_GL_CALLER_glCreateProgram(e, c, rt, r, nu, ne, a, p) \
7453     if (pContext)                                                                \
7454         result = pContext->handle_create_program(VOGL_ENTRYPOINT_##ne);           \
7455     else                                                                         \
7456         result = 0;
7457 #define DEF_FUNCTION_CUSTOM_GL_CALLER_glCreateProgramObjectARB(e, c, rt, r, nu, ne, a, p) \
7458     if (pContext)                                                                         \
7459         result = pContext->handle_create_program(VOGL_ENTRYPOINT_##ne);                    \
7460     else                                                                                  \
7461         result = 0;
7462
7463 #define DEF_FUNCTION_CUSTOM_GL_CALLER_glCreateShader(e, c, rt, r, nu, ne, a, p) \
7464     if (pContext)                                                               \
7465         result = pContext->handle_create_shader(VOGL_ENTRYPOINT_##ne, type);     \
7466     else                                                                        \
7467         result = 0;
7468 #define DEF_FUNCTION_CUSTOM_GL_CALLER_glCreateShaderObjectARB(e, c, rt, r, nu, ne, a, p) \
7469     if (pContext)                                                                        \
7470         result = pContext->handle_create_shader(VOGL_ENTRYPOINT_##ne, shaderType);        \
7471     else                                                                                 \
7472         result = 0;
7473
7474 #define DEF_FUNCTION_CUSTOM_GL_CALLER_glUseProgram(e, c, rt, r, nu, ne, a, p) \
7475     if (pContext)                                                             \
7476         pContext->handle_use_program(VOGL_ENTRYPOINT_##ne, program);
7477 #define DEF_FUNCTION_CUSTOM_GL_CALLER_glUseProgramObjectARB(e, c, rt, r, nu, ne, a, p) \
7478     if (pContext)                                                                      \
7479         pContext->handle_use_program(VOGL_ENTRYPOINT_##ne, programObj);
7480
7481 #define DEF_FUNCTION_CUSTOM_GL_CALLER_glDeleteShader(e, c, rt, r, nu, ne, a, p) \
7482     if (pContext)                                                               \
7483         pContext->handle_del_shader(VOGL_ENTRYPOINT_##ne, shader);
7484 #define DEF_FUNCTION_CUSTOM_GL_CALLER_glDeleteProgram(e, c, rt, r, nu, ne, a, p) \
7485     if (pContext)                                                                \
7486         pContext->handle_del_program(VOGL_ENTRYPOINT_##ne, program);
7487 #define DEF_FUNCTION_CUSTOM_GL_CALLER_glDeleteObjectARB(e, c, rt, r, nu, ne, a, p) \
7488     if (pContext)                                                                  \
7489         pContext->handle_del_object(VOGL_ENTRYPOINT_##ne, obj);
7490
7491 #define DEF_FUNCTION_CUSTOM_GL_CALLER_glDetachShader(e, c, rt, r, nu, ne, a, p) \
7492     if (pContext)                                                               \
7493         pContext->handle_detach_shader(VOGL_ENTRYPOINT_##ne, program, shader);
7494 #define DEF_FUNCTION_CUSTOM_GL_CALLER_glDetachObjectARB(e, c, rt, r, nu, ne, a, p) \
7495     if (pContext)                                                                  \
7496         pContext->handle_detach_shader(VOGL_ENTRYPOINT_##ne, containerObj, attachedObj);
7497
7498 //----------------------------------------------------------------------------------------------------------------------
7499 // Client side array usage detection
7500 //----------------------------------------------------------------------------------------------------------------------
7501 #define VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE                              \
7502     if ((!g_disable_client_side_array_tracing) && (pContext) && (pointer)) \
7503         vogl_check_for_client_side_array_usage(pContext, pointer);
7504 static inline void vogl_check_for_client_side_array_usage(vogl_context *pContext, const void *pPointer)
7505 {
7506     VOGL_NOTE_UNUSED(pPointer);
7507
7508     if (pContext->get_uses_client_side_arrays() || pContext->is_core_profile())
7509         return;
7510
7511     pContext->peek_and_record_gl_error();
7512
7513     GLint cur_array_buf_binding = 0;
7514     GL_ENTRYPOINT(glGetIntegerv)(GL_ARRAY_BUFFER_BINDING, &cur_array_buf_binding);
7515
7516     if (pContext->peek_and_drop_gl_error() == GL_NO_ERROR)
7517     {
7518         if (!cur_array_buf_binding)
7519         {
7520             pContext->set_uses_client_side_arrays(true);
7521             vogl_warning_printf("%s: Client side array usage has been detected, this will negatively impact tracing performance, use --vogl_disable_client_side_array_tracing to disable\n", VOGL_FUNCTION_NAME);
7522         }
7523     }
7524 }
7525
7526 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glVertexPointer(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7527 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glNormalPointer(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7528 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glColorPointer(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7529 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glIndexPointer(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7530 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glTexCoordPointer(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7531 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glEdgeFlagPointer(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7532 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glFogCoordPointer(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7533 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glSecondaryColorPointer(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7534 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glInterleavedArrays(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7535 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glVertexPointerEXT(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7536 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glNormalPointerEXT(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7537 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glColorPointerEXT(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7538 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glIndexPointerEXT(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7539 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glTexCoordPointerEXT(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7540 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glEdgeFlagPointerEXT(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7541 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glFogCoordPointerEXT(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7542 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glSecondaryColorPointerEXT(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7543 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glVertexAttribPointer(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7544 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glVertexAttribPointerARB(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7545 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glVertexAttribPointerNV(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7546 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glVertexAttribIPointer(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7547 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glVertexAttribIPointerEXT(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7548 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glVertexAttribLPointer(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7549 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glVertexAttribLPointerEXT(e, c, rt, r, nu, ne, a, p) VOGL_CHECK_FOR_CLIENT_SIDE_ARRAY_USAGE
7550
7551 //----------------------------------------------------------------------------------------------------------------------
7552 // glXUseXFont shadowing
7553 //----------------------------------------------------------------------------------------------------------------------
7554 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glXUseXFont(e, c, rt, r, nu, ne, a, p) vogl_glx_use_xfont_epilog_helper(pContext, trace_serializer, font, first, count, list_base);
7555 static void vogl_glx_use_xfont_epilog_helper(vogl_context *pContext, vogl_entrypoint_serializer &trace_serializer, Font font, int first, int count, int list_base)
7556 {
7557     if (!pContext)
7558         return;
7559
7560     if (pContext->peek_and_record_gl_error())
7561         return;
7562
7563     char *pFont = NULL;
7564
7565     if (pContext->get_display())
7566     {
7567         XFontStruct *pFont_struct = XQueryFont((Display *)pContext->get_display(), font);
7568
7569         if (pFont_struct)
7570         {
7571             unsigned long value = 0;
7572             Bool result = XGetFontProperty(pFont_struct, XA_FONT, &value);
7573             if (result)
7574             {
7575                 pFont = (char *)XGetAtomName((Display *)pContext->get_display(), (Atom)value);
7576                 if ((pFont) && (trace_serializer.is_in_begin()))
7577                 {
7578                     trace_serializer.add_key_value("font_name", pFont);
7579                 }
7580             }
7581         }
7582
7583         XFreeFontInfo(NULL, pFont_struct, 1);
7584     }
7585
7586     pContext->glx_font(pFont, first, count, list_base);
7587 }
7588
7589 //----------------------------------------------------------------------------------------------------------------------
7590 // vogl_display_list_bind_callback
7591 //----------------------------------------------------------------------------------------------------------------------
7592 static void vogl_display_list_bind_callback(vogl_namespace_t handle_namespace, GLenum target, GLuint handle, void *pOpaque)
7593 {
7594     vogl_context *pContext = static_cast<vogl_context *>(pOpaque);
7595
7596     // TODO: We don't whitelist anything but texture binds in display lists currently.
7597     switch (handle_namespace)
7598     {
7599         case VOGL_NAMESPACE_TEXTURES:
7600         {
7601             if ((handle) && (target != GL_NONE))
7602                 pContext->bind_texture_conditionally(target, handle);
7603             break;
7604         }
7605         default:
7606         {
7607             vogl_warning_printf("%s: TODO: Unsupported bind in display list, namespace %s target %s handle %u\n", VOGL_FUNCTION_NAME, vogl_get_namespace_name(handle_namespace), g_gl_enums.find_gl_name(target), handle);
7608             break;
7609         }
7610     }
7611 }
7612
7613 //----------------------------------------------------------------------------------------------------------------------
7614 // glCallList shadowing
7615 //----------------------------------------------------------------------------------------------------------------------
7616 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glCallList(e, c, rt, r, nu, ne, a, p) \
7617     if (pContext)                                                           \
7618         pContext->peek_and_record_gl_error();
7619 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glCallList(e, c, rt, r, nu, ne, a, p) \
7620     if (pContext && !pContext->peek_and_record_gl_error())                  \
7621         vogl_gl_call_list_helper(pContext, list);
7622 static void vogl_gl_call_list_helper(vogl_context *pContext, GLuint list)
7623 {
7624     if (!pContext)
7625         return;
7626
7627     pContext->parse_list_and_update_shadows(list, vogl_display_list_bind_callback, pContext);
7628 }
7629
7630 //----------------------------------------------------------------------------------------------------------------------
7631 // glCallLists shadowing
7632 //----------------------------------------------------------------------------------------------------------------------
7633 #define DEF_FUNCTION_CUSTOM_GL_PROLOG_glCallLists(e, c, rt, r, nu, ne, a, p) \
7634     if (pContext)                                                            \
7635         pContext->peek_and_record_gl_error();
7636 #define DEF_FUNCTION_CUSTOM_GL_EPILOG_glCallLists(e, c, rt, r, nu, ne, a, p) \
7637     if (pContext && !pContext->peek_and_record_gl_error())                   \
7638         vogl_gl_call_lists_helper(pContext, n, type, lists);
7639 static void vogl_gl_call_lists_helper(vogl_context *pContext, GLsizei n, GLenum type, const GLvoid *lists)
7640 {
7641     if (!pContext)
7642         return;
7643
7644     vogl_scoped_gl_error_absorber gl_error_absorber(pContext);
7645     VOGL_NOTE_UNUSED(gl_error_absorber);
7646
7647     pContext->parse_lists_and_update_shadows(n, type, lists, vogl_display_list_bind_callback, pContext);
7648 }
7649
7650 //----------------------------------------------------------------------------------------------------------------------
7651 // Ensure all entrypoints are fully serializable
7652 // This function MUST appear before #include "gl_glx_array_size_macros.inc", below
7653 //----------------------------------------------------------------------------------------------------------------------
7654 static void vogl_check_entrypoints()
7655 {
7656     vogl_debug_printf("vogl_check_entrypoints: begin (specify --vogl_debug for more validation)\n");
7657
7658     typedef vogl::hash_map<uint, dynamic_string> array_size_macro_hashmap;
7659     array_size_macro_hashmap defined_array_size_macros;
7660     array_size_macro_hashmap undefined_array_size_macros;
7661
7662 #define CUSTOM_FUNC_HANDLER_DEFINED(id) g_vogl_entrypoint_descs[id].m_has_custom_func_handler = true; //printf("%u %s\n", id, g_vogl_entrypoint_descs[id].m_pName);
7663 #define CUSTOM_FUNC_HANDLER_NOT_DEFINED(id)
7664 #include "gl_glx_custom_func_handler_validator.inc"
7665 #undef CUSTOM_FUNC_HANDLER_DEFINED
7666 #undef CUSTOM_FUNC_HANDLER_NOT_DEFINED
7667
7668 #define VALIDATE_ARRAY_SIZE_MACRO_DEFINED(name, index) defined_array_size_macros.insert(index, #name);
7669 #define VALIDATE_ARRAY_SIZE_MACRO_NOT_DEFINED(name, index) undefined_array_size_macros.insert(index, #name);
7670 #include "gl_glx_array_size_macros_validator.inc"
7671 #undef VALIDATE_ARRAY_SIZE_MACRO_DEFINED
7672 #undef VALIDATE_ARRAY_SIZE_MACRO_NOT_DEFINED
7673
7674     vogl::vector<uint> undefined_func_return_array_size_macros;
7675
7676 #define CUSTOM_FUNC_RETURN_PARAM_ARRAY_SIZE_HANDLER_DEFINED(macro_name, func_name)
7677 #define CUSTOM_FUNC_RETURN_PARAM_ARRAY_SIZE_HANDLER_NOT_DEFINED(macro_name, func_name) g_vogl_entrypoint_descs[VOGL_ENTRYPOINT_##func_name].m_custom_return_param_array_size_macro_is_missing = true;
7678 #include "gl_glx_custom_return_param_array_size_macro_validator.inc"
7679 #undef CUSTOM_FUNC_RETURN_PARAM_ARRAY_SIZE_HANDLER_DEFINED
7680 #undef CUSTOM_FUNC_RETURN_PARAM_ARRAY_SIZE_HANDLER_NOT_DEFINED
7681
7682     if (vogl::check_for_command_line_param("--vogl_debug"))
7683     {
7684         for (array_size_macro_hashmap::const_iterator it = undefined_array_size_macros.begin(); it != undefined_array_size_macros.end(); ++it)
7685         {
7686             uint idx = it->first;
7687             const dynamic_string &name = it->second;
7688             VOGL_NOTE_UNUSED(name);
7689
7690             gl_entrypoint_id_t func = static_cast<gl_entrypoint_id_t>(idx >> 16);
7691             uint param = idx & 0xFFFF;
7692
7693             vogl_warning_printf("%s: Custom array size macro for func %u \"%s\" param %u has not been defined, this function cannot be traced\n",
7694                                VOGL_FUNCTION_NAME, func, g_vogl_entrypoint_descs[func].m_pName, param);
7695
7696             g_vogl_entrypoint_param_descs[func][param].m_custom_array_size_macro_is_missing = true;
7697             g_vogl_entrypoint_descs[func].m_custom_array_size_macro_is_missing = true;
7698         }
7699
7700         vogl_debug_printf("Undefined array size macros:\n");
7701         vogl_debug_printf("---\n");
7702         for (array_size_macro_hashmap::const_iterator it = undefined_array_size_macros.begin(); it != undefined_array_size_macros.end(); ++it)
7703         {
7704             const dynamic_string &name = it->second;
7705             vogl_debug_printf("%s\n", name.get_ptr());
7706         }
7707         vogl_debug_printf("---\n");
7708
7709         vogl_debug_printf("Undefined return param array size macros:\n");
7710         vogl_debug_printf("---\n");
7711         for (uint i = 0; i < VOGL_NUM_ENTRYPOINTS; i++)
7712         {
7713             vogl_ctype_t return_ctype = g_vogl_entrypoint_descs[i].m_return_ctype;
7714
7715             if (return_ctype == VOGL_VOID)
7716                 continue;
7717             if (!g_vogl_process_gl_ctypes[return_ctype].m_is_pointer)
7718                 continue;
7719             //if (g_vogl_process_gl_ctypes[return_ctype].m_is_opaque_pointer)
7720             //  continue;
7721
7722             if (g_vogl_entrypoint_descs[i].m_custom_return_param_array_size_macro_is_missing)
7723             {
7724                 vogl_debug_printf("%s, opaque_ptr: %u\n", g_vogl_entrypoint_descs[i].m_pName, g_vogl_process_gl_ctypes[return_ctype].m_is_opaque_pointer);
7725             }
7726
7727             if (g_vogl_entrypoint_descs[i].m_whitelisted_for_displaylists)
7728             {
7729                 if (!g_vogl_entrypoint_descs[i].m_is_listable)
7730                 {
7731                     vogl_debug_printf("Function %s is marked as whitelistable in display lists, but is not a listable function!\n", g_vogl_entrypoint_descs[i].m_pName);
7732                 }
7733             }
7734         }
7735         vogl_debug_printf("---\n");
7736
7737         vogl_debug_printf("Whitelisted funcs with undefined array size macros:\n");
7738         vogl_debug_printf("---\n");
7739         for (uint i = 0; i < VOGL_NUM_ENTRYPOINTS; i++)
7740         {
7741             const gl_entrypoint_desc_t &desc = g_vogl_entrypoint_descs[i];
7742             if ((!desc.m_is_whitelisted) || (desc.m_has_custom_func_handler))
7743                 continue;
7744
7745             if (desc.m_custom_array_size_macro_is_missing)
7746                 vogl_debug_printf("%s\n", desc.m_pName);
7747         }
7748         vogl_debug_printf("---\n");
7749
7750         vogl_debug_printf("Whitelisted funcs with undefined return param array size macros:\n");
7751         vogl_debug_printf("---\n");
7752         for (uint i = 0; i < VOGL_NUM_ENTRYPOINTS; i++)
7753         {
7754             const gl_entrypoint_desc_t &desc = g_vogl_entrypoint_descs[i];
7755             if (desc.m_is_whitelisted)
7756                 continue;
7757
7758             vogl_ctype_t return_ctype = g_vogl_entrypoint_descs[i].m_return_ctype;
7759
7760             if (return_ctype == VOGL_VOID)
7761                 continue;
7762             if (!g_vogl_process_gl_ctypes[return_ctype].m_is_pointer)
7763                 continue;
7764             if (g_vogl_process_gl_ctypes[return_ctype].m_is_opaque_pointer)
7765                 continue;
7766
7767             if (g_vogl_entrypoint_descs[i].m_custom_return_param_array_size_macro_is_missing)
7768             {
7769                 vogl_debug_printf("%s\n", g_vogl_entrypoint_descs[i].m_pName);
7770             }
7771         }
7772
7773         vogl_debug_printf("---\n");
7774     }
7775
7776     vogl_debug_printf("vogl_check_entrypoints: done\n");
7777 }
7778
7779 //----------------------------------------------------------------------------------------------------------------------
7780 // Include generated macros to define the internal entrypoint funcs
7781 //----------------------------------------------------------------------------------------------------------------------
7782 #include "gl_glx_array_size_macros.inc"
7783 #include "gl_glx_func_return_param_array_size_macros.inc"
7784 #include "gl_glx_func_defs.inc"
7785
7786 #ifndef NO_PUBLIC_EXPORTS
7787 //----------------------------------------------------------------------------------------------------------------------
7788 // Declare exported gl/glx functions (each exported func immediately calls one of the internal vogl_* functions)
7789 //----------------------------------------------------------------------------------------------------------------------
7790 #define DEF_PROTO_EXPORTED(ret, name, args, params) \
7791     VOGL_API_EXPORT ret name args                    \
7792     {                                               \
7793         return VOGL_GLUER(vogl_, name) params;     \
7794     }
7795 #define DEF_PROTO_EXPORTED_VOID(ret, name, args, params) \
7796     VOGL_API_EXPORT ret name args                         \
7797     {                                                    \
7798         VOGL_GLUER(vogl_, name) params;                 \
7799     }
7800 #define DEF_PROTO_INTERNAL(ret, name, args, params)
7801 #define DEF_PROTO_INTERNAL_VOID(ret, name, args, params)
7802 #define DEF_FUNCTION_BEGIN(exported, category, ret, ret_type_enum, num_params, name, args, params) exported(ret, name, args, params)
7803 #define DEF_FUNCTION_BEGIN_VOID(exported, category, ret, ret_type_enum, num_params, name, args, params) exported(ret, name, args, params)
7804 #define DEF_FUNCTION_INIT(exported, category, ret, ret_type_enum, num_params, name, args, params)
7805 #define DEF_FUNCTION_INIT_VOID(exported, category, ret, ret_type_enum, num_params, name, args, params)
7806 #define DEF_FUNCTION_INPUT_VALUE_PARAM(idx, spectype, type, type_enum, param)
7807 #define DEF_FUNCTION_INPUT_REFERENCE_PARAM(idx, spectype, type, type_enum, param)
7808 #define DEF_FUNCTION_INPUT_ARRAY_PARAM(idx, spectype, type, type_enum, param, size)
7809 #define DEF_FUNCTION_OUTPUT_REFERENCE_PARAM(idx, spectype, type, type_enum, param)
7810 #define DEF_FUNCTION_OUTPUT_ARRAY_PARAM(idx, spectype, type, type_enum, param, size)
7811 #define DEF_FUNCTION_RETURN_PARAM(spectype, type, type_enum, size)
7812 #define DEF_FUNCTION_CALL_GL(exported, category, ret, ret_type_enum, num_params, name, args, params)
7813 #define DEF_FUNCTION_CALL_GL_VOID(exported, category, ret, ret_type_enum, num_params, name, args, params)
7814 #define DEF_FUNCTION_END(exported, category, ret, ret_type_enum, num_params, name, args, params)
7815 #define DEF_FUNCTION_END_VOID(exported, category, ret, ret_type_enum, num_params, name, args, params)
7816 #define DEF_FUNCTION_PARAM_COMPUTE_ARRAY_SIZE_GL_ENUM(pname) -1
7817
7818 #include "gl_glx_array_size_macros.inc"
7819 #include "gl_glx_func_defs.inc"
7820 #endif
7821
7822 //----------------------------------------------------------------------------------------------------------------------
7823 // Define our exported gliGetProcAddressRAD function
7824 //----------------------------------------------------------------------------------------------------------------------
7825 VOGL_API_EXPORT __GLXextFuncPtr gliGetProcAddressRAD(const GLubyte *procName)
7826 {
7827     if ((procName) && (!vogl_strcmp(reinterpret_cast<const char *>(procName), "gliGetProcAddressRAD")))
7828         return (__GLXextFuncPtr)gliGetProcAddressRAD;
7829
7830     return vogl_glXGetProcAddressARB(procName);
7831 }
7832
7833 //----------------------------------------------------------------------------------------------------------------------
7834 // Determine addresses of our gl/glx wrapper functions
7835 //----------------------------------------------------------------------------------------------------------------------
7836 static void vogl_init_wrapper_func_ptrs()
7837 {
7838     gl_entrypoint_desc_t *pDst = g_vogl_entrypoint_descs;
7839
7840 #define DEF_PROTO(exported, category, ret, ret_type, num_params, name, args, params) \
7841     pDst->m_pWrapper_func = reinterpret_cast<vogl_void_func_ptr_t>(vogl_##name);       \
7842     ++pDst;
7843 #define DEF_PROTO_VOID(exported, category, ret, ret_type, num_params, name, args, params) \
7844     pDst->m_pWrapper_func = reinterpret_cast<vogl_void_func_ptr_t>(vogl_##name);            \
7845     ++pDst;
7846 #include "gl_glx_protos.inc"
7847 #undef DEF_PROTO
7848 #undef DEF_PROTO_VOID
7849 }
7850
7851 //----------------------------------------------------------------------------------------------------------------------
7852 // vogl_direct_gl_func_prolog - This function is called before EVERY single GL/GLX function call we make.
7853 //----------------------------------------------------------------------------------------------------------------------
7854 static void vogl_direct_gl_func_prolog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **ppStack_data)
7855 {
7856     VOGL_NOTE_UNUSED(entrypoint_id);
7857     VOGL_NOTE_UNUSED(pUser_data);
7858
7859     if (g_dump_gl_calls_flag)
7860         printf("** GLPROLOG %s\n", g_vogl_entrypoint_descs[entrypoint_id].m_pName);
7861
7862     gl_entrypoint_id_t *pPrev_state = reinterpret_cast<gl_entrypoint_id_t *>(ppStack_data);
7863     *pPrev_state = VOGL_ENTRYPOINT_INVALID;
7864
7865     vogl_thread_local_data *pTLS_data = vogl_get_or_create_thread_local_data();
7866     if (pTLS_data)
7867     {
7868         *pPrev_state = pTLS_data->m_calling_driver_entrypoint_id;
7869         pTLS_data->m_calling_driver_entrypoint_id = entrypoint_id;
7870     }
7871 }
7872
7873 //----------------------------------------------------------------------------------------------------------------------
7874 // vogl_direct_gl_func_epilog - This function is called immediately after EVERY single GL/GLX function call we make.
7875 //----------------------------------------------------------------------------------------------------------------------
7876 static void vogl_direct_gl_func_epilog(gl_entrypoint_id_t entrypoint_id, void *pUser_data, void **ppStack_data)
7877 {
7878     VOGL_NOTE_UNUSED(entrypoint_id);
7879     VOGL_NOTE_UNUSED(pUser_data);
7880
7881 #if 0
7882     if (entrypoint_id == VOGL_ENTRYPOINT_glXMakeCurrent)
7883         {
7884                 // HACK HACK - crude test of the "calling driver entrypoint code"
7885                 glXGetCurrentContext();
7886         }
7887 #endif
7888
7889     if (g_dump_gl_calls_flag)
7890         printf("** GLEPILOG %s\n", g_vogl_entrypoint_descs[entrypoint_id].m_pName);
7891
7892     gl_entrypoint_id_t *pPrev_state = reinterpret_cast<gl_entrypoint_id_t *>(ppStack_data);
7893
7894     vogl_thread_local_data *pTLS_data = vogl_get_or_create_thread_local_data();
7895     if (pTLS_data)
7896     {
7897         pTLS_data->m_calling_driver_entrypoint_id = *pPrev_state;
7898     }
7899 }
7900
7901 //----------------------------------------------------------------------------------------------------------------------
7902 // Initialization
7903 // Note: Be VERY careful what you do in here! It's called very early during init (long before main, during c++ init)
7904 //----------------------------------------------------------------------------------------------------------------------
7905 void vogl_early_init()
7906 {
7907     vogl_init_thread_local_data();
7908
7909     vogl_set_direct_gl_func_prolog(vogl_direct_gl_func_prolog, NULL);
7910     vogl_set_direct_gl_func_epilog(vogl_direct_gl_func_epilog, NULL);
7911
7912     vogl_init_wrapper_func_ptrs();
7913
7914     vogl_check_entrypoints();
7915 }