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 trace_handle = attachment_obj.get_handle();
373 GLuint handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_RENDER_BUFFERS, trace_handle));
377 GL_ENTRYPOINT(glFramebufferRenderbuffer)(GL_DRAW_FRAMEBUFFER, attachment_target, GL_RENDERBUFFER, handle);
385 GLuint trace_handle = attachment_obj.get_handle();
388 GLuint handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_TEXTURES, trace_handle));
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))
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);
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);
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);
416 GL_ENTRYPOINT(glFramebufferTexture1D)(GL_DRAW_FRAMEBUFFER, attachment_target, tex_target, handle, level);
420 case GL_TEXTURE_2D_MULTISAMPLE:
421 case GL_TEXTURE_RECTANGLE:
423 GL_ENTRYPOINT(glFramebufferTexture2D)(GL_DRAW_FRAMEBUFFER, attachment_target, tex_target, handle, level);
426 case GL_TEXTURE_CUBE_MAP:
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));
431 GL_ENTRYPOINT(glFramebufferTexture2D)(GL_DRAW_FRAMEBUFFER, attachment_target, cube_map_face, handle, level);
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:
440 GL_ENTRYPOINT(glFramebufferTextureLayer)(GL_DRAW_FRAMEBUFFER, attachment_target, handle, level, layer);
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);
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)
468 uint num_valid_draw_buffers = last_valid_draw_buffer_idx + 1;
470 if (!num_valid_draw_buffers)
472 GL_ENTRYPOINT(glDrawBuffer)(GL_NONE);
475 else if (num_valid_draw_buffers == 1)
477 GL_ENTRYPOINT(glDrawBuffer)(m_draw_buffers[0]);
482 GL_ENTRYPOINT(glDrawBuffers)(m_draw_buffers.size(), m_draw_buffers.get_ptr());
486 GL_ENTRYPOINT(glReadBuffer)(m_read_buffer);
490 cur_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_DRAW_FRAMEBUFFER);
493 if (cur_status != m_status)
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));
502 if ((handle) && (created_handle))
504 remapper.delete_handle_and_object(VOGL_NAMESPACE_FRAMEBUFFERS, m_snapshot_handle, handle);
506 //GLuint handle32 = static_cast<GLuint>(handle);
507 //GL_ENTRYPOINT(glDeleteFramebuffers)(1, &handle32);
508 //VOGL_CHECK_GL_ERROR;
516 void vogl_framebuffer_state::clear()
520 m_snapshot_handle = 0;
521 m_has_been_bound = false;
523 m_attachments.clear();
525 m_draw_buffers.clear();
526 m_read_buffer = GL_NONE;
528 m_status = GL_FRAMEBUFFER_COMPLETE;
533 bool vogl_framebuffer_state::remap_handles(vogl_handle_remapper &remapper)
540 m_snapshot_handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_FRAMEBUFFERS, m_snapshot_handle));
544 for (GLenum_to_attachment_map::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it)
545 if (!it->second.remap_handles(remapper))
551 bool vogl_framebuffer_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
555 VOGL_NOTE_UNUSED(blob_manager);
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"));
564 node.add_key_value("read_buffer", m_read_buffer);
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]);
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()))
578 bool vogl_framebuffer_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
582 VOGL_NOTE_UNUSED(blob_manager);
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");
590 m_read_buffer = node.value_as_uint32("read_buffer");
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()))
595 m_draw_buffers.resize(pDraw_buffers_array->size());
597 for (uint i = 0; i < m_draw_buffers.size(); i++)
598 m_draw_buffers[i] = pDraw_buffers_array->value_as_uint32(i);
601 const json_node *pAttachments_array = node.find_child_array("attachments");
602 if (pAttachments_array)
604 for (uint i = 0; i < pAttachments_array->size(); i++)
606 const json_node *pAttachment = pAttachments_array->get_child(i);
609 GLenum attachment = vogl_get_json_value_as_enum(*pAttachment, "attachment");
610 if (attachment != GL_NONE)
612 if (!m_attachments[attachment].deserialize(*pAttachment))
624 bool vogl_framebuffer_state::compare_restorable_state(const vogl_gl_object_state &rhs_obj) const
628 if ((!m_is_valid) || (!rhs_obj.is_valid()))
631 if (rhs_obj.get_type() != cGLSTFramebuffer)
634 const vogl_framebuffer_state &rhs = static_cast<const vogl_framebuffer_state &>(rhs_obj);
639 if (m_status != rhs.m_status)
642 if (m_has_been_bound != rhs.m_has_been_bound)
645 if (m_read_buffer != rhs.m_read_buffer)
648 if (m_draw_buffers != rhs.m_draw_buffers)
651 return m_attachments == rhs.m_attachments;