1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 **************************************************************************/
26 // File: vogl_fbo_state.cpp
27 #include "vogl_common.h"
28 #include "vogl_fbo_state.h"
30 vogl_framebuffer_attachment::vogl_framebuffer_attachment()
31 : m_attachment(GL_NONE),
37 vogl_framebuffer_attachment::~vogl_framebuffer_attachment()
42 bool vogl_framebuffer_attachment::snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLenum attachment, GLenum type)
46 VOGL_NOTE_UNUSED(remapper);
47 VOGL_NOTE_UNUSED(context_info);
51 m_attachment = attachment;
59 GL_ENTRYPOINT(glGetFramebufferAttachmentParameteriv)(GL_FRAMEBUFFER, attachment, e, &val); \
60 VOGL_CHECK_GL_ERROR; \
61 m_params.insert(e, val); \
64 // TODO: Is this query really valid on default framebuffer FBO's?
65 DO_QUERY(GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
67 static const GLenum s_common_queries[] =
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
73 for (uint i = 0; i < VOGL_ARRAY_SIZE(s_common_queries); i++)
75 DO_QUERY(s_common_queries[i]);
78 if (m_type == GL_TEXTURE)
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);
91 bool vogl_framebuffer_attachment::remap_handles(vogl_handle_remapper &remapper)
95 if (!m_params.contains(GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME))
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()));
106 void vogl_framebuffer_attachment::clear()
110 m_attachment = GL_NONE;
115 bool vogl_framebuffer_attachment::serialize(json_node &node) const
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"));
122 for (GLenum_to_int_map::const_iterator it = m_params.begin(); it != m_params.end(); ++it)
124 const char *pEnum_name = g_gl_enums.find_name(it->first, "gl");
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"));
129 node.add_key_value(pEnum_name, it->second);
135 bool vogl_framebuffer_attachment::deserialize(const json_node &node)
141 m_attachment = vogl_get_json_value_as_enum(node, "attachment");
142 m_type = vogl_get_json_value_as_enum(node, "type");
144 for (uint i = 0; i < node.size(); i++)
146 const dynamic_string &key = node.get_key(i);
147 const json_value &val = node.get_value(i);
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);
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());
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));
163 m_params.insert(static_cast<GLenum>(enum_val), val.as_int());
171 bool vogl_framebuffer_attachment::operator==(const vogl_framebuffer_attachment &rhs) const
175 if (m_attachment != rhs.m_attachment)
177 if (m_type != rhs.m_type)
179 return m_params == rhs.m_params;
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),
192 vogl_framebuffer_state::~vogl_framebuffer_state()
199 bool vogl_framebuffer_state::snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 handle, GLenum target)
204 VOGL_NOTE_UNUSED(target);
208 VOGL_ASSERT(handle <= cUINT32_MAX);
210 m_snapshot_handle = static_cast<GLuint>(handle);
211 m_has_been_bound = GL_ENTRYPOINT(glIsFramebuffer)(m_snapshot_handle) != 0;
213 if (m_has_been_bound)
215 vogl_scoped_binding_state orig_framebuffers(GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER);
217 GL_ENTRYPOINT(glBindFramebuffer)(GL_FRAMEBUFFER, m_snapshot_handle);
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);
224 m_draw_buffers.resize(max_draw_buffers);
225 for (uint i = 0; i < max_draw_buffers; i++)
227 m_draw_buffers[i] = vogl_get_gl_integer(GL_DRAW_BUFFER0 + i);
231 m_read_buffer = vogl_get_gl_integer(GL_READ_BUFFER);
234 // TODO: Read GL_FRAMEBUFFER_DEFAULT_WIDTH, etc.
236 m_status = GL_FRAMEBUFFER_COMPLETE;
239 m_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_DRAW_FRAMEBUFFER);
243 int max_color_attachments = 0;
244 GL_ENTRYPOINT(glGetIntegerv)(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
247 const GLenum s_default_attachments[] =
249 GL_FRONT_LEFT, GL_FRONT_RIGHT, GL_BACK_LEFT, GL_BACK_RIGHT, GL_DEPTH, GL_STENCIL
252 const GLenum s_framebuffer_attachments[] =
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
259 const GLenum *pAttachments = s_default_attachments;
260 uint num_attachments = VOGL_ARRAY_SIZE(s_default_attachments);
264 pAttachments = s_framebuffer_attachments;
265 num_attachments = VOGL_ARRAY_SIZE(s_framebuffer_attachments);
268 for (uint i = 0; i < num_attachments; i++)
270 GLenum attachment = pAttachments[i];
272 if ((attachment >= GL_COLOR_ATTACHMENT0) && (attachment <= GL_COLOR_ATTACHMENT15))
274 if (attachment > static_cast<uint>((GL_COLOR_ATTACHMENT0 + max_color_attachments - 1)))
279 GL_ENTRYPOINT(glGetFramebufferAttachmentParameteriv)(GL_FRAMEBUFFER, attachment, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &obj_type);
288 case GL_FRAMEBUFFER_DEFAULT:
290 case GL_RENDERBUFFER:
292 if (!m_attachments[attachment].snapshot(context_info, remapper, attachment, obj_type))
310 bool vogl_framebuffer_state::restore(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 &handle) const
314 VOGL_NOTE_UNUSED(context_info);
321 if (!m_snapshot_handle)
323 // Can't restore the default framebuffer
328 bool created_handle = false;
333 GL_ENTRYPOINT(glGenFramebuffers)(1, &handle32);
334 if ((vogl_check_gl_error()) || (!handle32))
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);
342 created_handle = true;
345 VOGL_ASSERT(handle <= cUINT32_MAX);
347 if (m_has_been_bound)
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);
352 GL_ENTRYPOINT(glBindFramebuffer)(GL_FRAMEBUFFER, static_cast<GLuint>(handle));
353 if (vogl_check_gl_error())
356 for (GLenum_to_attachment_map::const_iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
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();
362 switch (attachment_type)
364 case GL_FRAMEBUFFER_DEFAULT:
368 case GL_RENDERBUFFER:
370 GLuint handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_RENDER_BUFFERS, attachment_obj.get_handle()));
374 GL_ENTRYPOINT(glFramebufferRenderbuffer)(GL_DRAW_FRAMEBUFFER, attachment_target, GL_RENDERBUFFER, handle);
381 GLuint handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_TEXTURES, attachment_obj.get_handle()));
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))
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);
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);
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);
409 GL_ENTRYPOINT(glFramebufferTexture1D)(GL_DRAW_FRAMEBUFFER, attachment_target, tex_target, handle, level);
413 case GL_TEXTURE_2D_MULTISAMPLE:
414 case GL_TEXTURE_RECTANGLE:
416 GL_ENTRYPOINT(glFramebufferTexture2D)(GL_DRAW_FRAMEBUFFER, attachment_target, tex_target, handle, level);
419 case GL_TEXTURE_CUBE_MAP:
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));
424 GL_ENTRYPOINT(glFramebufferTexture2D)(GL_DRAW_FRAMEBUFFER, attachment_target, cube_map_face, handle, level);
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:
433 GL_ENTRYPOINT(glFramebufferTextureLayer)(GL_DRAW_FRAMEBUFFER, attachment_target, handle, level, layer);
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);
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)
459 uint num_valid_draw_buffers = last_valid_draw_buffer_idx + 1;
461 if (!num_valid_draw_buffers)
463 GL_ENTRYPOINT(glDrawBuffer)(GL_NONE);
466 else if (num_valid_draw_buffers == 1)
468 GL_ENTRYPOINT(glDrawBuffer)(m_draw_buffers[0]);
473 GL_ENTRYPOINT(glDrawBuffers)(m_draw_buffers.size(), m_draw_buffers.get_ptr());
477 GL_ENTRYPOINT(glReadBuffer)(m_read_buffer);
481 cur_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_DRAW_FRAMEBUFFER);
484 if (cur_status != m_status)
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));
493 if ((handle) && (created_handle))
495 remapper.delete_handle_and_object(VOGL_NAMESPACE_FRAMEBUFFERS, m_snapshot_handle, handle);
497 //GLuint handle32 = static_cast<GLuint>(handle);
498 //GL_ENTRYPOINT(glDeleteFramebuffers)(1, &handle32);
499 //VOGL_CHECK_GL_ERROR;
507 void vogl_framebuffer_state::clear()
511 m_snapshot_handle = 0;
512 m_has_been_bound = false;
514 m_attachments.clear();
516 m_draw_buffers.clear();
517 m_read_buffer = GL_NONE;
519 m_status = GL_FRAMEBUFFER_COMPLETE;
524 bool vogl_framebuffer_state::remap_handles(vogl_handle_remapper &remapper)
531 m_snapshot_handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_FRAMEBUFFERS, m_snapshot_handle));
535 for (GLenum_to_attachment_map::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
536 if (!it->second.remap_handles(remapper))
542 bool vogl_framebuffer_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
546 VOGL_NOTE_UNUSED(blob_manager);
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"));
555 node.add_key_value("read_buffer", m_read_buffer);
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]);
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()))
569 bool vogl_framebuffer_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
573 VOGL_NOTE_UNUSED(blob_manager);
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");
581 m_read_buffer = node.value_as_uint32("read_buffer");
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()))
586 m_draw_buffers.resize(pDraw_buffers_array->size());
588 for (uint i = 0; i < m_draw_buffers.size(); i++)
589 m_draw_buffers[i] = pDraw_buffers_array->value_as_uint32(i);
592 const json_node *pAttachments_array = node.find_child_array("attachments");
593 if (pAttachments_array)
595 for (uint i = 0; i < pAttachments_array->size(); i++)
597 const json_node *pAttachment = pAttachments_array->get_child(i);
600 GLenum attachment = vogl_get_json_value_as_enum(*pAttachment, "attachment");
601 if (attachment != GL_NONE)
603 if (!m_attachments[attachment].deserialize(*pAttachment))
615 bool vogl_framebuffer_state::compare_restorable_state(const vogl_gl_object_state &rhs_obj) const
619 if ((!m_is_valid) || (!rhs_obj.is_valid()))
622 if (rhs_obj.get_type() != cGLSTFramebuffer)
625 const vogl_framebuffer_state &rhs = static_cast<const vogl_framebuffer_state &>(rhs_obj);
630 if (m_status != rhs.m_status)
633 if (m_has_been_bound != rhs.m_has_been_bound)
636 if (m_read_buffer != rhs.m_read_buffer)
639 if (m_draw_buffers != rhs.m_draw_buffers)
642 return m_attachments == rhs.m_attachments;