]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_fbo_state.cpp
- Fix for restoring FBO's that have attachments that the tracer was not able to query...
[vogl] / src / voglcommon / vogl_fbo_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_fbo_state.cpp
27 #include "vogl_common.h"
28 #include "vogl_fbo_state.h"
29
30 vogl_framebuffer_attachment::vogl_framebuffer_attachment()
31     : m_attachment(GL_NONE),
32       m_type(GL_NONE)
33 {
34     VOGL_FUNC_TRACER
35 }
36
37 vogl_framebuffer_attachment::~vogl_framebuffer_attachment()
38 {
39     VOGL_FUNC_TRACER
40 }
41
42 bool vogl_framebuffer_attachment::snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLenum attachment, GLenum type)
43 {
44     VOGL_FUNC_TRACER
45
46     VOGL_NOTE_UNUSED(remapper);
47     VOGL_NOTE_UNUSED(context_info);
48
49     clear();
50
51     m_attachment = attachment;
52
53     m_type = type;
54
55 #define DO_QUERY(e)                                                                                \
56     do                                                                                             \
57     {                                                                                              \
58         int val = 0;                                                                               \
59         GL_ENTRYPOINT(glGetFramebufferAttachmentParameteriv)(GL_FRAMEBUFFER, attachment, e, &val); \
60         VOGL_CHECK_GL_ERROR;                                                                        \
61         m_params.insert(e, val);                                                                   \
62     } while (0)
63
64     // TODO: Is this query really valid on default framebuffer FBO's?
65     DO_QUERY(GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
66
67     static const GLenum s_common_queries[] =
68         {
69             GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
70             GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
71         };
72
73     for (uint i = 0; i < VOGL_ARRAY_SIZE(s_common_queries); i++)
74     {
75         DO_QUERY(s_common_queries[i]);
76     }
77
78     if (m_type == GL_TEXTURE)
79     {
80         DO_QUERY(GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL);
81         DO_QUERY(GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE);
82         DO_QUERY(GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER);
83         DO_QUERY(GL_FRAMEBUFFER_ATTACHMENT_LAYERED);
84     }
85
86 #undef DO_QUERY
87
88     return true;
89 }
90
91 bool vogl_framebuffer_attachment::remap_handles(vogl_handle_remapper &remapper)
92 {
93     VOGL_FUNC_TRACER
94
95     if (!m_params.contains(GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME))
96         return false;
97
98     if (m_type == GL_RENDERBUFFER)
99         m_params[GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME] = static_cast<uint>(remapper.remap_handle(VOGL_NAMESPACE_RENDER_BUFFERS, get_handle()));
100     else if (m_type == GL_TEXTURE)
101         m_params[GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME] = static_cast<uint>(remapper.remap_handle(VOGL_NAMESPACE_TEXTURES, get_handle()));
102
103     return true;
104 }
105
106 void vogl_framebuffer_attachment::clear()
107 {
108     VOGL_FUNC_TRACER
109
110     m_attachment = GL_NONE;
111     m_type = GL_NONE;
112     m_params.clear();
113 }
114
115 bool vogl_framebuffer_attachment::serialize(json_node &node) const
116 {
117     VOGL_FUNC_TRACER
118
119     node.add_key_value("attachment", g_gl_enums.find_name(m_attachment, "gl"));
120     node.add_key_value("type", g_gl_enums.find_name(m_type, "gl"));
121
122     for (GLenum_to_int_map::const_iterator it = m_params.begin(); it != m_params.end(); ++it)
123     {
124         const char *pEnum_name = g_gl_enums.find_name(it->first, "gl");
125
126         if (g_gl_enums.get_pname_type(it->first) == cSTGLenum)
127             node.add_key_value(pEnum_name, g_gl_enums.find_name(it->second, "gl"));
128         else
129             node.add_key_value(pEnum_name, it->second);
130     }
131
132     return true;
133 }
134
135 bool vogl_framebuffer_attachment::deserialize(const json_node &node)
136 {
137     VOGL_FUNC_TRACER
138
139     clear();
140
141     m_attachment = vogl_get_json_value_as_enum(node, "attachment");
142     m_type = vogl_get_json_value_as_enum(node, "type");
143
144     for (uint i = 0; i < node.size(); i++)
145     {
146         const dynamic_string &key = node.get_key(i);
147         const json_value &val = node.get_value(i);
148
149         if (key == "attachment")
150             m_attachment = vogl_get_json_value_as_enum(val);
151         else if (key == "type")
152             m_type = vogl_get_json_value_as_enum(val);
153         else
154         {
155             uint64_t enum_val = g_gl_enums.find_enum(key);
156             if ((enum_val == gl_enums::cUnknownEnum) || (enum_val > cUINT32_MAX))
157                 vogl_warning_printf("%s: Invalid enum \"%s\"\n", VOGL_METHOD_NAME, key.get_ptr());
158             else
159             {
160                 if (g_gl_enums.get_pname_type(enum_val) == cSTGLenum)
161                     m_params.insert(static_cast<GLenum>(enum_val), vogl_get_json_value_as_enum(val));
162                 else
163                     m_params.insert(static_cast<GLenum>(enum_val), val.as_int());
164             }
165         }
166     }
167
168     return true;
169 }
170
171 bool vogl_framebuffer_attachment::operator==(const vogl_framebuffer_attachment &rhs) const
172 {
173     VOGL_FUNC_TRACER
174
175     if (m_attachment != rhs.m_attachment)
176         return false;
177     if (m_type != rhs.m_type)
178         return false;
179     return m_params == rhs.m_params;
180 }
181
182 vogl_framebuffer_state::vogl_framebuffer_state()
183     : m_snapshot_handle(0),
184       m_has_been_bound(false),
185       m_read_buffer(GL_NONE),
186       m_status(GL_FRAMEBUFFER_COMPLETE),
187       m_is_valid(false)
188 {
189     VOGL_FUNC_TRACER
190 }
191
192 vogl_framebuffer_state::~vogl_framebuffer_state()
193 {
194     VOGL_FUNC_TRACER
195
196     clear();
197 }
198
199 bool vogl_framebuffer_state::snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 handle, GLenum target)
200 {
201     VOGL_FUNC_TRACER
202
203     VOGL_CHECK_GL_ERROR;
204     VOGL_NOTE_UNUSED(target);
205
206     clear();
207
208     VOGL_ASSERT(handle <= cUINT32_MAX);
209
210     m_snapshot_handle = static_cast<GLuint>(handle);
211     m_has_been_bound = GL_ENTRYPOINT(glIsFramebuffer)(m_snapshot_handle) != 0;
212
213     if (m_has_been_bound)
214     {
215         vogl_scoped_binding_state orig_framebuffers(GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER);
216
217         GL_ENTRYPOINT(glBindFramebuffer)(GL_FRAMEBUFFER, m_snapshot_handle);
218         VOGL_CHECK_GL_ERROR;
219
220         // These are per-framebuffer states, not per-context, so save them.
221         uint max_draw_buffers = vogl_get_gl_integer(GL_MAX_DRAW_BUFFERS);
222         VOGL_CHECK_GL_ERROR;
223
224         m_draw_buffers.resize(max_draw_buffers);
225         for (uint i = 0; i < max_draw_buffers; i++)
226         {
227             m_draw_buffers[i] = vogl_get_gl_integer(GL_DRAW_BUFFER0 + i);
228             VOGL_CHECK_GL_ERROR;
229         }
230
231         m_read_buffer = vogl_get_gl_integer(GL_READ_BUFFER);
232         VOGL_CHECK_GL_ERROR;
233
234         // TODO: Read GL_FRAMEBUFFER_DEFAULT_WIDTH, etc.
235
236         m_status = GL_FRAMEBUFFER_COMPLETE;
237         if (handle)
238         {
239             m_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_DRAW_FRAMEBUFFER);
240             VOGL_CHECK_GL_ERROR;
241         }
242
243         int max_color_attachments = 0;
244         GL_ENTRYPOINT(glGetIntegerv)(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
245         VOGL_CHECK_GL_ERROR;
246
247         const GLenum s_default_attachments[] =
248             {
249                 GL_FRONT_LEFT, GL_FRONT_RIGHT, GL_BACK_LEFT, GL_BACK_RIGHT, GL_DEPTH, GL_STENCIL
250             };
251
252         const GLenum s_framebuffer_attachments[] =
253             {
254                 GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5, GL_COLOR_ATTACHMENT6, GL_COLOR_ATTACHMENT7,
255                 GL_COLOR_ATTACHMENT8, GL_COLOR_ATTACHMENT9, GL_COLOR_ATTACHMENT10, GL_COLOR_ATTACHMENT11, GL_COLOR_ATTACHMENT12, GL_COLOR_ATTACHMENT13, GL_COLOR_ATTACHMENT14, GL_COLOR_ATTACHMENT15,
256                 GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT
257             };
258
259         const GLenum *pAttachments = s_default_attachments;
260         uint num_attachments = VOGL_ARRAY_SIZE(s_default_attachments);
261
262         if (handle)
263         {
264             pAttachments = s_framebuffer_attachments;
265             num_attachments = VOGL_ARRAY_SIZE(s_framebuffer_attachments);
266         }
267
268         for (uint i = 0; i < num_attachments; i++)
269         {
270             GLenum attachment = pAttachments[i];
271
272             if ((attachment >= GL_COLOR_ATTACHMENT0) && (attachment <= GL_COLOR_ATTACHMENT15))
273             {
274                 if (attachment > static_cast<uint>((GL_COLOR_ATTACHMENT0 + max_color_attachments - 1)))
275                     continue;
276             }
277
278             int obj_type = 0;
279             GL_ENTRYPOINT(glGetFramebufferAttachmentParameteriv)(GL_FRAMEBUFFER, attachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &obj_type);
280             VOGL_CHECK_GL_ERROR;
281
282             switch (obj_type)
283             {
284                 case GL_NONE:
285                 {
286                     break;
287                 }
288                 case GL_FRAMEBUFFER_DEFAULT:
289                 case GL_TEXTURE:
290                 case GL_RENDERBUFFER:
291                 {
292                     if (!m_attachments[attachment].snapshot(context_info, remapper, attachment, obj_type))
293                         return false;
294                     break;
295                 }
296                 default:
297                 {
298                     VOGL_ASSERT_ALWAYS;
299                     break;
300                 }
301             }
302         }
303     }
304
305     m_is_valid = true;
306
307     return true;
308 }
309
310 bool vogl_framebuffer_state::restore(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 &handle) const
311 {
312     VOGL_FUNC_TRACER
313
314     VOGL_NOTE_UNUSED(context_info);
315
316     VOGL_CHECK_GL_ERROR;
317
318     if (!m_is_valid)
319         return false;
320
321     if (!m_snapshot_handle)
322     {
323         // Can't restore the default framebuffer
324         VOGL_ASSERT_ALWAYS;
325         return false;
326     }
327
328     bool created_handle = false;
329
330     if (!handle)
331     {
332         GLuint handle32 = 0;
333         GL_ENTRYPOINT(glGenFramebuffers)(1, &handle32);
334         if ((vogl_check_gl_error()) || (!handle32))
335             return false;
336
337         handle = handle32;
338
339         remapper.declare_handle(VOGL_NAMESPACE_FRAMEBUFFERS, m_snapshot_handle, handle, GL_NONE);
340         VOGL_ASSERT(remapper.remap_handle(VOGL_NAMESPACE_FRAMEBUFFERS, m_snapshot_handle) == handle);
341
342         created_handle = true;
343     }
344
345     VOGL_ASSERT(handle <= cUINT32_MAX);
346
347     if (m_has_been_bound)
348     {
349         // The order of these saves/restores matters because the draw and read buffers affect the currently bound framebuffer.
350         vogl_scoped_binding_state binding_state(GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER);
351
352         GL_ENTRYPOINT(glBindFramebuffer)(GL_FRAMEBUFFER, static_cast<GLuint>(handle));
353         if (vogl_check_gl_error())
354             goto handle_error;
355
356         for (GLenum_to_attachment_map::const_iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
357         {
358             const vogl_framebuffer_attachment &attachment_obj = it->second;
359             const GLenum attachment_target = attachment_obj.get_attachment();
360             const GLenum attachment_type = attachment_obj.get_type();
361
362             switch (attachment_type)
363             {
364                 case GL_FRAMEBUFFER_DEFAULT:
365                 {
366                     goto handle_error;
367                 }
368                 case GL_RENDERBUFFER:
369                 {
370                     GLuint trace_handle = attachment_obj.get_handle();
371                     if (trace_handle)
372                     {
373                         GLuint handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_RENDER_BUFFERS, trace_handle));
374                         if (!handle)
375                             goto handle_error;
376
377                         GL_ENTRYPOINT(glFramebufferRenderbuffer)(GL_DRAW_FRAMEBUFFER, attachment_target, GL_RENDERBUFFER, handle);
378                         VOGL_CHECK_GL_ERROR;
379                     }
380
381                     break;
382                 }
383                 case GL_TEXTURE:
384                 {
385                     GLuint trace_handle = attachment_obj.get_handle();
386                     if (trace_handle)
387                     {
388                         GLuint handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_TEXTURES, trace_handle));
389                         if (!handle)
390                             goto handle_error;
391
392                         //const GLenum tex_target = vogl_determine_texture_target(context_info, handle);
393                         GLenum tex_target = GL_NONE;
394                         if (!remapper.determine_to_object_target(VOGL_NAMESPACE_TEXTURES, handle, tex_target))
395                         {
396                             vogl_error_printf("%s: Failed determining FBO texture attachment's target, trace FBO handle %u GL handle %u, trace texture handle %u GL handle %u\n", VOGL_METHOD_NAME, m_snapshot_handle, static_cast<uint32>(handle), attachment_obj.get_handle(), handle);
397                             goto handle_error;
398                         }
399
400                         const int cube_map_face = attachment_obj.get_param(GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, 0);
401                         const int layer = attachment_obj.get_param(GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER, 0);
402                         const int level = attachment_obj.get_param(GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, 0);
403                         const int layered = attachment_obj.get_param(GL_FRAMEBUFFER_ATTACHMENT_LAYERED, GL_FALSE);
404
405                         if (layered)
406                         {
407                             // GL_FRAMEBUFFER_ATTACHMENT_LAYERED can only be true if glFramebufferTexture was used to attach.
408                             GL_ENTRYPOINT(glFramebufferTexture)(GL_DRAW_FRAMEBUFFER, attachment_target, handle, level);
409                         }
410                         else
411                         {
412                             switch (tex_target)
413                             {
414                                 case GL_TEXTURE_1D:
415                                 {
416                                     GL_ENTRYPOINT(glFramebufferTexture1D)(GL_DRAW_FRAMEBUFFER, attachment_target, tex_target, handle, level);
417                                     break;
418                                 }
419                                 case GL_TEXTURE_2D:
420                                 case GL_TEXTURE_2D_MULTISAMPLE:
421                                 case GL_TEXTURE_RECTANGLE:
422                                 {
423                                     GL_ENTRYPOINT(glFramebufferTexture2D)(GL_DRAW_FRAMEBUFFER, attachment_target, tex_target, handle, level);
424                                     break;
425                                 }
426                                 case GL_TEXTURE_CUBE_MAP:
427                                 {
428                                     VOGL_ASSERT((cube_map_face == GL_TEXTURE_CUBE_MAP_POSITIVE_X) || (cube_map_face == GL_TEXTURE_CUBE_MAP_POSITIVE_Y) || (cube_map_face == GL_TEXTURE_CUBE_MAP_POSITIVE_Z) ||
429                                                   (cube_map_face == GL_TEXTURE_CUBE_MAP_NEGATIVE_X) || (cube_map_face == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y) || (cube_map_face == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z));
430
431                                     GL_ENTRYPOINT(glFramebufferTexture2D)(GL_DRAW_FRAMEBUFFER, attachment_target, cube_map_face, handle, level);
432                                     break;
433                                 }
434                                 case GL_TEXTURE_3D:
435                                 case GL_TEXTURE_1D_ARRAY:
436                                 case GL_TEXTURE_2D_ARRAY:
437                                 case GL_TEXTURE_CUBE_MAP_ARRAY:
438                                 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
439                                 {
440                                     GL_ENTRYPOINT(glFramebufferTextureLayer)(GL_DRAW_FRAMEBUFFER, attachment_target, handle, level, layer);
441                                     break;
442                                 }
443                                 default:
444                                 {
445                                     vogl_error_printf("%s: Don't know how to attach texture with target %s to FBO, trace FBO handle %u GL handle %u, trace texture handle %u GL handle %u\n", VOGL_METHOD_NAME, g_gl_enums.find_gl_name(tex_target), m_snapshot_handle, static_cast<uint32>(handle), attachment_obj.get_handle(), handle);
446                                     goto handle_error;
447                                 }
448                             }
449                         }
450
451                         VOGL_CHECK_GL_ERROR;
452                     }
453
454                     break;
455                 }
456                 default:
457                 {
458                     goto handle_error;
459                 }
460             }
461         }
462
463         int last_valid_draw_buffer_idx;
464         for (last_valid_draw_buffer_idx = m_draw_buffers.size() - 1; last_valid_draw_buffer_idx >= 0; last_valid_draw_buffer_idx--)
465             if (m_draw_buffers[last_valid_draw_buffer_idx] != GL_NONE)
466                 break;
467
468         uint num_valid_draw_buffers = last_valid_draw_buffer_idx + 1;
469
470         if (!num_valid_draw_buffers)
471         {
472             GL_ENTRYPOINT(glDrawBuffer)(GL_NONE);
473             VOGL_CHECK_GL_ERROR;
474         }
475         else if (num_valid_draw_buffers == 1)
476         {
477             GL_ENTRYPOINT(glDrawBuffer)(m_draw_buffers[0]);
478             VOGL_CHECK_GL_ERROR;
479         }
480         else
481         {
482             GL_ENTRYPOINT(glDrawBuffers)(m_draw_buffers.size(), m_draw_buffers.get_ptr());
483             VOGL_CHECK_GL_ERROR;
484         }
485
486         GL_ENTRYPOINT(glReadBuffer)(m_read_buffer);
487         VOGL_CHECK_GL_ERROR;
488
489         GLenum cur_status;
490         cur_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_DRAW_FRAMEBUFFER);
491         VOGL_CHECK_GL_ERROR;
492
493         if (cur_status != m_status)
494         {
495             vogl_error_printf("%s: Restored FBO's completeness (%s) is not the same as the trace's (%s), trace handle %u GL handle %u!\n", VOGL_METHOD_NAME, g_gl_enums.find_name(cur_status, "gl"), g_gl_enums.find_name(m_status, "gl"), m_snapshot_handle, static_cast<uint32>(handle));
496         }
497     }
498
499     return true;
500
501 handle_error:
502     if ((handle) && (created_handle))
503     {
504         remapper.delete_handle_and_object(VOGL_NAMESPACE_FRAMEBUFFERS, m_snapshot_handle, handle);
505
506         //GLuint handle32 = static_cast<GLuint>(handle);
507         //GL_ENTRYPOINT(glDeleteFramebuffers)(1, &handle32);
508         //VOGL_CHECK_GL_ERROR;
509
510         handle = 0;
511     }
512
513     return false;
514 }
515
516 void vogl_framebuffer_state::clear()
517 {
518     VOGL_FUNC_TRACER
519
520     m_snapshot_handle = 0;
521     m_has_been_bound = false;
522
523     m_attachments.clear();
524
525     m_draw_buffers.clear();
526     m_read_buffer = GL_NONE;
527
528     m_status = GL_FRAMEBUFFER_COMPLETE;
529
530     m_is_valid = false;
531 }
532
533 bool vogl_framebuffer_state::remap_handles(vogl_handle_remapper &remapper)
534 {
535     VOGL_FUNC_TRACER
536
537     if (!m_is_valid)
538         return false;
539
540     m_snapshot_handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_FRAMEBUFFERS, m_snapshot_handle));
541
542     bool success = true;
543
544     for (GLenum_to_attachment_map::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
545         if (!it->second.remap_handles(remapper))
546             success = false;
547
548     return success;
549 }
550
551 bool vogl_framebuffer_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
552 {
553     VOGL_FUNC_TRACER
554
555     VOGL_NOTE_UNUSED(blob_manager);
556
557     if (!m_is_valid)
558         return false;
559
560     node.add_key_value("handle", m_snapshot_handle);
561     node.add_key_value("has_been_bound", m_has_been_bound);
562     node.add_key_value("status", g_gl_enums.find_name(m_status, "gl"));
563
564     node.add_key_value("read_buffer", m_read_buffer);
565
566     json_node &draw_buffers_array = node.add_array("draw_buffers");
567     for (uint i = 0; i < m_draw_buffers.size(); i++)
568         draw_buffers_array.add_value(m_draw_buffers[i]);
569
570     json_node &attachments_array = node.add_array("attachments");
571     for (GLenum_to_attachment_map::const_iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
572         if (!it->second.serialize(attachments_array.add_object()))
573             return false;
574
575     return true;
576 }
577
578 bool vogl_framebuffer_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
579 {
580     VOGL_FUNC_TRACER
581
582     VOGL_NOTE_UNUSED(blob_manager);
583
584     clear();
585
586     m_snapshot_handle = node.value_as_int("handle");
587     m_has_been_bound = node.value_as_bool("has_been_bound", true);
588     m_status = vogl_get_json_value_as_enum(node, "status");
589
590     m_read_buffer = node.value_as_uint32("read_buffer");
591
592     const json_node *pDraw_buffers_array = node.find_child_array("draw_buffers");
593     if ((pDraw_buffers_array) && (pDraw_buffers_array->are_all_children_values()))
594     {
595         m_draw_buffers.resize(pDraw_buffers_array->size());
596
597         for (uint i = 0; i < m_draw_buffers.size(); i++)
598             m_draw_buffers[i] = pDraw_buffers_array->value_as_uint32(i);
599     }
600
601     const json_node *pAttachments_array = node.find_child_array("attachments");
602     if (pAttachments_array)
603     {
604         for (uint i = 0; i < pAttachments_array->size(); i++)
605         {
606             const json_node *pAttachment = pAttachments_array->get_child(i);
607             if (pAttachment)
608             {
609                 GLenum attachment = vogl_get_json_value_as_enum(*pAttachment, "attachment");
610                 if (attachment != GL_NONE)
611                 {
612                     if (!m_attachments[attachment].deserialize(*pAttachment))
613                         return false;
614                 }
615             }
616         }
617     }
618
619     m_is_valid = true;
620
621     return true;
622 }
623
624 bool vogl_framebuffer_state::compare_restorable_state(const vogl_gl_object_state &rhs_obj) const
625 {
626     VOGL_FUNC_TRACER
627
628     if ((!m_is_valid) || (!rhs_obj.is_valid()))
629         return false;
630
631     if (rhs_obj.get_type() != cGLSTFramebuffer)
632         return false;
633
634     const vogl_framebuffer_state &rhs = static_cast<const vogl_framebuffer_state &>(rhs_obj);
635
636     if (this == &rhs)
637         return true;
638
639     if (m_status != rhs.m_status)
640         return false;
641
642     if (m_has_been_bound != rhs.m_has_been_bound)
643         return false;
644
645     if (m_read_buffer != rhs.m_read_buffer)
646         return false;
647
648     if (m_draw_buffers != rhs.m_draw_buffers)
649         return false;
650
651     return m_attachments == rhs.m_attachments;
652 }