]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_fbo_state.cpp
Initial vogl checkin
[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 handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_RENDER_BUFFERS, attachment_obj.get_handle()));
371                     if (!handle)
372                         goto handle_error;
373
374                     GL_ENTRYPOINT(glFramebufferRenderbuffer)(GL_DRAW_FRAMEBUFFER, attachment_target, GL_RENDERBUFFER, handle);
375                     VOGL_CHECK_GL_ERROR;
376
377                     break;
378                 }
379                 case GL_TEXTURE:
380                 {
381                     GLuint handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_TEXTURES, attachment_obj.get_handle()));
382                     if (!handle)
383                         goto handle_error;
384
385                     //const GLenum tex_target = vogl_determine_texture_target(context_info, handle);
386                     GLenum tex_target = GL_NONE;
387                     if (!remapper.determine_to_object_target(VOGL_NAMESPACE_TEXTURES, handle, tex_target))
388                     {
389                         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);
390                         goto handle_error;
391                     }
392
393                     const int cube_map_face = attachment_obj.get_param(GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, 0);
394                     const int layer = attachment_obj.get_param(GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER, 0);
395                     const int level = attachment_obj.get_param(GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, 0);
396                     const int layered = attachment_obj.get_param(GL_FRAMEBUFFER_ATTACHMENT_LAYERED, GL_FALSE);
397
398                     if (layered)
399                     {
400                         // GL_FRAMEBUFFER_ATTACHMENT_LAYERED can only be true if glFramebufferTexture was used to attach.
401                         GL_ENTRYPOINT(glFramebufferTexture)(GL_DRAW_FRAMEBUFFER, attachment_target, handle, level);
402                     }
403                     else
404                     {
405                         switch (tex_target)
406                         {
407                             case GL_TEXTURE_1D:
408                             {
409                                 GL_ENTRYPOINT(glFramebufferTexture1D)(GL_DRAW_FRAMEBUFFER, attachment_target, tex_target, handle, level);
410                                 break;
411                             }
412                             case GL_TEXTURE_2D:
413                             case GL_TEXTURE_2D_MULTISAMPLE:
414                             case GL_TEXTURE_RECTANGLE:
415                             {
416                                 GL_ENTRYPOINT(glFramebufferTexture2D)(GL_DRAW_FRAMEBUFFER, attachment_target, tex_target, handle, level);
417                                 break;
418                             }
419                             case GL_TEXTURE_CUBE_MAP:
420                             {
421                                 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) ||
422                                               (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));
423
424                                 GL_ENTRYPOINT(glFramebufferTexture2D)(GL_DRAW_FRAMEBUFFER, attachment_target, cube_map_face, handle, level);
425                                 break;
426                             }
427                             case GL_TEXTURE_3D:
428                             case GL_TEXTURE_1D_ARRAY:
429                             case GL_TEXTURE_2D_ARRAY:
430                             case GL_TEXTURE_CUBE_MAP_ARRAY:
431                             case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
432                             {
433                                 GL_ENTRYPOINT(glFramebufferTextureLayer)(GL_DRAW_FRAMEBUFFER, attachment_target, handle, level, layer);
434                                 break;
435                             }
436                             default:
437                             {
438                                 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);
439                                 goto handle_error;
440                             }
441                         }
442                     }
443
444                     VOGL_CHECK_GL_ERROR;
445                     break;
446                 }
447                 default:
448                 {
449                     goto handle_error;
450                 }
451             }
452         }
453
454         int last_valid_draw_buffer_idx;
455         for (last_valid_draw_buffer_idx = m_draw_buffers.size() - 1; last_valid_draw_buffer_idx >= 0; last_valid_draw_buffer_idx--)
456             if (m_draw_buffers[last_valid_draw_buffer_idx] != GL_NONE)
457                 break;
458
459         uint num_valid_draw_buffers = last_valid_draw_buffer_idx + 1;
460
461         if (!num_valid_draw_buffers)
462         {
463             GL_ENTRYPOINT(glDrawBuffer)(GL_NONE);
464             VOGL_CHECK_GL_ERROR;
465         }
466         else if (num_valid_draw_buffers == 1)
467         {
468             GL_ENTRYPOINT(glDrawBuffer)(m_draw_buffers[0]);
469             VOGL_CHECK_GL_ERROR;
470         }
471         else
472         {
473             GL_ENTRYPOINT(glDrawBuffers)(m_draw_buffers.size(), m_draw_buffers.get_ptr());
474             VOGL_CHECK_GL_ERROR;
475         }
476
477         GL_ENTRYPOINT(glReadBuffer)(m_read_buffer);
478         VOGL_CHECK_GL_ERROR;
479
480         GLenum cur_status;
481         cur_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_DRAW_FRAMEBUFFER);
482         VOGL_CHECK_GL_ERROR;
483
484         if (cur_status != m_status)
485         {
486             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));
487         }
488     }
489
490     return true;
491
492 handle_error:
493     if ((handle) && (created_handle))
494     {
495         remapper.delete_handle_and_object(VOGL_NAMESPACE_FRAMEBUFFERS, m_snapshot_handle, handle);
496
497         //GLuint handle32 = static_cast<GLuint>(handle);
498         //GL_ENTRYPOINT(glDeleteFramebuffers)(1, &handle32);
499         //VOGL_CHECK_GL_ERROR;
500
501         handle = 0;
502     }
503
504     return false;
505 }
506
507 void vogl_framebuffer_state::clear()
508 {
509     VOGL_FUNC_TRACER
510
511     m_snapshot_handle = 0;
512     m_has_been_bound = false;
513
514     m_attachments.clear();
515
516     m_draw_buffers.clear();
517     m_read_buffer = GL_NONE;
518
519     m_status = GL_FRAMEBUFFER_COMPLETE;
520
521     m_is_valid = false;
522 }
523
524 bool vogl_framebuffer_state::remap_handles(vogl_handle_remapper &remapper)
525 {
526     VOGL_FUNC_TRACER
527
528     if (!m_is_valid)
529         return false;
530
531     m_snapshot_handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_FRAMEBUFFERS, m_snapshot_handle));
532
533     bool success = true;
534
535     for (GLenum_to_attachment_map::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
536         if (!it->second.remap_handles(remapper))
537             success = false;
538
539     return success;
540 }
541
542 bool vogl_framebuffer_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
543 {
544     VOGL_FUNC_TRACER
545
546     VOGL_NOTE_UNUSED(blob_manager);
547
548     if (!m_is_valid)
549         return false;
550
551     node.add_key_value("handle", m_snapshot_handle);
552     node.add_key_value("has_been_bound", m_has_been_bound);
553     node.add_key_value("status", g_gl_enums.find_name(m_status, "gl"));
554
555     node.add_key_value("read_buffer", m_read_buffer);
556
557     json_node &draw_buffers_array = node.add_array("draw_buffers");
558     for (uint i = 0; i < m_draw_buffers.size(); i++)
559         draw_buffers_array.add_value(m_draw_buffers[i]);
560
561     json_node &attachments_array = node.add_array("attachments");
562     for (GLenum_to_attachment_map::const_iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
563         if (!it->second.serialize(attachments_array.add_object()))
564             return false;
565
566     return true;
567 }
568
569 bool vogl_framebuffer_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
570 {
571     VOGL_FUNC_TRACER
572
573     VOGL_NOTE_UNUSED(blob_manager);
574
575     clear();
576
577     m_snapshot_handle = node.value_as_int("handle");
578     m_has_been_bound = node.value_as_bool("has_been_bound", true);
579     m_status = vogl_get_json_value_as_enum(node, "status");
580
581     m_read_buffer = node.value_as_uint32("read_buffer");
582
583     const json_node *pDraw_buffers_array = node.find_child_array("draw_buffers");
584     if ((pDraw_buffers_array) && (pDraw_buffers_array->are_all_children_values()))
585     {
586         m_draw_buffers.resize(pDraw_buffers_array->size());
587
588         for (uint i = 0; i < m_draw_buffers.size(); i++)
589             m_draw_buffers[i] = pDraw_buffers_array->value_as_uint32(i);
590     }
591
592     const json_node *pAttachments_array = node.find_child_array("attachments");
593     if (pAttachments_array)
594     {
595         for (uint i = 0; i < pAttachments_array->size(); i++)
596         {
597             const json_node *pAttachment = pAttachments_array->get_child(i);
598             if (pAttachment)
599             {
600                 GLenum attachment = vogl_get_json_value_as_enum(*pAttachment, "attachment");
601                 if (attachment != GL_NONE)
602                 {
603                     if (!m_attachments[attachment].deserialize(*pAttachment))
604                         return false;
605                 }
606             }
607         }
608     }
609
610     m_is_valid = true;
611
612     return true;
613 }
614
615 bool vogl_framebuffer_state::compare_restorable_state(const vogl_gl_object_state &rhs_obj) const
616 {
617     VOGL_FUNC_TRACER
618
619     if ((!m_is_valid) || (!rhs_obj.is_valid()))
620         return false;
621
622     if (rhs_obj.get_type() != cGLSTFramebuffer)
623         return false;
624
625     const vogl_framebuffer_state &rhs = static_cast<const vogl_framebuffer_state &>(rhs_obj);
626
627     if (this == &rhs)
628         return true;
629
630     if (m_status != rhs.m_status)
631         return false;
632
633     if (m_has_been_bound != rhs.m_has_been_bound)
634         return false;
635
636     if (m_read_buffer != rhs.m_read_buffer)
637         return false;
638
639     if (m_draw_buffers != rhs.m_draw_buffers)
640         return false;
641
642     return m_attachments == rhs.m_attachments;
643 }