]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_program_state.cpp
Initial vogl checkin
[vogl] / src / voglcommon / vogl_program_state.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_program_state.cpp
27 // TODO: Remap uniform block locations
28
29 #include "vogl_program_state.h"
30
31 #define VOGL_PROGRAM_VERSION 0x0100
32
33 static bool get_program_bool(GLuint handle, GLenum pname)
34 {
35     VOGL_FUNC_TRACER
36
37     GLint val = false;
38     GL_ENTRYPOINT(glGetProgramiv)(handle, pname, &val);
39     VOGL_CHECK_GL_ERROR;
40     return val != 0;
41 }
42
43 static int get_program_int(GLuint handle, GLenum pname)
44 {
45     VOGL_FUNC_TRACER
46
47     GLint val = false;
48     GL_ENTRYPOINT(glGetProgramiv)(handle, pname, &val);
49     VOGL_CHECK_GL_ERROR;
50     return val;
51 }
52
53 vogl_program_state::vogl_program_state()
54     : m_snapshot_handle(0),
55       m_program_binary_format(GL_NONE),
56       m_num_active_attribs(0),
57       m_num_active_uniforms(0),
58       m_num_active_uniform_blocks(0),
59       m_transform_feedback_mode(GL_NONE),
60       m_transform_feedback_num_varyings(0),
61       m_marked_for_deletion(false),
62       m_link_status(false),
63       m_verify_status(false),
64       m_link_snapshot(false),
65       m_is_valid(false)
66 {
67     VOGL_FUNC_TRACER
68 }
69
70 vogl_program_state::vogl_program_state(const vogl_program_state &other)
71     : m_snapshot_handle(0),
72       m_program_binary_format(GL_NONE),
73       m_num_active_attribs(0),
74       m_num_active_uniforms(0),
75       m_num_active_uniform_blocks(0),
76       m_transform_feedback_mode(GL_NONE),
77       m_transform_feedback_num_varyings(0),
78       m_marked_for_deletion(false),
79       m_link_status(false),
80       m_verify_status(false),
81       m_link_snapshot(false),
82       m_is_valid(false)
83 {
84     VOGL_FUNC_TRACER
85     *this = other;
86 }
87
88 vogl_program_state::~vogl_program_state()
89 {
90     VOGL_FUNC_TRACER
91 }
92
93 vogl_program_state &vogl_program_state::operator=(const vogl_program_state &rhs)
94 {
95     VOGL_FUNC_TRACER
96
97     if (this == &rhs)
98         return *this;
99
100     clear();
101
102 #define CPY(x) x = rhs.x;
103
104     CPY(m_snapshot_handle);
105
106     CPY(m_program_binary);
107     CPY(m_program_binary_format);
108
109     CPY(m_attached_shaders);
110
111     CPY(m_info_log);
112
113     CPY(m_num_active_attribs);
114     CPY(m_num_active_uniforms);
115     CPY(m_num_active_uniform_blocks);
116
117     CPY(m_attribs);
118     CPY(m_uniforms);
119     CPY(m_uniform_blocks);
120
121     CPY(m_shaders);
122     CPY(m_outputs);
123
124     CPY(m_transform_feedback_mode);
125     CPY(m_transform_feedback_num_varyings);
126     CPY(m_varyings);
127
128     CPY(m_marked_for_deletion);
129     CPY(m_link_status);
130     CPY(m_verify_status);
131     CPY(m_link_snapshot);
132
133     CPY(m_is_valid);
134
135 #undef CPY
136
137     if (rhs.m_pLink_time_snapshot.get())
138     {
139         m_pLink_time_snapshot.reset(vogl_new(vogl_program_state, *(rhs.m_pLink_time_snapshot.get())));
140     }
141
142     return *this;
143 }
144
145 bool vogl_program_state::snapshot_uniforms(const vogl_context_info &context_info, vogl_handle_remapper &remapper)
146 {
147     VOGL_FUNC_TRACER
148
149     VOGL_NOTE_UNUSED(context_info);
150     VOGL_NOTE_UNUSED(remapper);
151
152     growable_array<GLchar, 4096> temp_buf;
153
154     // Snapshot active uniforms
155     GLint active_uniform_max_length = math::maximum(1024, get_program_int(m_snapshot_handle, GL_ACTIVE_UNIFORM_MAX_LENGTH));
156     temp_buf.resize(active_uniform_max_length);
157     m_uniforms.resize(m_num_active_uniforms);
158     for (uint uniform_iter = 0; uniform_iter < m_num_active_uniforms; uniform_iter++)
159     {
160         GLsizei actual_len = 0;
161         GLint size = 0;
162         GLenum type = GL_NONE;
163
164         GL_ENTRYPOINT(glGetActiveUniform)(m_snapshot_handle, uniform_iter, active_uniform_max_length, &actual_len, &size, &type, temp_buf.get_ptr());
165         VOGL_CHECK_GL_ERROR;
166
167         vogl_program_uniform_state &uniform = m_uniforms[uniform_iter];
168         uniform.m_size = size;
169         uniform.m_type = type;
170         uniform.m_name.set(reinterpret_cast<char *>(temp_buf.get_ptr()));
171         uniform.m_base_location = -1;
172
173         if (!uniform.m_name.begins_with("gl_", true))
174         {
175             uniform.m_base_location = GL_ENTRYPOINT(glGetUniformLocation)(m_snapshot_handle, temp_buf.get_ptr());
176             VOGL_CHECK_GL_ERROR;
177         }
178
179         uint type_size_in_bytes = vogl_gl_get_uniform_size_in_bytes(type);
180         if (!type_size_in_bytes)
181             continue;
182
183         GLenum base_type = vogl_gl_get_uniform_base_type(type);
184         if (base_type == GL_NONE)
185             continue;
186
187         uniform.m_data.resize(size * type_size_in_bytes);
188
189         if (uniform.m_base_location != -1)
190         {
191             for (int element = 0; element < size; element++)
192             {
193                 uint8 *pDst = &uniform.m_data[element * type_size_in_bytes];
194
195                 if (base_type == GL_FLOAT)
196                     GL_ENTRYPOINT(glGetUniformfv)(m_snapshot_handle, uniform.m_base_location + element, (GLfloat *)pDst);
197                 else if (base_type == GL_DOUBLE)
198                     GL_ENTRYPOINT(glGetUniformdv)(m_snapshot_handle, uniform.m_base_location + element, (GLdouble *)pDst);
199                 else if (base_type == GL_UNSIGNED_INT)
200                     GL_ENTRYPOINT(glGetUniformuiv)(m_snapshot_handle, uniform.m_base_location + element, (GLuint *)pDst);
201                 else
202                     GL_ENTRYPOINT(glGetUniformiv)(m_snapshot_handle, uniform.m_base_location + element, (GLint *)pDst);
203
204                 VOGL_CHECK_GL_ERROR;
205             }
206         }
207     }
208
209     return true;
210 }
211
212 bool vogl_program_state::snapshot_uniform_blocks(const vogl_context_info &context_info, vogl_handle_remapper &remapper)
213 {
214     VOGL_FUNC_TRACER
215
216     VOGL_NOTE_UNUSED(remapper);
217
218     // Snapshot active uniform blocks
219     if ((context_info.get_version() >= VOGL_GL_VERSION_3_1) && GL_ENTRYPOINT(glGetActiveUniformBlockiv) && GL_ENTRYPOINT(glGetActiveUniformBlockName))
220     {
221         growable_array<char, 256> name_buf;
222
223         m_uniform_blocks.reserve(m_num_active_uniform_blocks);
224
225         for (uint uniform_block_index = 0; uniform_block_index < m_num_active_uniform_blocks; uniform_block_index++)
226         {
227             GLint name_len = 0;
228             GL_ENTRYPOINT(glGetActiveUniformBlockiv)(m_snapshot_handle, uniform_block_index, GL_UNIFORM_BLOCK_NAME_LENGTH, &name_len);
229             VOGL_CHECK_GL_ERROR;
230
231             vogl_program_uniform_block_state state;
232             state.clear();
233             state.m_uniform_block_index = uniform_block_index;
234
235             name_buf.resize(name_len);
236
237             GLsizei actual_len = 0;
238             GL_ENTRYPOINT(glGetActiveUniformBlockName)(m_snapshot_handle, uniform_block_index, name_len, &actual_len, reinterpret_cast<GLchar *>(name_buf.get_ptr()));
239             VOGL_CHECK_GL_ERROR;
240
241             if (name_buf.size())
242                 state.m_name.set(name_buf.get_ptr());
243
244             GL_ENTRYPOINT(glGetActiveUniformBlockiv)(m_snapshot_handle, uniform_block_index, GL_UNIFORM_BLOCK_BINDING, &state.m_uniform_block_binding_point);
245             VOGL_CHECK_GL_ERROR;
246
247             GL_ENTRYPOINT(glGetActiveUniformBlockiv)(m_snapshot_handle, uniform_block_index, GL_UNIFORM_BLOCK_DATA_SIZE, &state.m_uniform_block_data_size);
248             VOGL_CHECK_GL_ERROR;
249
250             GL_ENTRYPOINT(glGetActiveUniformBlockiv)(m_snapshot_handle, uniform_block_index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &state.m_uniform_block_active_uniforms);
251             VOGL_CHECK_GL_ERROR;
252
253             // TODO: Get and store referenced flags: GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER, or GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER, etc.
254
255             m_uniform_blocks.push_back(state);
256         }
257     }
258
259     return true;
260 }
261
262 bool vogl_program_state::snapshot_active_attribs(const vogl_context_info &context_info, vogl_handle_remapper &remapper)
263 {
264     VOGL_FUNC_TRACER
265
266     VOGL_NOTE_UNUSED(context_info);
267     VOGL_NOTE_UNUSED(remapper);
268
269     growable_array<GLchar, 4096> temp_buf;
270
271     // Snapshot active attribs
272     GLint active_attrib_max_length = math::maximum(1024, get_program_int(m_snapshot_handle, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH));
273     temp_buf.resize(active_attrib_max_length);
274
275     m_attribs.resize(m_num_active_attribs);
276     for (uint attrib_iter = 0; attrib_iter < m_num_active_attribs; attrib_iter++)
277     {
278         GLint size = 0;
279         GLenum type = GL_NONE;
280         GLsizei actual_len = 0;
281
282         GL_ENTRYPOINT(glGetActiveAttrib)(m_snapshot_handle, attrib_iter, active_attrib_max_length, &actual_len, &size, &type, temp_buf.get_ptr());
283         VOGL_CHECK_GL_ERROR;
284
285         vogl_program_attrib_state &attrib = m_attribs[attrib_iter];
286         attrib.m_name.set(reinterpret_cast<char *>(temp_buf.get_ptr()));
287
288         GLint loc = -1;
289         if (!attrib.m_name.begins_with("gl_", true))
290         {
291             loc = GL_ENTRYPOINT(glGetAttribLocation)(m_snapshot_handle, temp_buf.get_ptr());
292             VOGL_CHECK_GL_ERROR;
293         }
294         attrib.m_bound_location = loc;
295
296         attrib.m_size = size;
297         attrib.m_type = type;
298     }
299
300     return true;
301 }
302
303 bool vogl_program_state::snapshot_attached_shaders(const vogl_context_info &context_info, vogl_handle_remapper &remapper, bool linked_using_binary)
304 {
305     VOGL_FUNC_TRACER
306
307     VOGL_NOTE_UNUSED(context_info);
308     VOGL_NOTE_UNUSED(remapper);
309
310     GLint num_attached_shaders = get_program_int(m_snapshot_handle, GL_ATTACHED_SHADERS);
311     if (num_attached_shaders)
312     {
313         m_attached_shaders.resize(num_attached_shaders);
314
315         GLsizei actual_count = 0;
316         GL_ENTRYPOINT(glGetAttachedShaders)(m_snapshot_handle, num_attached_shaders, &actual_count, m_attached_shaders.get_ptr());
317         VOGL_CHECK_GL_ERROR;
318
319         VOGL_ASSERT(static_cast<uint>(actual_count) == m_attached_shaders.size());
320
321         // We can't trust glIsShader(), we must check our own shadow to determine if these are our handles or not.
322         attached_shader_vec actual_attached_shaders;
323
324         for (GLsizei i = 0; i < actual_count; i++)
325         {
326             if (remapper.is_valid_handle(VOGL_NAMESPACE_SHADERS, m_attached_shaders[i]))
327                 actual_attached_shaders.push_back(m_attached_shaders[i]);
328             else if (!linked_using_binary)
329                 vogl_warning_printf("%s: GL shader %u attached to GL program %u cannot be found in our object shadow\n", VOGL_METHOD_NAME, m_attached_shaders[i], m_snapshot_handle);
330         }
331
332         m_attached_shaders.swap(actual_attached_shaders);
333         num_attached_shaders = m_attached_shaders.size();
334     }
335
336     return true;
337 }
338
339 bool vogl_program_state::snapshot_shader_objects(const vogl_context_info &context_info, vogl_handle_remapper &remapper)
340 {
341     VOGL_FUNC_TRACER
342
343     m_shaders.resize(m_attached_shaders.size());
344     for (uint i = 0; i < m_shaders.size(); i++)
345     {
346         GLuint handle = m_attached_shaders[i];
347
348         if (!m_shaders[i].snapshot(context_info, remapper, handle, GL_NONE))
349             return false;
350     }
351
352     return true;
353 }
354
355 bool vogl_program_state::snapshot_info_log(const vogl_context_info &context_info, vogl_handle_remapper &remapper)
356 {
357     VOGL_FUNC_TRACER
358
359     VOGL_NOTE_UNUSED(context_info);
360     VOGL_NOTE_UNUSED(remapper);
361
362     GLint info_log_len = get_program_int(m_snapshot_handle, GL_INFO_LOG_LENGTH);
363
364     growable_array<GLchar, 4096> temp_buf(info_log_len);
365
366     // Snapshot program info log
367     if (info_log_len)
368     {
369         GLsizei actual_len = 0;
370         GL_ENTRYPOINT(glGetProgramInfoLog)(m_snapshot_handle, info_log_len, &actual_len, temp_buf.get_ptr());
371
372         m_info_log.set(reinterpret_cast<char *>(temp_buf.get_ptr()));
373     }
374
375     return true;
376 }
377
378 bool vogl_program_state::snapshot_program_binary(const vogl_context_info &context_info, vogl_handle_remapper &remapper)
379 {
380     VOGL_FUNC_TRACER
381
382     VOGL_NOTE_UNUSED(context_info);
383     VOGL_NOTE_UNUSED(remapper);
384
385 // HACK HACK - this crashes AMD's driver in Sam3 in interactive mode when we try to get the program binary's length, no idea why yet
386 #if 0
387         if (m_link_status)
388         {
389                 int retrievable_hint = get_program_int(m_snapshot_handle, GL_PROGRAM_BINARY_RETRIEVABLE_HINT);
390                 if (retrievable_hint)
391                 {
392                         int program_binary_length = get_program_int(m_snapshot_handle, GL_PROGRAM_BINARY_LENGTH);
393
394                         if (!vogl_check_gl_error() && program_binary_length)
395                         {
396                                 m_program_binary.resize(program_binary_length);
397
398                                 GLsizei actual_len = 0;
399                                 GL_ENTRYPOINT(glGetProgramBinary)(m_snapshot_handle, program_binary_length, &actual_len, &m_program_binary_format, m_program_binary.get_ptr());
400
401                                 if (!vogl_check_gl_error())
402                                 {
403                                         m_program_binary.resize(actual_len);
404                                 }
405                                 else
406                                 {
407                                         vogl_error_printf("%s: Failed retrieving program binary for GL program %u!\n", VOGL_METHOD_NAME, m_snapshot_handle);
408
409                                         m_program_binary.clear();
410                                         m_program_binary_format = GL_NONE;
411                                 }
412                         }
413                 }
414         }
415 #endif
416
417     return true;
418 }
419
420 bool vogl_program_state::snapshot_basic_info(const vogl_context_info &context_info, vogl_handle_remapper &remapper)
421 {
422     VOGL_FUNC_TRACER
423
424     VOGL_NOTE_UNUSED(remapper);
425
426     m_link_status = get_program_bool(m_snapshot_handle, GL_LINK_STATUS);
427     m_marked_for_deletion = get_program_bool(m_snapshot_handle, GL_DELETE_STATUS);
428     m_verify_status = get_program_bool(m_snapshot_handle, GL_VALIDATE_STATUS);
429     m_num_active_attribs = get_program_int(m_snapshot_handle, GL_ACTIVE_ATTRIBUTES);
430     m_num_active_uniforms = get_program_int(m_snapshot_handle, GL_ACTIVE_UNIFORMS);
431
432     if ((context_info.get_version() >= VOGL_GL_VERSION_3_1) && GL_ENTRYPOINT(glGetActiveUniformBlockiv))
433         m_num_active_uniform_blocks = get_program_int(m_snapshot_handle, GL_ACTIVE_UNIFORM_BLOCKS);
434
435     return true;
436 }
437
438 bool vogl_program_state::snapshot_outputs(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint program)
439 {
440     VOGL_FUNC_TRACER
441     VOGL_NOTE_UNUSED(remapper);
442
443     if ((!context_info.supports_extension("GL_ARB_program_interface_query")) ||
444         (!GL_ENTRYPOINT(glGetProgramInterfaceiv)) || (!GL_ENTRYPOINT(glGetProgramResourceName)) || (!GL_ENTRYPOINT(glGetProgramResourceiv)))
445     {
446         return true;
447     }
448
449     GLint num_active_outputs = 0;
450     GL_ENTRYPOINT(glGetProgramInterfaceiv)(program, GL_PROGRAM_OUTPUT, GL_ACTIVE_RESOURCES, &num_active_outputs);
451     VOGL_CHECK_GL_ERROR;
452
453     m_outputs.resize(num_active_outputs);
454     for (int i = 0; i < num_active_outputs; i++)
455     {
456         vogl_program_output_state &output = m_outputs[i];
457
458         output.clear();
459
460         GLchar name[256];
461         GLsizei name_len = 0;
462         GL_ENTRYPOINT(glGetProgramResourceName)(program, GL_PROGRAM_OUTPUT, i, sizeof(name), &name_len, name);
463         VOGL_CHECK_GL_ERROR;
464
465         const GLenum props_to_query[5] = { GL_LOCATION, GL_LOCATION_INDEX, GL_TYPE, GL_ARRAY_SIZE, GL_IS_PER_PATCH }; // TODO: GL_LOCATION_COMPONENT
466         GLint props[5] = { 0, 0, 0, 0, 0 };
467         GL_ENTRYPOINT(glGetProgramResourceiv)(program, GL_PROGRAM_OUTPUT, i, VOGL_ARRAY_SIZE(props_to_query), props_to_query, VOGL_ARRAY_SIZE(props), NULL, props);
468         VOGL_CHECK_GL_ERROR;
469
470         output.m_name = reinterpret_cast<const char *>(name);
471         output.m_location = props[0];
472         output.m_location_index = props[1];
473         output.m_type = props[2];
474         output.m_array_size = props[3];
475         output.m_is_per_patch = props[4] != 0;
476     }
477
478     return true;
479 }
480
481 bool vogl_program_state::snapshot_transform_feedback(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint program)
482 {
483     VOGL_FUNC_TRACER
484     VOGL_NOTE_UNUSED(remapper);
485     VOGL_NOTE_UNUSED(context_info);
486
487     // The latest/current mode
488     GL_ENTRYPOINT(glGetProgramiv)(program, GL_TRANSFORM_FEEDBACK_BUFFER_MODE, reinterpret_cast<GLint *>(&m_transform_feedback_mode));
489     VOGL_CHECK_GL_ERROR;
490
491     // This is the linked state, NOT the current state
492     GL_ENTRYPOINT(glGetProgramiv)(program, GL_TRANSFORM_FEEDBACK_VARYINGS, reinterpret_cast<GLint *>(&m_transform_feedback_num_varyings));
493     VOGL_CHECK_GL_ERROR;
494
495     m_varyings.resize(m_transform_feedback_num_varyings);
496
497     for (uint i = 0; i < m_transform_feedback_num_varyings; i++)
498     {
499         GLchar name[512] = { '\0' };
500         GLsizei length = 0, size = 0;
501         GLenum type = GL_NONE;
502
503         // This is the linked state, NOT the current state
504         GL_ENTRYPOINT(glGetTransformFeedbackVarying)(program, i, sizeof(name), &length, &size, &type, name);
505         VOGL_CHECK_GL_ERROR;
506
507         vogl_program_transform_feedback_varying &varying = m_varyings[i];
508         varying.clear();
509
510         varying.m_index = i;
511         varying.m_name.set(reinterpret_cast<const char *>(name));
512         varying.m_size = size;
513         varying.m_type = type;
514     }
515
516     return true;
517 }
518
519 bool vogl_program_state::snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 handle, GLenum target)
520 {
521     VOGL_FUNC_TRACER
522
523     VOGL_NOTE_UNUSED(context_info);
524     VOGL_NOTE_UNUSED(target);
525
526     VOGL_CHECK_GL_ERROR;
527
528     clear();
529
530     VOGL_ASSERT(handle <= cUINT32_MAX);
531
532     m_snapshot_handle = static_cast<GLuint>(handle);
533
534     if (!snapshot_basic_info(context_info, remapper))
535     {
536         clear();
537         return false;
538     }
539
540     if (!snapshot_outputs(context_info, remapper, m_snapshot_handle))
541     {
542         clear();
543         return false;
544     }
545
546     if (!snapshot_program_binary(context_info, remapper))
547     {
548         clear();
549         return false;
550     }
551
552     if (!snapshot_info_log(context_info, remapper))
553     {
554         clear();
555         return false;
556     }
557
558     if (!snapshot_attached_shaders(context_info, remapper, false))
559     {
560         clear();
561         return false;
562     }
563
564     if (!snapshot_active_attribs(context_info, remapper))
565     {
566         clear();
567         return false;
568     }
569
570     if (!snapshot_uniforms(context_info, remapper))
571     {
572         clear();
573         return false;
574     }
575
576     if (!snapshot_uniform_blocks(context_info, remapper))
577     {
578         clear();
579         return false;
580     }
581
582     // We'll snapshot this stuff, although some of it is linked state.
583     if (!snapshot_transform_feedback(context_info, remapper, m_snapshot_handle))
584     {
585         clear();
586         return false;
587     }
588
589     m_link_snapshot = false;
590     m_is_valid = true;
591
592     return true;
593 }
594
595 bool vogl_program_state::link_snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint handle, const void *pBinary, uint binary_size, GLenum binary_format)
596 {
597     VOGL_FUNC_TRACER
598
599     VOGL_CHECK_GL_ERROR;
600
601     clear();
602
603     m_snapshot_handle = handle;
604
605     bool linked_using_binary = pBinary && binary_size;
606
607     if (!snapshot_basic_info(context_info, remapper))
608     {
609         clear();
610         return false;
611     }
612
613     if (!snapshot_outputs(context_info, remapper, handle))
614     {
615         clear();
616         return false;
617     }
618
619     if (linked_using_binary)
620         m_program_binary.append(static_cast<const uint8 *>(pBinary), binary_size);
621
622     m_program_binary_format = binary_format;
623
624     if (!snapshot_info_log(context_info, remapper))
625     {
626         clear();
627         return false;
628     }
629
630     if (!snapshot_active_attribs(context_info, remapper))
631     {
632         clear();
633         return false;
634     }
635
636     if (!snapshot_attached_shaders(context_info, remapper, linked_using_binary))
637     {
638         clear();
639         return false;
640     }
641
642     if (!snapshot_shader_objects(context_info, remapper))
643     {
644         clear();
645         return false;
646     }
647
648     if (!linked_using_binary)
649     {
650         if (!snapshot_program_binary(context_info, remapper))
651         {
652             clear();
653             return false;
654         }
655     }
656
657     if (!snapshot_transform_feedback(context_info, remapper, handle))
658     {
659         clear();
660         return false;
661     }
662
663     if ((m_link_status) && (!m_attached_shaders.size()) && (!linked_using_binary))
664     {
665         vogl_error_printf("%s: Program %u was successfully linked, but there are no attached shaders!\n", VOGL_METHOD_NAME, m_snapshot_handle);
666     }
667
668     // We don't care about the attached shader handlers, we've now snapshotted and copied the ACTUAL shaders.
669     m_attached_shaders.clear();
670
671     m_link_snapshot = true;
672     m_is_valid = true;
673
674     return true;
675 }
676
677 void vogl_program_state::set_link_time_snapshot(vogl_unique_ptr<vogl_program_state> &pSnapshot)
678 {
679     VOGL_FUNC_TRACER
680
681     if (pSnapshot.get() && !pSnapshot->is_valid())
682     {
683         VOGL_ASSERT_ALWAYS;
684         return;
685     }
686
687     m_pLink_time_snapshot = pSnapshot;
688 }
689
690 bool vogl_program_state::restore_uniforms(uint32 handle32, const vogl_context_info &context_info, vogl_handle_remapper &remapper, bool &any_restore_warnings, bool &any_gl_errors) const
691 {
692     VOGL_FUNC_TRACER
693
694     VOGL_NOTE_UNUSED(context_info);
695
696     const GLint num_active_uniforms = get_program_int(handle32, GL_ACTIVE_UNIFORMS);
697
698     const GLint active_uniform_max_length = math::maximum(1024, get_program_int(handle32, GL_ACTIVE_UNIFORM_MAX_LENGTH));
699
700     growable_array<GLchar, 1024> temp_buf(active_uniform_max_length);
701
702     vogl_uniform_state_vec uniforms_to_restore(num_active_uniforms);
703
704     // Restore active uniforms
705     for (int uniform_iter = 0; uniform_iter < num_active_uniforms; uniform_iter++)
706     {
707         GLsizei actual_len = 0;
708         GLint size = 0;
709         GLenum type = GL_NONE;
710
711         GL_ENTRYPOINT(glGetActiveUniform)(handle32, uniform_iter, temp_buf.size(), &actual_len, &size, &type, temp_buf.get_ptr());
712         if (vogl_check_gl_error())
713             any_gl_errors = true;
714
715         VOGL_ASSERT(actual_len <= static_cast<GLsizei>(temp_buf.size()));
716
717         vogl_program_uniform_state &uniform = uniforms_to_restore[uniform_iter];
718         uniform.m_size = size;
719         uniform.m_type = type;
720         uniform.m_name.set(reinterpret_cast<char *>(temp_buf.get_ptr()));
721         uniform.m_base_location = -1;
722
723         if (!uniform.m_name.begins_with("gl_", true))
724         {
725             uniform.m_base_location = GL_ENTRYPOINT(glGetUniformLocation)(handle32, temp_buf.get_ptr());
726             if (vogl_check_gl_error())
727                 any_gl_errors = true;
728         }
729     } // uniform_iter
730
731     for (uint uniform_iter = 0; uniform_iter < m_uniforms.size(); uniform_iter++)
732     {
733         const vogl_program_uniform_state &trace_uniform = m_uniforms[uniform_iter];
734         if ((trace_uniform.m_name.begins_with("_gl", true)) || (trace_uniform.m_base_location < 0) || (!trace_uniform.m_data.size()))
735             continue;
736
737         uint restore_uniform_index;
738         for (restore_uniform_index = 0; restore_uniform_index < uniforms_to_restore.size(); restore_uniform_index++)
739             if (uniforms_to_restore[restore_uniform_index].m_name.compare(trace_uniform.m_name, true) == 0)
740                 break;
741
742         if (restore_uniform_index == uniforms_to_restore.size())
743         {
744             vogl_warning_printf("%s: Failed finding trace uniform \"%s\", trace program %u GL program %u\n", VOGL_METHOD_NAME,
745                                trace_uniform.m_name.get_ptr(), m_snapshot_handle, handle32);
746             any_restore_warnings = true;
747             continue;
748         }
749
750         const vogl_program_uniform_state &restore_uniform = uniforms_to_restore[restore_uniform_index];
751
752         if (restore_uniform.m_base_location < 0)
753         {
754             vogl_warning_printf("%s: Trace uniform \"%s\"'s type %s was found in the restored program but the restore handle is invalid, trace program %u GL program %u.\n", VOGL_METHOD_NAME,
755                                trace_uniform.m_name.get_ptr(), g_gl_enums.find_gl_name(trace_uniform.m_type), m_snapshot_handle, handle32);
756             any_restore_warnings = true;
757             continue;
758         }
759
760         if (restore_uniform.m_type != trace_uniform.m_type)
761         {
762             vogl_warning_printf("%s: Trace uniform \"%s\"'s type (%s) is different from restored trace program type (%s), trace program %u GL program %u. Uniform type conversion is not yet supported, skipping uniform!\n", VOGL_METHOD_NAME,
763                                trace_uniform.m_name.get_ptr(), g_gl_enums.find_gl_name(trace_uniform.m_type), g_gl_enums.find_gl_name(restore_uniform.m_type),
764                                m_snapshot_handle, handle32);
765             any_restore_warnings = true;
766             continue;
767         }
768
769         if (restore_uniform.m_size != trace_uniform.m_size)
770         {
771             vogl_warning_printf("%s: Trace uniform \"%s\" type %s array size (%u) is different from restored program's array size (%u), trace program %u GL program %u. Will set as many array entries as possible and hope for the best.\n", VOGL_METHOD_NAME,
772                                trace_uniform.m_name.get_ptr(), g_gl_enums.find_gl_name(trace_uniform.m_type), trace_uniform.m_size, restore_uniform.m_size,
773                                m_snapshot_handle, handle32);
774             any_restore_warnings = true;
775         }
776
777         const uint array_size = math::minimum<uint>(restore_uniform.m_size, trace_uniform.m_size);
778         const void *pTrace_data = trace_uniform.m_data.get_ptr();
779         const GLint restore_location = restore_uniform.m_base_location;
780
781         for (uint i = 0; i < array_size; i++)
782             remapper.declare_location(m_snapshot_handle, handle32, restore_uniform.m_base_location + i, restore_location + i);
783
784         if (array_size)
785         {
786             switch (restore_uniform.m_type)
787             {
788                 case GL_DOUBLE:
789                 {
790                     GL_ENTRYPOINT(glUniform1dv)(restore_location, array_size, static_cast<const GLdouble *>(pTrace_data));
791                     break;
792                 }
793                 case GL_DOUBLE_VEC2:
794                 {
795                     GL_ENTRYPOINT(glUniform2dv)(restore_location, array_size, static_cast<const GLdouble *>(pTrace_data));
796                     break;
797                 }
798                 case GL_DOUBLE_VEC3:
799                 {
800                     GL_ENTRYPOINT(glUniform3dv)(restore_location, array_size, static_cast<const GLdouble *>(pTrace_data));
801                     break;
802                 }
803                 case GL_DOUBLE_VEC4:
804                 {
805                     GL_ENTRYPOINT(glUniform4dv)(restore_location, array_size, static_cast<const GLdouble *>(pTrace_data));
806                     break;
807                 }
808                 case GL_DOUBLE_MAT2:
809                 {
810                     GL_ENTRYPOINT(glUniformMatrix2dv)(restore_location, array_size, GL_FALSE, static_cast<const GLdouble *>(pTrace_data));
811                     break;
812                 }
813                 case GL_DOUBLE_MAT3:
814                 {
815                     GL_ENTRYPOINT(glUniformMatrix3dv)(restore_location, array_size, GL_FALSE, static_cast<const GLdouble *>(pTrace_data));
816                     break;
817                 }
818                 case GL_DOUBLE_MAT4:
819                 {
820                     GL_ENTRYPOINT(glUniformMatrix4dv)(restore_location, array_size, GL_FALSE, static_cast<const GLdouble *>(pTrace_data));
821                     break;
822                 }
823                 case GL_DOUBLE_MAT2x3:
824                 {
825                     GL_ENTRYPOINT(glUniformMatrix2x3dv)(restore_location, array_size, GL_FALSE, static_cast<const GLdouble *>(pTrace_data));
826                     break;
827                 }
828                 case GL_DOUBLE_MAT3x2:
829                 {
830                     GL_ENTRYPOINT(glUniformMatrix3x2dv)(restore_location, array_size, GL_FALSE, static_cast<const GLdouble *>(pTrace_data));
831                     break;
832                 }
833                 case GL_DOUBLE_MAT2x4:
834                 {
835                     GL_ENTRYPOINT(glUniformMatrix2x4dv)(restore_location, array_size, GL_FALSE, static_cast<const GLdouble *>(pTrace_data));
836                     break;
837                 }
838                 case GL_DOUBLE_MAT4x2:
839                 {
840                     GL_ENTRYPOINT(glUniformMatrix4x2dv)(restore_location, array_size, GL_FALSE, static_cast<const GLdouble *>(pTrace_data));
841                     break;
842                 }
843                 case GL_DOUBLE_MAT3x4:
844                 {
845                     GL_ENTRYPOINT(glUniformMatrix3x4dv)(restore_location, array_size, GL_FALSE, static_cast<const GLdouble *>(pTrace_data));
846                     break;
847                 }
848                 case GL_DOUBLE_MAT4x3:
849                 {
850                     GL_ENTRYPOINT(glUniformMatrix4x3dv)(restore_location, array_size, GL_FALSE, static_cast<const GLdouble *>(pTrace_data));
851                     break;
852                 }
853
854                 case GL_FLOAT:
855                 {
856                     GL_ENTRYPOINT(glUniform1fv)(restore_location, array_size, static_cast<const GLfloat *>(pTrace_data));
857                     break;
858                 }
859                 case GL_FLOAT_VEC2:
860                 {
861                     GL_ENTRYPOINT(glUniform2fv)(restore_location, array_size, static_cast<const GLfloat *>(pTrace_data));
862                     break;
863                 }
864                 case GL_FLOAT_VEC3:
865                 {
866                     GL_ENTRYPOINT(glUniform3fv)(restore_location, array_size, static_cast<const GLfloat *>(pTrace_data));
867                     break;
868                 }
869                 case GL_FLOAT_VEC4:
870                 {
871                     GL_ENTRYPOINT(glUniform4fv)(restore_location, array_size, static_cast<const GLfloat *>(pTrace_data));
872                     break;
873                 }
874                 case GL_FLOAT_MAT2:
875                 {
876                     GL_ENTRYPOINT(glUniformMatrix2fv)(restore_location, array_size, GL_FALSE, static_cast<const GLfloat *>(pTrace_data));
877                     break;
878                 }
879                 case GL_FLOAT_MAT3:
880                 {
881                     GL_ENTRYPOINT(glUniformMatrix3fv)(restore_location, array_size, GL_FALSE, static_cast<const GLfloat *>(pTrace_data));
882                     break;
883                 }
884                 case GL_FLOAT_MAT4:
885                 {
886                     GL_ENTRYPOINT(glUniformMatrix4fv)(restore_location, array_size, GL_FALSE, static_cast<const GLfloat *>(pTrace_data));
887                     break;
888                 }
889                 case GL_FLOAT_MAT2x3:
890                 {
891                     GL_ENTRYPOINT(glUniformMatrix2x3fv)(restore_location, array_size, GL_FALSE, static_cast<const GLfloat *>(pTrace_data));
892                     break;
893                 }
894                 case GL_FLOAT_MAT3x2:
895                 {
896                     GL_ENTRYPOINT(glUniformMatrix3x2fv)(restore_location, array_size, GL_FALSE, static_cast<const GLfloat *>(pTrace_data));
897                     break;
898                 }
899                 case GL_FLOAT_MAT2x4:
900                 {
901                     GL_ENTRYPOINT(glUniformMatrix2x4fv)(restore_location, array_size, GL_FALSE, static_cast<const GLfloat *>(pTrace_data));
902                     break;
903                 }
904                 case GL_FLOAT_MAT4x2:
905                 {
906                     GL_ENTRYPOINT(glUniformMatrix4x2fv)(restore_location, array_size, GL_FALSE, static_cast<const GLfloat *>(pTrace_data));
907                     break;
908                 }
909                 case GL_FLOAT_MAT3x4:
910                 {
911                     GL_ENTRYPOINT(glUniformMatrix3x4fv)(restore_location, array_size, GL_FALSE, static_cast<const GLfloat *>(pTrace_data));
912                     break;
913                 }
914                 case GL_FLOAT_MAT4x3:
915                 {
916                     GL_ENTRYPOINT(glUniformMatrix4x3fv)(restore_location, array_size, GL_FALSE, static_cast<const GLfloat *>(pTrace_data));
917                     break;
918                 }
919
920                 case GL_SAMPLER_1D:
921                 case GL_SAMPLER_2D:
922                 case GL_SAMPLER_3D:
923                 case GL_SAMPLER_CUBE:
924                 case GL_SAMPLER_1D_SHADOW:
925                 case GL_SAMPLER_2D_SHADOW:
926                 case GL_SAMPLER_1D_ARRAY:
927                 case GL_SAMPLER_2D_ARRAY:
928                 case GL_SAMPLER_1D_ARRAY_SHADOW:
929                 case GL_SAMPLER_2D_ARRAY_SHADOW:
930                 case GL_SAMPLER_2D_MULTISAMPLE:
931                 case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
932                 case GL_SAMPLER_CUBE_SHADOW:
933                 case GL_SAMPLER_BUFFER:
934                 case GL_SAMPLER_2D_RECT:
935                 case GL_SAMPLER_2D_RECT_SHADOW:
936                 case GL_INT_SAMPLER_1D:
937                 case GL_INT_SAMPLER_2D:
938                 case GL_INT_SAMPLER_3D:
939                 case GL_INT_SAMPLER_CUBE:
940                 case GL_INT_SAMPLER_1D_ARRAY:
941                 case GL_INT_SAMPLER_2D_ARRAY:
942                 case GL_INT_SAMPLER_2D_MULTISAMPLE:
943                 case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
944                 case GL_INT_SAMPLER_BUFFER:
945                 case GL_INT_SAMPLER_2D_RECT:
946                 case GL_UNSIGNED_INT_SAMPLER_1D:
947                 case GL_UNSIGNED_INT_SAMPLER_2D:
948                 case GL_UNSIGNED_INT_SAMPLER_3D:
949                 case GL_UNSIGNED_INT_SAMPLER_CUBE:
950                 case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
951                 case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
952                 case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
953                 case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
954                 case GL_UNSIGNED_INT_SAMPLER_BUFFER:
955                 case GL_UNSIGNED_INT_SAMPLER_2D_RECT:
956
957                 case GL_IMAGE_1D:
958                 case GL_IMAGE_2D:
959                 case GL_IMAGE_3D:
960                 case GL_IMAGE_2D_RECT:
961                 case GL_IMAGE_CUBE:
962                 case GL_IMAGE_BUFFER:
963                 case GL_IMAGE_1D_ARRAY:
964                 case GL_IMAGE_2D_ARRAY:
965                 case GL_IMAGE_2D_MULTISAMPLE:
966                 case GL_IMAGE_2D_MULTISAMPLE_ARRAY:
967                 case GL_INT_IMAGE_1D:
968                 case GL_INT_IMAGE_2D:
969                 case GL_INT_IMAGE_3D:
970                 case GL_INT_IMAGE_2D_RECT:
971                 case GL_INT_IMAGE_CUBE:
972                 case GL_INT_IMAGE_BUFFER:
973                 case GL_INT_IMAGE_1D_ARRAY:
974                 case GL_INT_IMAGE_2D_ARRAY:
975                 case GL_INT_IMAGE_2D_MULTISAMPLE:
976                 case GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY:
977                 case GL_UNSIGNED_INT_IMAGE_1D:
978                 case GL_UNSIGNED_INT_IMAGE_2D:
979                 case GL_UNSIGNED_INT_IMAGE_3D:
980                 case GL_UNSIGNED_INT_IMAGE_2D_RECT:
981                 case GL_UNSIGNED_INT_IMAGE_CUBE:
982                 case GL_UNSIGNED_INT_IMAGE_BUFFER:
983                 case GL_UNSIGNED_INT_IMAGE_1D_ARRAY:
984                 case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
985                 case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE:
986                 case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY:
987
988                 case GL_INT:
989                 case GL_BOOL:
990                 {
991                     GL_ENTRYPOINT(glUniform1iv)(restore_location, array_size, static_cast<const GLint *>(pTrace_data));
992                     break;
993                 }
994                 case GL_INT_VEC2:
995                 case GL_BOOL_VEC2:
996                 {
997                     GL_ENTRYPOINT(glUniform2iv)(restore_location, array_size, static_cast<const GLint *>(pTrace_data));
998                     break;
999                 }
1000                 case GL_INT_VEC3:
1001                 case GL_BOOL_VEC3:
1002                 {
1003                     GL_ENTRYPOINT(glUniform3iv)(restore_location, array_size, static_cast<const GLint *>(pTrace_data));
1004                     break;
1005                 }
1006                 case GL_INT_VEC4:
1007                 case GL_BOOL_VEC4:
1008                 {
1009                     GL_ENTRYPOINT(glUniform4iv)(restore_location, array_size, static_cast<const GLint *>(pTrace_data));
1010                     break;
1011                 }
1012                 case GL_UNSIGNED_INT:
1013                 case GL_UNSIGNED_INT_ATOMIC_COUNTER: // TODO: is this correct?
1014                 {
1015                     GL_ENTRYPOINT(glUniform1uiv)(restore_location, array_size, static_cast<const GLuint *>(pTrace_data));
1016                     break;
1017                 }
1018                 case GL_UNSIGNED_INT_VEC2:
1019                 {
1020                     GL_ENTRYPOINT(glUniform2uiv)(restore_location, array_size, static_cast<const GLuint *>(pTrace_data));
1021                     break;
1022                 }
1023                 case GL_UNSIGNED_INT_VEC3:
1024                 {
1025                     GL_ENTRYPOINT(glUniform3uiv)(restore_location, array_size, static_cast<const GLuint *>(pTrace_data));
1026                     break;
1027                 }
1028                 case GL_UNSIGNED_INT_VEC4:
1029                 {
1030                     GL_ENTRYPOINT(glUniform4uiv)(restore_location, array_size, static_cast<const GLuint *>(pTrace_data));
1031                     break;
1032                 }
1033                 default:
1034                 {
1035                     VOGL_ASSERT_ALWAYS;
1036                     vogl_warning_printf("%s: Unknown uniform type 0x%04X\n", VOGL_FUNCTION_NAME, restore_uniform.m_type);
1037                     any_restore_warnings = true;
1038                     continue;
1039                 }
1040             }
1041
1042             if (vogl_check_gl_error())
1043             {
1044                 any_gl_errors = true;
1045
1046                 vogl_warning_printf("%s: A GL error occurred while attempting to restore trace uniform \"%s\" type %s, trace program %u GL program %u.\n", VOGL_METHOD_NAME,
1047                                    trace_uniform.m_name.get_ptr(), g_gl_enums.find_gl_name(trace_uniform.m_type), m_snapshot_handle, handle32);
1048             }
1049         }
1050     } // uniform_iter
1051
1052     return true;
1053 }
1054
1055 bool vogl_program_state::restore_uniform_blocks(uint32 handle32, const vogl_context_info &context_info, vogl_handle_remapper &remapper, bool &any_restore_warnings, bool &any_gl_errors) const
1056 {
1057     VOGL_FUNC_TRACER
1058
1059     VOGL_NOTE_UNUSED(remapper);
1060
1061     // Restore active uniform blocks
1062     if ((context_info.get_version() >= VOGL_GL_VERSION_3_1) && GL_ENTRYPOINT(glGetUniformBlockIndex) && GL_ENTRYPOINT(glUniformBlockBinding))
1063     {
1064         VOGL_CHECK_GL_ERROR;
1065
1066         growable_array<char, 256> name_buf;
1067
1068         for (uint uniform_block_index = 0; uniform_block_index < m_num_active_uniform_blocks; uniform_block_index++)
1069         {
1070             const vogl_program_uniform_block_state &state = m_uniform_blocks[uniform_block_index];
1071
1072             if (state.m_name.is_empty())
1073             {
1074                 vogl_error_printf("%s: Trace program %u GL program %u: Invalid uniform block name!\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32);
1075                 any_restore_warnings = true;
1076                 continue;
1077             }
1078
1079             GLuint restore_block_index = GL_ENTRYPOINT(glGetUniformBlockIndex)(handle32, reinterpret_cast<const GLchar *>(state.m_name.get_ptr()));
1080             bool gl_err = vogl_check_gl_error();
1081             if (gl_err)
1082                 any_gl_errors = true;
1083
1084             if ((gl_err) || (restore_block_index == GL_INVALID_INDEX))
1085             {
1086                 vogl_error_printf("%s: Trace program %u GL program %u: Failed finding uniform block \"%s\"\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32, state.m_name.get_ptr());
1087                 any_restore_warnings = true;
1088                 continue;
1089             }
1090
1091             if (restore_block_index != state.m_uniform_block_index)
1092             {
1093                 vogl_error_printf("%s: Trace program %u GL program %u: Uniform block \"%s\"'s restore block index (%u) differs from it's trace index (%u)! Uniform block indices are not currently remapped while replaying GL calls, so this trace may not be replayable!\n",
1094                                  VOGL_METHOD_NAME, (uint)m_snapshot_handle, handle32, state.m_name.get_ptr(), state.m_uniform_block_index, restore_block_index);
1095                 any_restore_warnings = true;
1096             }
1097
1098             GL_ENTRYPOINT(glUniformBlockBinding)(handle32, restore_block_index, state.m_uniform_block_binding_point);
1099             if (vogl_check_gl_error())
1100             {
1101                 any_gl_errors = true;
1102
1103                 vogl_error_printf("%s: Trace program %u GL program %u: Failed restoring uniform block \"%s\"'s binding point %u\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32, state.m_name.get_ptr(), state.m_uniform_block_binding_point);
1104                 any_restore_warnings = true;
1105                 continue;
1106             }
1107
1108             GLint restore_block_data_size = 0;
1109             GL_ENTRYPOINT(glGetActiveUniformBlockiv)(handle32, restore_block_index, GL_UNIFORM_BLOCK_DATA_SIZE, &restore_block_data_size);
1110             if (vogl_check_gl_error())
1111                 any_gl_errors = true;
1112
1113             if (state.m_uniform_block_data_size != restore_block_data_size)
1114             {
1115                 vogl_warning_printf("%s: Trace program %u GL program %u: Uniform block \"%s\"'s data size (%u) differs from trace's (%u)\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32, state.m_name.get_ptr(), state.m_uniform_block_data_size, restore_block_data_size);
1116                 any_restore_warnings = true;
1117             }
1118
1119             GLint restore_block_active_uniforms = 0;
1120             GL_ENTRYPOINT(glGetActiveUniformBlockiv)(handle32, restore_block_index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &restore_block_active_uniforms);
1121             if (vogl_check_gl_error())
1122                 any_gl_errors = true;
1123
1124             if (state.m_uniform_block_active_uniforms != restore_block_active_uniforms)
1125             {
1126                 vogl_warning_printf("%s: Trace program %u GL program %u: Uniform block \"%s\"'s number of active block uniforms (%u) differs from trace's (%i)\n",
1127                                    VOGL_METHOD_NAME, (uint)m_snapshot_handle, handle32, state.m_name.get_ptr(), state.m_uniform_block_active_uniforms, restore_block_active_uniforms);
1128                 any_restore_warnings = true;
1129             }
1130         }
1131     }
1132     else if (m_num_active_uniform_blocks)
1133     {
1134         vogl_error_printf("%s: Trace program %u GL program %u has %u active uniform blocks, but the current context does not support uniform blocks!\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32, m_num_active_uniform_blocks);
1135         any_restore_warnings = true;
1136     }
1137
1138     return true;
1139 }
1140
1141 bool vogl_program_state::restore_active_attribs(uint32 handle32, const vogl_context_info &context_info, vogl_handle_remapper &remapper, bool &any_restore_warnings, bool &any_gl_errors) const
1142 {
1143     VOGL_FUNC_TRACER
1144
1145     VOGL_NOTE_UNUSED(context_info);
1146     VOGL_NOTE_UNUSED(remapper);
1147     VOGL_NOTE_UNUSED(any_restore_warnings);
1148
1149     // Restore active attribs
1150     for (uint attrib_iter = 0; attrib_iter < m_num_active_attribs; attrib_iter++)
1151     {
1152         const vogl_program_attrib_state &attrib = m_attribs[attrib_iter];
1153         if ((attrib.m_name.begins_with("_gl", true)) || (attrib.m_bound_location < 0))
1154             continue;
1155
1156         GL_ENTRYPOINT(glBindAttribLocation)(handle32, attrib.m_bound_location, reinterpret_cast<const GLchar *>(attrib.m_name.get_ptr()));
1157
1158         if (vogl_check_gl_error())
1159         {
1160             any_gl_errors = true;
1161
1162             vogl_warning_printf("%s: GL error while binding attrib location %i name %s of trace program %u GL program %u\n", VOGL_METHOD_NAME, attrib.m_bound_location, attrib.m_name.get_ptr(), m_snapshot_handle, handle32);
1163         }
1164     }
1165
1166     return true;
1167 }
1168
1169 bool vogl_program_state::restore_outputs(uint32 handle32, const vogl_context_info &context_info, vogl_handle_remapper &remapper, bool &any_restore_warnings, bool &any_gl_errors) const
1170 {
1171     VOGL_FUNC_TRACER
1172     VOGL_NOTE_UNUSED(remapper);
1173
1174     for (uint i = 0; i < m_outputs.size(); i++)
1175     {
1176         const vogl_program_output_state &output = m_outputs[i];
1177
1178         if ((output.m_name.is_empty()) || (output.m_name.begins_with("gl_", true)))
1179             continue;
1180
1181         int location = output.m_location;
1182         int location_index = output.m_location_index;
1183
1184         if ((context_info.supports_extension("GL_ARB_blend_func_extended")) && (GL_ENTRYPOINT(glBindFragDataLocationIndexed)))
1185         {
1186             GL_ENTRYPOINT(glBindFragDataLocationIndexed)(handle32, location, location_index, reinterpret_cast<const GLchar *>(output.m_name.get_ptr()));
1187         }
1188         else
1189         {
1190             if (location_index)
1191             {
1192                 // TODO: This is not a warning, but we'll try to soldier on anyway.
1193                 any_restore_warnings = true;
1194
1195                 vogl_error_printf("%s: GL_ARB_blend_func_extended is not supported, but GL program %u uses a non-zero location index\n", VOGL_METHOD_NAME, handle32);
1196             }
1197
1198             GL_ENTRYPOINT(glBindFragDataLocation)(handle32, location, reinterpret_cast<const GLchar *>(output.m_name.get_ptr()));
1199         }
1200
1201         if (vogl_check_gl_error())
1202         {
1203             any_gl_errors = true;
1204
1205             vogl_error_printf("%s: GL error while binding fragdata location %i location index %i name %s GL program %u\n", VOGL_METHOD_NAME, output.m_location, output.m_location_index, output.m_name.get_ptr(), handle32);
1206         }
1207     }
1208
1209     return true;
1210 }
1211
1212 bool vogl_program_state::restore_transform_feedback(uint32 handle32, const vogl_context_info &context_info, vogl_handle_remapper &remapper, bool &any_restore_warnings, bool &any_gl_errors) const
1213 {
1214     VOGL_FUNC_TRACER;
1215     VOGL_NOTE_UNUSED(context_info);
1216     VOGL_NOTE_UNUSED(remapper);
1217     VOGL_NOTE_UNUSED(any_restore_warnings);
1218
1219     if (!m_varyings.size())
1220         return true;
1221
1222     dynamic_string_array names;
1223
1224     for (uint i = 0; i < m_varyings.size(); i++)
1225     {
1226         const vogl_program_transform_feedback_varying &varying = m_varyings[i];
1227
1228         GLint index = varying.m_index;
1229         if (index < 0)
1230             continue;
1231
1232         names.ensure_element_is_valid(index);
1233         names[index] = varying.m_name;
1234     }
1235
1236     vogl::vector<GLchar *> varyings(names.size());
1237     for (uint i = 0; i < names.size(); i++)
1238         varyings[i] = (GLchar *)(names[i].get_ptr());
1239
1240     GL_ENTRYPOINT(glTransformFeedbackVaryings)(handle32, varyings.size(), varyings.get_ptr(), m_transform_feedback_mode);
1241     if (vogl_check_gl_error())
1242     {
1243         any_gl_errors = true;
1244
1245         vogl_error_printf("%s: GL error while setting transform feedback varyings, GL program %u\n", VOGL_METHOD_NAME, handle32);
1246     }
1247
1248     return true;
1249 }
1250
1251 bool vogl_program_state::restore_link_snapshot(uint32 handle32, const vogl_context_info &context_info, vogl_handle_remapper &remapper, bool &any_restore_warnings, bool &any_gl_errors, bool &link_succeeded) const
1252 {
1253     VOGL_FUNC_TRACER
1254
1255     if (vogl_check_gl_error())
1256         any_gl_errors = true;
1257
1258     VOGL_ASSERT(m_link_snapshot);
1259
1260     if (!restore_active_attribs(handle32, context_info, remapper, any_restore_warnings, any_gl_errors))
1261         return false;
1262
1263     if (!restore_outputs(handle32, context_info, remapper, any_restore_warnings, any_gl_errors))
1264         return false;
1265
1266     if (!restore_transform_feedback(handle32, context_info, remapper, any_restore_warnings, any_gl_errors))
1267         return false;
1268
1269     uint_vec shader_handles;
1270
1271     if (m_program_binary.size())
1272     {
1273         GL_ENTRYPOINT(glProgramBinary)(handle32, m_program_binary_format, m_program_binary.get_ptr(), m_program_binary.size());
1274         if (vogl_check_gl_error())
1275         {
1276             vogl_warning_printf("%s: GL error while setting link-time snapshot program binary on trace program %u GL program %u\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32);
1277             any_gl_errors = true;
1278         }
1279         else if (get_program_bool(handle32, GL_LINK_STATUS))
1280             link_succeeded = true;
1281     }
1282
1283     if ((!link_succeeded) && (m_shaders.size()))
1284     {
1285         shader_handles.resize(m_shaders.size());
1286
1287         for (uint i = 0; i < m_shaders.size(); i++)
1288         {
1289             shader_handles[i] = GL_ENTRYPOINT(glCreateShader)(m_shaders[i].get_shader_type());
1290             if ((vogl_check_gl_error()) || (!shader_handles[i]))
1291                 goto handle_error;
1292
1293             GLuint64 handle = shader_handles[i];
1294             if (!m_shaders[i].restore(context_info, remapper, handle))
1295                 goto handle_error;
1296
1297             if (!m_shaders[i].get_restore_compile_status())
1298             {
1299                 vogl_warning_printf("%s: Failed compiling shadowed link-time shader while restoring program, trace program %u GL program %u\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32);
1300                 any_restore_warnings = true;
1301             }
1302         }
1303
1304         for (uint i = 0; i < m_shaders.size(); i++)
1305         {
1306             GL_ENTRYPOINT(glAttachShader)(handle32, shader_handles[i]);
1307
1308             if (vogl_check_gl_error())
1309             {
1310                 for (uint j = 0; j < i; j++)
1311                     GL_ENTRYPOINT(glDetachShader)(handle32, shader_handles[j]);
1312
1313                 goto handle_error;
1314             }
1315         }
1316
1317         GL_ENTRYPOINT(glLinkProgram)(handle32);
1318
1319         if (vogl_check_gl_error())
1320         {
1321             vogl_warning_printf("%s: GL error while linking link-time snapshot program on trace program %u GL program %u\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32);
1322             any_gl_errors = true;
1323         }
1324         else if (get_program_bool(handle32, GL_LINK_STATUS))
1325             link_succeeded = true;
1326
1327         for (uint i = 0; i < shader_handles.size(); i++)
1328         {
1329             GL_ENTRYPOINT(glDetachShader)(handle32, shader_handles[i]);
1330             if (vogl_check_gl_error())
1331                 any_gl_errors = true;
1332
1333             GL_ENTRYPOINT(glDeleteShader)(shader_handles[i]);
1334             shader_handles[i] = 0;
1335
1336             if (vogl_check_gl_error())
1337                 any_gl_errors = true;
1338         }
1339     }
1340
1341     return true;
1342
1343 handle_error:
1344     for (uint i = 0; i < shader_handles.size(); i++)
1345         if (shader_handles[i])
1346             GL_ENTRYPOINT(glDeleteShader)(shader_handles[i]);
1347
1348     if (vogl_check_gl_error())
1349         any_gl_errors = true;
1350
1351     return false;
1352 }
1353
1354 bool vogl_program_state::link_program(uint32 handle32, const vogl_context_info &context_info, vogl_handle_remapper &remapper, bool &any_restore_warnings, bool &any_gl_errors, bool &link_succeeded) const
1355 {
1356     VOGL_FUNC_TRACER
1357
1358     if (vogl_check_gl_error())
1359         any_gl_errors = true;
1360
1361     if (m_link_snapshot)
1362         return restore_link_snapshot(handle32, context_info, remapper, any_restore_warnings, any_gl_errors, link_succeeded);
1363
1364     // First restore the program's linked state.
1365     if (m_pLink_time_snapshot.get())
1366     {
1367         if (!m_pLink_time_snapshot->link_program(handle32, context_info, remapper, any_restore_warnings, any_gl_errors, link_succeeded))
1368             return false;
1369     }
1370
1371     // Now restore the program's actual/current state.
1372     if (!restore_active_attribs(handle32, context_info, remapper, any_restore_warnings, any_gl_errors))
1373         return false;
1374
1375     if (!restore_outputs(handle32, context_info, remapper, any_restore_warnings, any_gl_errors))
1376         return false;
1377
1378     // TODO: Restore the CURRENT transform feedback state (not just the last linked state) - this will require deeper shadowing and considering how rarely transform feedback is used by games it's low priority.
1379
1380     if (m_attached_shaders.size())
1381     {
1382         for (uint i = 0; i < m_attached_shaders.size(); i++)
1383         {
1384             GLuint trace_handle = m_attached_shaders[i];
1385             GLuint replay_handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_SHADERS, trace_handle));
1386             if (replay_handle)
1387             {
1388                 GL_ENTRYPOINT(glAttachShader)(handle32, replay_handle);
1389                 if (vogl_check_gl_error())
1390                 {
1391                     vogl_error_printf("%s: GL error while attaching shader %u to trace program %u GL program %u\n", VOGL_METHOD_NAME, replay_handle, m_snapshot_handle, handle32);
1392                     return false;
1393                 }
1394             }
1395         }
1396     }
1397
1398     return true;
1399 }
1400
1401 bool vogl_program_state::restore(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 &handle) const
1402 {
1403     VOGL_FUNC_TRACER
1404
1405     VOGL_NOTE_UNUSED(context_info);
1406
1407     VOGL_CHECK_GL_ERROR;
1408
1409     if (!m_is_valid)
1410         return false;
1411
1412     vogl_scoped_binding_state orig_binding(GL_PROGRAM);
1413
1414     bool created_handle = false;
1415
1416     if (!handle)
1417     {
1418         handle = GL_ENTRYPOINT(glCreateProgram)();
1419         if ((vogl_check_gl_error()) || (!handle))
1420             return false;
1421
1422         remapper.declare_handle(VOGL_NAMESPACE_PROGRAMS, m_snapshot_handle, handle, GL_NONE);
1423         VOGL_ASSERT(remapper.remap_handle(VOGL_NAMESPACE_PROGRAMS, m_snapshot_handle) == handle);
1424
1425         created_handle = true;
1426     }
1427
1428     VOGL_ASSERT(handle <= cUINT32_MAX);
1429     GLuint handle32 = static_cast<GLuint>(handle);
1430
1431     bool any_gl_errors = false;
1432     bool any_restore_warnings = false;
1433
1434     bool link_succeeded = false;
1435     if (!link_program(handle32, context_info, remapper, any_restore_warnings, any_gl_errors, link_succeeded))
1436         goto handle_error;
1437
1438     if ((m_pLink_time_snapshot.get()) && (link_succeeded != m_pLink_time_snapshot->m_link_status))
1439     {
1440         if (!link_succeeded)
1441         {
1442             if (m_pLink_time_snapshot->m_link_status)
1443             {
1444                 vogl_error_printf("%s: Failed linking trace program %u GL program %u, but this program was recorded as successfully linked in the trace\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32);
1445                 goto handle_error;
1446             }
1447             else
1448             {
1449                 vogl_debug_printf("%s: Failed linking trace program %u GL program %u, but this program was also recorded as not successfully linked in the trace\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32);
1450             }
1451         }
1452         else if (!m_pLink_time_snapshot->m_link_status)
1453         {
1454             vogl_warning_printf("%s: Succeeded linking trace program %u GL program %u, which is odd because this program was recorded as not successfully linked in the trace\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32);
1455             any_restore_warnings = true;
1456         }
1457     }
1458
1459     if (link_succeeded)
1460     {
1461         GL_ENTRYPOINT(glUseProgram)(handle32);
1462         if (vogl_check_gl_error())
1463         {
1464             any_gl_errors = true;
1465             goto handle_error;
1466         }
1467
1468         if (!restore_uniforms(handle32, context_info, remapper, any_restore_warnings, any_gl_errors))
1469             goto handle_error;
1470
1471         if (!restore_uniform_blocks(handle32, context_info, remapper, any_restore_warnings, any_gl_errors))
1472             goto handle_error;
1473
1474     } // if (link_succeeded)
1475
1476     if (any_gl_errors)
1477     {
1478         vogl_warning_printf("%s: One or more GL errors occurred while attempting to restore trace program %u GL program %u. This program has been restored as much as possible, but the replay may diverge.\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32);
1479     }
1480
1481     if (any_restore_warnings)
1482     {
1483         vogl_warning_printf("%s: One or more restore warnings occurred while attempting to restore trace program %u GL program %u. This program has been restored as much as possible, but the replay may diverge.\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32);
1484     }
1485
1486     return true;
1487
1488 handle_error:
1489     vogl_error_printf("%s: Failed restoring trace program %u GL program %u\n", VOGL_METHOD_NAME, m_snapshot_handle, handle32);
1490
1491     if ((handle) && (created_handle))
1492     {
1493         GL_ENTRYPOINT(glUseProgram)(0);
1494         VOGL_CHECK_GL_ERROR;
1495
1496         remapper.delete_handle_and_object(VOGL_NAMESPACE_PROGRAMS, m_snapshot_handle, handle);
1497
1498         handle = 0;
1499         handle32 = 0;
1500     }
1501
1502     return false;
1503 }
1504
1505 bool vogl_program_state::remap_handles(vogl_handle_remapper &remapper)
1506 {
1507     VOGL_FUNC_TRACER
1508
1509     if (!m_is_valid)
1510         return false;
1511
1512     uint replay_handle = m_snapshot_handle;
1513
1514     uint trace_handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_PROGRAMS, m_snapshot_handle));
1515
1516     m_snapshot_handle = trace_handle;
1517
1518     if (!m_link_snapshot)
1519     {
1520         for (uint i = 0; i < m_attached_shaders.size(); i++)
1521             m_attached_shaders[i] = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_SHADERS, m_attached_shaders[i]));
1522     }
1523     else
1524     {
1525         for (uint i = 0; i < m_shaders.size(); i++)
1526         {
1527             // Try to remap the shader's handle, although it could be dead by now. This makes it easier to diff state captures.
1528             if (m_shaders[i].is_valid())
1529             {
1530                 GLuint64 snapshot_handle = m_shaders[i].get_snapshot_handle();
1531                 if (remapper.is_valid_handle(VOGL_NAMESPACE_SHADERS, snapshot_handle))
1532                     m_shaders[i].set_snapshot_handle(remapper.remap_handle(VOGL_NAMESPACE_SHADERS, snapshot_handle));
1533             }
1534         }
1535     }
1536
1537     for (uint i = 0; i < m_uniforms.size(); i++)
1538         if (m_uniforms[i].m_base_location >= 0)
1539             m_uniforms[i].m_base_location = remapper.remap_location(replay_handle, m_uniforms[i].m_base_location);
1540
1541     if (m_pLink_time_snapshot.get())
1542     {
1543         if (!m_pLink_time_snapshot->remap_handles(remapper))
1544             return false;
1545     }
1546
1547     return true;
1548 }
1549
1550 void vogl_program_state::clear()
1551 {
1552     VOGL_FUNC_TRACER
1553
1554     m_snapshot_handle = 0;
1555
1556     m_program_binary.clear();
1557     m_program_binary_format = GL_NONE;
1558
1559     m_attached_shaders.clear();
1560
1561     m_info_log.clear();
1562
1563     m_num_active_attribs = 0;
1564     m_num_active_uniforms = 0;
1565     m_num_active_uniform_blocks = 0;
1566
1567     m_attribs.clear();
1568     m_uniforms.clear();
1569     m_uniform_blocks.clear();
1570
1571     m_shaders.clear();
1572
1573     m_outputs.clear();
1574
1575     m_pLink_time_snapshot.reset();
1576
1577     m_transform_feedback_mode = GL_NONE;
1578     m_transform_feedback_num_varyings = 0;
1579     m_varyings.clear();
1580
1581     m_marked_for_deletion = false;
1582     m_link_status = false;
1583     m_verify_status = false;
1584
1585     m_link_snapshot = false;
1586     m_is_valid = false;
1587 }
1588
1589 bool vogl_program_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
1590 {
1591     VOGL_FUNC_TRACER
1592
1593     VOGL_NOTE_UNUSED(blob_manager);
1594
1595     if (!m_is_valid)
1596         return false;
1597
1598     node.add_key_value("version", VOGL_PROGRAM_VERSION);
1599     node.add_key_value("handle", m_snapshot_handle);
1600     node.add_key_value("link_snapshot", m_link_snapshot);
1601     node.add_key_value("link_status", m_link_status);
1602     node.add_key_value("verify_status", m_verify_status);
1603     node.add_key_value("marked_for_deletion", m_marked_for_deletion);
1604     node.add_key_value("num_active_attribs", m_num_active_attribs);
1605     node.add_key_value("num_active_uniforms", m_num_active_uniforms);
1606     node.add_key_value("num_active_uniform_blocks", m_num_active_uniform_blocks);
1607     node.add_key_value("info_log", m_info_log);
1608
1609     node.add_key_value("program_binary_format", m_program_binary_format);
1610     if (m_program_binary.size())
1611     {
1612         dynamic_string id(blob_manager.add_buf_compute_unique_id(m_program_binary.get_ptr(), m_program_binary.size(), "program_binary", "bin"));
1613         node.add_key_value("program_binary", id);
1614     }
1615
1616     if (m_attached_shaders.size())
1617     {
1618         json_node &attached_shaders_array = node.add_array("attached_shaders");
1619         for (uint i = 0; i < m_attached_shaders.size(); i++)
1620             attached_shaders_array.add_value(m_attached_shaders[i]);
1621     }
1622
1623     if (m_shaders.size())
1624     {
1625         json_node &shader_objects_array = node.add_array("shader_objects");
1626         for (uint i = 0; i < m_shaders.size(); i++)
1627             if (!m_shaders[i].serialize(shader_objects_array.add_object(), blob_manager))
1628                 return false;
1629     }
1630
1631     if (m_attribs.size())
1632     {
1633         json_node &active_attribs_array = node.add_array("active_attribs");
1634         for (uint i = 0; i < m_attribs.size(); i++)
1635         {
1636             const vogl_program_attrib_state &attrib = m_attribs[i];
1637
1638             json_node &attrib_node = active_attribs_array.add_object();
1639             attrib_node.add_key_value("name", attrib.m_name);
1640             attrib_node.add_key_value("type", g_gl_enums.find_gl_name(attrib.m_type));
1641             attrib_node.add_key_value("size", attrib.m_size);
1642             attrib_node.add_key_value("location", attrib.m_bound_location);
1643         }
1644     }
1645
1646     if (m_uniforms.size())
1647     {
1648         json_node &active_uniforms_array = node.add_array("active_uniforms");
1649
1650         vogl::vector<char> temp_buf;
1651         temp_buf.reserve(64);
1652
1653         for (uint i = 0; i < m_uniforms.size(); i++)
1654         {
1655             const vogl_program_uniform_state &uniform = m_uniforms[i];
1656
1657             json_node &uniform_node = active_uniforms_array.add_object();
1658             uniform_node.add_key_value("name", uniform.m_name);
1659             uniform_node.add_key_value("type", g_gl_enums.find_gl_name(uniform.m_type));
1660             uniform_node.add_key_value("size", uniform.m_size);
1661             uniform_node.add_key_value("base_location", uniform.m_base_location);
1662
1663             json_node &uniform_data = uniform_node.add_array("uniform_data");
1664
1665             if (uniform.m_data.size())
1666             {
1667                 const uint type_size_in_GLints = vogl_gl_get_uniform_size_in_GLints(uniform.m_type);
1668                 const uint type_size = vogl_gl_get_uniform_size_in_bytes(uniform.m_type);
1669                 const GLenum base_type = vogl_gl_get_uniform_base_type(uniform.m_type);
1670
1671                 for (int element = 0; element < uniform.m_size; element++)
1672                 {
1673                     for (uint element_ofs = 0; element_ofs < type_size_in_GLints; element_ofs++)
1674                     {
1675                         const uint32 *pData = reinterpret_cast<const uint32 *>(uniform.m_data.get_ptr() + type_size * element + sizeof(uint32) * element_ofs);
1676
1677                         if (base_type == GL_FLOAT)
1678                         {
1679                             float f = *reinterpret_cast<const float *>(pData);
1680
1681                             json_value v;
1682                             v.set_value(f);
1683
1684                             temp_buf.resize(0);
1685                             v.serialize(temp_buf, false);
1686
1687                             json_value dv;
1688                             bool failed = true;
1689                             if (dv.deserialize(temp_buf.get_ptr(), temp_buf.size()))
1690                             {
1691                                 if (static_cast<float>(dv.as_double()) == f)
1692                                     failed = false;
1693                             }
1694
1695                             if (failed)
1696                             {
1697                                 dynamic_string str(cVarArg, "0x%08X", *pData);
1698                                 uniform_data.add_value(str);
1699                             }
1700                             else
1701                             {
1702                                 uniform_data.add_value(f);
1703                             }
1704                         }
1705                         else if (base_type == GL_DOUBLE)
1706                         {
1707                             double f = *reinterpret_cast<const double *>(pData);
1708
1709                             json_value v;
1710                             v.set_value(f);
1711
1712                             temp_buf.resize(0);
1713                             v.serialize(temp_buf, false);
1714
1715                             json_value dv;
1716
1717                             bool failed = true;
1718                             if (dv.deserialize(temp_buf.get_ptr(), temp_buf.size()))
1719                             {
1720                                 if (dv.as_double() == f)
1721                                     failed = false;
1722                             }
1723
1724                             if (failed)
1725                             {
1726                                 uniform_data.add_value(*reinterpret_cast<const uint64_t *>(pData));
1727                             }
1728                             else
1729                             {
1730                                 uniform_data.add_value(f);
1731                             }
1732                         }
1733                         else if (base_type == GL_BOOL)
1734                         {
1735                             uniform_data.add_value(*pData != 0);
1736                         }
1737                         else if (base_type == GL_UNSIGNED_INT)
1738                         {
1739                             uniform_data.add_value(*reinterpret_cast<const GLuint *>(pData));
1740                         }
1741                         else
1742                         {
1743                             uniform_data.add_value(*reinterpret_cast<const GLint *>(pData));
1744                         }
1745                     }
1746                 }
1747             }
1748         }
1749     }
1750
1751     if (m_uniform_blocks.size())
1752     {
1753         json_node &active_uniforms_blocks_array = node.add_array("active_uniform_blocks");
1754         for (uint i = 0; i < m_uniform_blocks.size(); i++)
1755         {
1756             const vogl_program_uniform_block_state &state = m_uniform_blocks[i];
1757
1758             json_node &uniform_block_node = active_uniforms_blocks_array.add_object();
1759             uniform_block_node.add_key_value("block_index", state.m_uniform_block_index);
1760             uniform_block_node.add_key_value("name", state.m_name);
1761             uniform_block_node.add_key_value("binding_point", state.m_uniform_block_binding_point);
1762             uniform_block_node.add_key_value("data_size", state.m_uniform_block_data_size);
1763             uniform_block_node.add_key_value("active_uniforms", state.m_uniform_block_active_uniforms);
1764         }
1765     }
1766
1767     if (m_outputs.size())
1768     {
1769         json_node &outputs_array = node.add_array("outputs");
1770         for (uint i = 0; i < m_outputs.size(); i++)
1771         {
1772             const vogl_program_output_state &output = m_outputs[i];
1773
1774             json_node &outputs_node = outputs_array.add_object();
1775             outputs_node.add_key_value("index", i);
1776             outputs_node.add_key_value("name", output.m_name);
1777             outputs_node.add_key_value("location", output.m_location);
1778             outputs_node.add_key_value("location_index", output.m_location_index);
1779             outputs_node.add_key_value("type", g_gl_enums.find_gl_name(output.m_type));
1780             outputs_node.add_key_value("array_size", output.m_array_size);
1781             outputs_node.add_key_value("is_per_patch", output.m_is_per_patch);
1782         }
1783     }
1784
1785     node.add_key_value("transform_feedback_mode", g_gl_enums.find_gl_name(m_transform_feedback_mode));
1786     node.add_key_value("transform_feedback_num_varyings", m_transform_feedback_num_varyings);
1787
1788     if (m_transform_feedback_num_varyings)
1789     {
1790         json_node &varyings_array = node.add_array("transform_feedback_varyings");
1791         for (uint i = 0; i < m_varyings.size(); i++)
1792         {
1793             const vogl_program_transform_feedback_varying &varying = m_varyings[i];
1794
1795             json_node &node = varyings_array.add_object();
1796             node.add_key_value("index", varying.m_index);
1797             node.add_key_value("name", varying.m_name);
1798             node.add_key_value("size", varying.m_size);
1799             node.add_key_value("type", g_gl_enums.find_gl_name(varying.m_type));
1800         }
1801     }
1802
1803     if (m_pLink_time_snapshot.get())
1804     {
1805         if (!m_pLink_time_snapshot->serialize(node.add_object("link_time_snapshot"), blob_manager))
1806             return false;
1807     }
1808
1809     return true;
1810 }
1811
1812 bool vogl_program_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
1813 {
1814     VOGL_FUNC_TRACER
1815
1816     VOGL_NOTE_UNUSED(blob_manager);
1817
1818     clear();
1819
1820     if (!node.is_object())
1821         return false;
1822
1823     m_snapshot_handle = node.value_as_uint32("handle");
1824     m_link_snapshot = node.value_as_bool("link_snapshot");
1825     m_link_status = node.value_as_bool("link_status");
1826     m_verify_status = node.value_as_bool("verify_status");
1827     m_marked_for_deletion = node.value_as_bool("marked_for_deletion");
1828     m_num_active_attribs = node.value_as_int("num_active_attribs");
1829     m_num_active_uniforms = node.value_as_int("num_active_uniforms");
1830     m_num_active_uniform_blocks = node.value_as_int("num_active_uniform_blocks");
1831     m_info_log = node.value_as_string("info_log");
1832
1833     m_program_binary_format = node.value_as_uint32("program_binary_format");
1834     if (node.has_key("program_binary"))
1835     {
1836         if (!blob_manager.get(node.value_as_string("program_binary"), m_program_binary))
1837         {
1838             vogl_warning_printf("%s: Failed retreiving program_binary data blob from trace program %u\n", VOGL_METHOD_NAME, m_snapshot_handle);
1839         }
1840     }
1841
1842     const json_node *pAttached_shaders_array = node.find_child_array("attached_shaders");
1843     if (pAttached_shaders_array)
1844     {
1845         m_attached_shaders.resize(pAttached_shaders_array->size());
1846         for (uint i = 0; i < m_attached_shaders.size(); i++)
1847             m_attached_shaders[i] = pAttached_shaders_array->value_as_uint32(i);
1848     }
1849
1850     const json_node *pShader_objects_array = node.find_child_array("shader_objects");
1851     if (pShader_objects_array)
1852     {
1853         if (!pShader_objects_array->are_all_children_objects())
1854             vogl_warning_printf("%s: shader_objects node must contain all objects, trace program %u\n", VOGL_METHOD_NAME, m_snapshot_handle);
1855         else
1856         {
1857             m_shaders.resize(pShader_objects_array->size());
1858             for (uint i = 0; i < m_shaders.size(); i++)
1859                 if (!m_shaders[i].deserialize(*pShader_objects_array->get_value_as_object(i), blob_manager))
1860                     return false;
1861         }
1862     }
1863
1864     const json_node *pActive_attribs_array = node.find_child_array("active_attribs");
1865     if (pActive_attribs_array)
1866     {
1867         m_attribs.resize(pActive_attribs_array->size());
1868
1869         for (uint i = 0; i < m_attribs.size(); i++)
1870         {
1871             const json_node *pAttrib_node = pActive_attribs_array->get_value_as_object(i);
1872             if (!pAttrib_node)
1873             {
1874                 vogl_warning_printf("%s: Missing attrib object in active_attribs array, trace program %u\n", VOGL_METHOD_NAME, m_snapshot_handle);
1875             }
1876             else
1877             {
1878                 vogl_program_attrib_state &attrib = m_attribs[i];
1879
1880                 attrib.m_name = pAttrib_node->value_as_string("name");
1881                 attrib.m_type = vogl_get_json_value_as_enum(*pAttrib_node, "type");
1882                 attrib.m_size = pAttrib_node->value_as_int32("size");
1883                 attrib.m_bound_location = pAttrib_node->value_as_int32("location");
1884             }
1885         }
1886     }
1887
1888     const json_node *pActive_uniforms_array = node.find_child_array("active_uniforms");
1889     if (pActive_uniforms_array)
1890     {
1891         m_uniforms.resize(pActive_uniforms_array->size());
1892
1893         for (uint i = 0; i < m_uniforms.size(); i++)
1894         {
1895             const json_node *pUniform_node = pActive_uniforms_array->get_value_as_object(i);
1896             if (!pUniform_node)
1897             {
1898                 vogl_warning_printf("%s: Missing uniform object in active_uniforms array, trace program %u\n", VOGL_METHOD_NAME, m_snapshot_handle);
1899                 continue;
1900             }
1901
1902             vogl_program_uniform_state &uniform = m_uniforms[i];
1903             uniform.m_name = pUniform_node->value_as_string("name");
1904             uniform.m_type = vogl_get_json_value_as_enum(*pUniform_node, "type");
1905             uniform.m_size = pUniform_node->value_as_int32("size");
1906             uniform.m_base_location = pUniform_node->value_as_int32("base_location");
1907
1908             const json_node *pUniform_data = pUniform_node->find_child_array("uniform_data");
1909             if (!pUniform_data)
1910             {
1911                 vogl_warning_printf("%s: Missing uniform_data child array in active_uniforms array, trace program %u\n", VOGL_METHOD_NAME, m_snapshot_handle);
1912                 continue;
1913             }
1914
1915             const uint type_size_in_GLints = vogl_gl_get_uniform_size_in_GLints(uniform.m_type);
1916             const uint type_size = vogl_gl_get_uniform_size_in_bytes(uniform.m_type);
1917             const GLenum base_type = vogl_gl_get_uniform_base_type(uniform.m_type);
1918
1919             uint uniform_data_size_in_bytes = type_size * uniform.m_size;
1920             if (uniform_data_size_in_bytes > 0)
1921             {
1922                 uniform.m_data.resize(uniform_data_size_in_bytes);
1923             }
1924
1925             if ((!type_size_in_GLints) || (!type_size))
1926             {
1927                 vogl_warning_printf("%s: Uniform type error while processing active_uniforms array, trace program %u\n", VOGL_METHOD_NAME, m_snapshot_handle);
1928                 continue;
1929             }
1930
1931             if (pUniform_data->size() != uniform.m_size * type_size_in_GLints)
1932             {
1933                 vogl_warning_printf("%s: uniform_data array's size is invalid, trace program %u\n", VOGL_METHOD_NAME, m_snapshot_handle);
1934                 continue;
1935             }
1936
1937             for (int element = 0; element < uniform.m_size; element++)
1938             {
1939                 for (uint element_ofs = 0; element_ofs < type_size_in_GLints; element_ofs++)
1940                 {
1941                     const uint array_index = element * type_size_in_GLints + element_ofs;
1942
1943                     uint32 *pData = reinterpret_cast<uint32 *>(uniform.m_data.get_ptr() + type_size * element + sizeof(uint32) * element_ofs);
1944                     float *pFloat_data = reinterpret_cast<float *>(pData);
1945                     double *pDouble_data = reinterpret_cast<double *>(pData);
1946
1947                     const json_value &json_val = pUniform_data->get_value(array_index);
1948
1949                     if (json_val.is_string())
1950                     {
1951                         dynamic_string str(json_val.as_string());
1952
1953                         str.trim();
1954
1955                         bool is_hex = str.begins_with("0x", true);
1956                         bool is_negative = str.begins_with("-", true);
1957
1958                         const char *pBuf = str.get_ptr();
1959
1960                         uint64_t val64 = 0;
1961                         bool success = is_negative ? string_ptr_to_int64(pBuf, reinterpret_cast<int64_t &>(val64)) : string_ptr_to_uint64(pBuf, val64);
1962
1963                         if (!success)
1964                             vogl_warning_printf("%s: Failed converting uniform array element \"%s\" to integer, trace program %u\n", VOGL_METHOD_NAME, str.get_ptr(), m_snapshot_handle);
1965                         else if (base_type == GL_FLOAT)
1966                         {
1967                             if (is_hex)
1968                                 *pFloat_data = *reinterpret_cast<const float *>(&val64);
1969                             else
1970                                 *pFloat_data = static_cast<float>(val64);
1971                         }
1972                         else if (base_type == GL_DOUBLE)
1973                         {
1974                             if (is_hex)
1975                                 *pDouble_data = *reinterpret_cast<const double *>(&val64);
1976                             else
1977                                 *pDouble_data = static_cast<double>(val64);
1978                         }
1979                         else
1980                             *pData = static_cast<uint32>(val64);
1981                     }
1982                     else
1983                     {
1984                         if (base_type == GL_FLOAT)
1985                             *pFloat_data = json_val.as_float();
1986                         else if (base_type == GL_DOUBLE)
1987                             *pDouble_data = json_val.as_double();
1988                         else if (base_type == GL_UNSIGNED_INT)
1989                             *pData = json_val.as_uint32();
1990                         else
1991                             *pData = json_val.as_int32();
1992                     }
1993                 }
1994             }
1995         }
1996     }
1997
1998     const json_node *pActive_uniforms_blocks_array = node.find_child_array("active_uniform_blocks");
1999     if (pActive_uniforms_blocks_array)
2000     {
2001         if (!pActive_uniforms_blocks_array->are_all_children_objects())
2002         {
2003             vogl_warning_printf("%s: active_uniform_blocks child array does not contain all child objects in trace program %u\n", VOGL_METHOD_NAME, m_snapshot_handle);
2004         }
2005         else
2006         {
2007             m_uniform_blocks.resize(pActive_uniforms_blocks_array->size());
2008
2009             for (uint i = 0; i < pActive_uniforms_blocks_array->size(); i++)
2010             {
2011                 vogl_program_uniform_block_state &state = m_uniform_blocks[i];
2012
2013                 const json_node &uniform_block_node = *pActive_uniforms_blocks_array->get_value_as_object(i);
2014
2015                 state.m_uniform_block_index = uniform_block_node.value_as_uint32("block_index");
2016                 state.m_name = uniform_block_node.value_as_string("name");
2017                 state.m_uniform_block_binding_point = uniform_block_node.value_as_int("binding_point");
2018                 state.m_uniform_block_data_size = uniform_block_node.value_as_int("data_size");
2019                 state.m_uniform_block_active_uniforms = uniform_block_node.value_as_int("active_uniforms");
2020             }
2021         }
2022     }
2023
2024     const json_node *pOutputs_array = node.find_child_array("outputs");
2025     if (pOutputs_array)
2026     {
2027         if (!pOutputs_array->are_all_children_objects())
2028         {
2029             vogl_warning_printf("%s: outputs child array does not contain all child objects in trace program %u\n", VOGL_METHOD_NAME, m_snapshot_handle);
2030         }
2031         else
2032         {
2033             m_outputs.resize(pOutputs_array->size());
2034             for (uint i = 0; i < pOutputs_array->size(); i++)
2035             {
2036                 const json_node &output_node = *pOutputs_array->get_value_as_object(i);
2037
2038                 vogl_program_output_state &output = m_outputs[i];
2039
2040                 output.clear();
2041
2042                 output.m_name = output_node.value_as_string("name");
2043                 output.m_location = output_node.value_as_int32("location");
2044                 output.m_location_index = output_node.value_as_int32("location_index");
2045                 output.m_type = vogl_get_json_value_as_enum(output_node, "type", GL_NONE);
2046                 output.m_array_size = output_node.value_as_int32("array_size");
2047                 output.m_is_per_patch = output_node.value_as_bool("is_per_patch");
2048             }
2049         }
2050     }
2051
2052     const json_node *pLink_time_snapshot_node = node.find_child_object("link_time_snapshot");
2053     if (pLink_time_snapshot_node)
2054     {
2055         m_pLink_time_snapshot.reset(vogl_new(vogl_program_state));
2056         if (!m_pLink_time_snapshot->deserialize(*pLink_time_snapshot_node, blob_manager))
2057             return false;
2058
2059         if (!m_pLink_time_snapshot->is_valid())
2060         {
2061             vogl_warning_printf("%s: Deserialized link-time snapshot, but it wasn't valid! Trace program %u\n", VOGL_METHOD_NAME, m_snapshot_handle);
2062
2063             m_pLink_time_snapshot.reset();
2064         }
2065     }
2066
2067     m_transform_feedback_mode = vogl_get_json_value_as_enum(node, "transform_feedback_mode", GL_NONE);
2068     m_transform_feedback_num_varyings = node.value_as_uint32("transform_feedback_num_varyings");
2069
2070     if (m_transform_feedback_num_varyings)
2071     {
2072         const json_node *pVaryings_array = node.find_child_array("transform_feedback_varyings");
2073         if (pVaryings_array)
2074         {
2075             m_varyings.resize(m_transform_feedback_num_varyings);
2076
2077             for (uint i = 0; i < m_transform_feedback_num_varyings; i++)
2078             {
2079                 const json_node &varying_node = *pVaryings_array->get_value_as_object(i);
2080
2081                 vogl_program_transform_feedback_varying &varying = m_varyings[i];
2082
2083                 varying.clear();
2084
2085                 m_varyings[i].m_index = varying_node.value_as_uint32("index");
2086                 m_varyings[i].m_name = varying_node.value_as_string("name");
2087                 m_varyings[i].m_size = varying_node.value_as_int("size");
2088                 m_varyings[i].m_type = vogl_get_json_value_as_enum(varying_node, "type");
2089             }
2090         }
2091         else
2092         {
2093             vogl_warning_printf("%s: transform_feedback_num_varyings is %u, but the transform_feedback_varyings array wasn't found! Trace program %u\n", VOGL_METHOD_NAME, m_transform_feedback_num_varyings, m_snapshot_handle);
2094
2095             m_varyings.clear();
2096
2097             m_transform_feedback_mode = GL_NONE;
2098             m_transform_feedback_num_varyings = 0;
2099         }
2100     }
2101
2102     m_is_valid = true;
2103
2104     return true;
2105 }
2106
2107 // TODO: This is not currently used for anything, needs more testing.
2108 // true if rhs is *exactly* the same.
2109 bool vogl_program_state::compare_full_state(const vogl_program_state &rhs) const
2110 {
2111     VOGL_FUNC_TRACER
2112
2113 #define CMP(x)      \
2114     if (x != rhs.x) \
2115         return false;
2116     CMP(m_is_valid);
2117     CMP(m_link_snapshot);
2118
2119     CMP(m_snapshot_handle);
2120     CMP(m_attached_shaders);
2121     CMP(m_attribs);
2122     CMP(m_uniforms);
2123     CMP(m_uniform_blocks);
2124
2125     CMP(m_info_log);
2126
2127     CMP(m_num_active_attribs);
2128     CMP(m_num_active_uniforms);
2129     CMP(m_num_active_uniform_blocks);
2130
2131     CMP(m_transform_feedback_mode);
2132     CMP(m_transform_feedback_num_varyings);
2133     CMP(m_varyings);
2134
2135     CMP(m_marked_for_deletion);
2136     CMP(m_link_status);
2137     CMP(m_verify_status);
2138     CMP(m_outputs);
2139 #undef CMP
2140
2141     if ((m_pLink_time_snapshot.get() != NULL) != (rhs.m_pLink_time_snapshot.get() != NULL))
2142         return false;
2143
2144     if (m_pLink_time_snapshot.get())
2145     {
2146         if (!m_pLink_time_snapshot->compare_full_state(*rhs.m_pLink_time_snapshot.get()))
2147             return false;
2148     }
2149
2150     if (m_shaders.size() != rhs.m_shaders.size())
2151         return false;
2152     for (uint i = 0; i < m_shaders.size(); i++)
2153         if (!m_shaders[i].compare_full_state(rhs.m_shaders[i]))
2154             return false;
2155
2156     return true;
2157 }
2158
2159 // TODO: This is not currently used for anything, needs more testing.
2160 // true if rhs is more or less GL equivalent (not everything in vogl_program_state is either restorable to a GL program object, or makes sense to restore).
2161 // Note that restoring a program on a different driver may result in the restored state's to not exactly match (due to different optimization levels for example).
2162 bool vogl_program_state::compare_restorable_state(const vogl_gl_object_state &rhs_obj) const
2163 {
2164     VOGL_FUNC_TRACER
2165
2166     if ((!m_is_valid) || (!rhs_obj.is_valid()))
2167         return false;
2168
2169     if (rhs_obj.get_type() != cGLSTProgram)
2170         return false;
2171
2172     const vogl_program_state &rhs = static_cast<const vogl_program_state &>(rhs_obj);
2173
2174     if (this == &rhs)
2175         return true;
2176
2177 #define CMP(x)      \
2178     if (x != rhs.x) \
2179         return false;
2180     CMP(m_link_status);
2181     CMP(m_link_snapshot);
2182     CMP(m_uniform_blocks);
2183 #undef CMP
2184
2185     for (uint i = 0; i < m_attribs.size(); i++)
2186     {
2187         if (m_attribs[i].m_name.begins_with("gl_", true))
2188             continue;
2189
2190         uint j;
2191         for (j = 0; j < rhs.m_attribs.size(); j++)
2192             if (m_attribs[i].m_name.compare(rhs.m_attribs[j].m_name, true) == 0)
2193                 break;
2194         if (j == rhs.m_attribs.size())
2195             return false;
2196
2197         if (m_attribs[i].m_bound_location != rhs.m_attribs[j].m_bound_location)
2198             return false;
2199         if (m_attribs[i].m_size != rhs.m_attribs[j].m_size)
2200             return false;
2201         if (m_attribs[i].m_type != rhs.m_attribs[j].m_type)
2202             return false;
2203     }
2204
2205     for (uint i = 0; i < m_uniforms.size(); i++)
2206     {
2207         if (m_uniforms[i].m_name.begins_with("gl_", true))
2208             continue;
2209
2210         uint j;
2211         for (j = 0; j < rhs.m_uniforms.size(); j++)
2212             if (m_uniforms[i].m_name.compare(rhs.m_uniforms[j].m_name, true) == 0)
2213                 break;
2214         if (j == rhs.m_uniforms.size())
2215             return false;
2216
2217         if (m_uniforms[i].m_base_location != rhs.m_uniforms[j].m_base_location)
2218             return false;
2219         if (m_uniforms[i].m_size != rhs.m_uniforms[j].m_size)
2220             return false;
2221         if (m_uniforms[i].m_type != rhs.m_uniforms[j].m_type)
2222             return false;
2223         if (m_uniforms[i].m_data != rhs.m_uniforms[j].m_data)
2224             return false;
2225     }
2226
2227     return true;
2228 }
2229
2230 vogl_linked_program_state::vogl_linked_program_state()
2231 {
2232     VOGL_FUNC_TRACER
2233 }
2234
2235 vogl_linked_program_state::vogl_linked_program_state(const vogl_linked_program_state &other)
2236     : m_linked_programs(other.m_linked_programs)
2237 {
2238     VOGL_FUNC_TRACER
2239 }
2240
2241 vogl_linked_program_state::~vogl_linked_program_state()
2242 {
2243     VOGL_FUNC_TRACER
2244 }
2245
2246 vogl_linked_program_state &vogl_linked_program_state::operator=(const vogl_linked_program_state &rhs)
2247 {
2248     VOGL_FUNC_TRACER
2249
2250     if (this == &rhs)
2251         return *this;
2252
2253     m_linked_programs = rhs.m_linked_programs;
2254
2255     return *this;
2256 }
2257
2258 void vogl_linked_program_state::clear()
2259 {
2260     VOGL_FUNC_TRACER
2261
2262     m_linked_programs.clear();
2263 }
2264
2265 bool vogl_linked_program_state::add_snapshot(GLuint handle, const vogl_program_state &prog)
2266 {
2267     VOGL_FUNC_TRACER
2268
2269     m_linked_programs[handle] = prog;
2270     return true;
2271 }
2272
2273 bool vogl_linked_program_state::add_snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint handle, GLenum binary_format, const void *pBinary, uint binary_size)
2274 {
2275     VOGL_FUNC_TRACER
2276
2277     vogl_program_state_map::insert_result res(m_linked_programs.insert(handle));
2278
2279     vogl_program_state &prog = (res.first)->second;
2280
2281     if (!prog.link_snapshot(context_info, remapper, handle, pBinary, binary_size, binary_format))
2282     {
2283         m_linked_programs.erase(handle);
2284         return false;
2285     }
2286
2287     return true;
2288 }
2289
2290 bool vogl_linked_program_state::remove_snapshot(GLuint handle)
2291 {
2292     VOGL_FUNC_TRACER
2293
2294     return m_linked_programs.erase(handle);
2295 }
2296
2297 const vogl_program_state *vogl_linked_program_state::find_snapshot(GLuint handle) const
2298 {
2299     VOGL_FUNC_TRACER
2300
2301     return m_linked_programs.find_value(handle);
2302 }
2303
2304 vogl_program_state *vogl_linked_program_state::find_snapshot(GLuint handle)
2305 {
2306     VOGL_FUNC_TRACER
2307
2308     return m_linked_programs.find_value(handle);
2309 }
2310
2311 bool vogl_linked_program_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
2312 {
2313     VOGL_FUNC_TRACER
2314
2315     node.init_array();
2316
2317     for (vogl_program_state_map::const_iterator it = m_linked_programs.begin(); it != m_linked_programs.end(); ++it)
2318     {
2319         VOGL_ASSERT(it->first == it->second.get_snapshot_handle());
2320
2321         if (!it->second.serialize(node.add_object(), blob_manager))
2322             return false;
2323     }
2324
2325     return true;
2326 }
2327
2328 bool vogl_linked_program_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
2329 {
2330     VOGL_FUNC_TRACER
2331
2332     clear();
2333
2334     if (!node.is_array() || !node.are_all_children_objects())
2335     {
2336         VOGL_ASSERT_ALWAYS;
2337
2338         return false;
2339     }
2340
2341     vogl_program_state prog;
2342
2343     for (uint i = 0; i < node.size(); i++)
2344     {
2345         if (!prog.deserialize(*node.get_value_as_object(i), blob_manager))
2346         {
2347             VOGL_ASSERT_ALWAYS;
2348
2349             clear();
2350             return false;
2351         }
2352
2353         if (static_cast<uint32>(prog.get_snapshot_handle()) != prog.get_snapshot_handle())
2354         {
2355             VOGL_ASSERT_ALWAYS;
2356
2357             clear();
2358             return false;
2359         }
2360
2361         if (!m_linked_programs.insert(static_cast<uint32>(prog.get_snapshot_handle()), prog).second)
2362         {
2363             VOGL_ASSERT_ALWAYS;
2364
2365             clear();
2366             return false;
2367         }
2368     }
2369
2370     return true;
2371 }
2372
2373 bool vogl_linked_program_state::remap_handles(vogl_handle_remapper &remapper)
2374 {
2375     VOGL_FUNC_TRACER
2376
2377     vogl_program_state_map new_linked_programs;
2378
2379     for (vogl_program_state_map::iterator it = m_linked_programs.begin(); it != m_linked_programs.end(); ++it)
2380     {
2381         VOGL_ASSERT(it->first == it->second.get_snapshot_handle());
2382
2383         GLuint new_handle = static_cast<uint32>(remapper.remap_handle(VOGL_NAMESPACE_PROGRAMS, it->first));
2384
2385         vogl_program_state new_prog_state(it->second);
2386         if (!new_prog_state.remap_handles(remapper))
2387             return false;
2388
2389         VOGL_ASSERT(new_handle == new_prog_state.get_snapshot_handle());
2390
2391         if (!new_linked_programs.insert(new_handle, new_prog_state).second)
2392             return false;
2393     }
2394
2395     m_linked_programs.swap(new_linked_programs);
2396
2397     return true;
2398 }