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_gl_state_snapshot.cpp
27 #include "vogl_gl_state_snapshot.h"
28 #include "vogl_uuid.h"
30 vogl_context_snapshot::vogl_context_snapshot()
36 vogl_context_snapshot::~vogl_context_snapshot()
43 void vogl_context_snapshot::destroy_objects()
47 for (uint i = 0; i < m_object_ptrs.size(); i++)
48 vogl_delete(m_object_ptrs[i]);
49 m_object_ptrs.clear();
52 void vogl_context_snapshot::clear()
56 m_context_desc.clear();
57 m_context_info.clear();
58 m_general_state.clear();
59 m_texenv_state.clear();
60 m_light_state.clear();
61 m_material_state.clear();
62 m_display_list_state.clear();
63 m_matrix_state.clear();
64 m_polygon_stipple_state.clear();
65 m_current_vertex_attrib_state.clear();
66 m_arb_program_environment_state.clear();
73 // Note info may NOT be valid here if the context was never made current!
74 bool vogl_context_snapshot::capture(const vogl_context_desc &desc, const vogl_context_info &info, const vogl_capture_context_params &capture_params, vogl_handle_remapper &remapper)
82 vogl_printf("%s: Starting capture on trace context 0x%" PRIx64 ", has context info: %u\n", VOGL_METHOD_NAME, cast_val_to_uint64(desc.get_trace_context()), info.is_valid());
84 m_context_desc = desc;
85 m_context_info = info;
86 m_display_list_state = capture_params.m_display_lists;
88 // Has this context been ever made current?
91 if (!m_general_state.snapshot(m_context_info))
94 if (!m_current_vertex_attrib_state.snapshot(m_context_info))
97 if (!info.is_core_profile())
99 if (!m_texenv_state.snapshot(m_context_info))
102 if (!m_light_state.snapshot(m_context_info))
105 if (!m_material_state.snapshot(m_context_info))
108 if (!m_matrix_state.snapshot(m_context_info))
111 if (!m_polygon_stipple_state.snapshot(m_context_info))
114 if (info.supports_extension("GL_ARB_vertex_program"))
116 if (!m_arb_program_environment_state.snapshot(m_context_info))
121 // Keep this list in sync with vogl_gl_object_state_type (order doesn't matter, just make sure all valid object types are present)
122 const vogl_gl_object_state_type s_object_type_capture_order[] = { cGLSTTexture, cGLSTBuffer, cGLSTSampler, cGLSTQuery, cGLSTRenderbuffer, cGLSTFramebuffer, cGLSTVertexArray, cGLSTShader, cGLSTProgram, cGLSTSync, cGLSTARBProgram };
123 VOGL_ASSUME(VOGL_ARRAY_SIZE(s_object_type_capture_order) == cGLSTTotalTypes - 1);
125 for (uint i = 0; i < VOGL_ARRAY_SIZE(s_object_type_capture_order); i++)
126 if (!capture_objects(s_object_type_capture_order[i], capture_params, remapper))
134 vogl_printf("%s: Capture succeeded\n", VOGL_METHOD_NAME);
140 vogl_printf("%s: Capture failed\n", VOGL_METHOD_NAME);
144 bool vogl_context_snapshot::remap_handles(vogl_handle_remapper &remapper)
151 vogl_printf("%s: Remapping object handles and program locations\n", VOGL_METHOD_NAME);
153 if (!m_general_state.remap_handles(remapper))
156 if (m_arb_program_environment_state.is_valid())
158 if (!m_arb_program_environment_state.remap_handles(remapper))
162 for (uint i = 0; i < m_object_ptrs.size(); i++)
164 uint64_t orig_handle = m_object_ptrs[i]->get_snapshot_handle();
166 if (!m_object_ptrs[i]->remap_handles(remapper))
169 uint64_t new_handle = m_object_ptrs[i]->get_snapshot_handle();
170 VOGL_NOTE_UNUSED(new_handle);
174 VOGL_ASSERT(new_handle);
176 // TODO: Validate the inverse mapping is good to catch silly bugs
180 vogl_printf("%s: Remap complete\n", VOGL_METHOD_NAME);
185 bool vogl_context_snapshot::capture_objects(vogl_gl_object_state_type state_type, const vogl_capture_context_params &capture_params, vogl_handle_remapper &remapper)
189 vogl_printf("Capturing %ss\n", get_gl_object_state_type_str(state_type));
193 if ((state_type == cGLSTVertexArray) && (m_context_info.is_compatibility_profile()))
195 // Save the default VAO.
196 vogl_gl_object_state *p = vogl_gl_object_state_factory(cGLSTVertexArray);
199 bool success = p->snapshot(m_context_info, remapper, 0, GL_NONE);
206 m_object_ptrs.push_back(p);
211 if (state_type == cGLSTSync)
213 m_object_ptrs.reserve(m_object_ptrs.size() + capture_params.m_syncs.size());
215 for (vogl_sync_hash_set::const_iterator it = capture_params.m_syncs.begin(); it != capture_params.m_syncs.end(); ++it)
217 uint64_t handle = it->first;
219 if (!GL_ENTRYPOINT(glIsSync)(vogl_handle_to_sync(handle)))
225 vogl_gl_object_state *p = vogl_gl_object_state_factory(state_type);
228 bool success = p->snapshot(m_context_info, remapper, handle, GL_NONE);
235 m_object_ptrs.push_back(p);
241 vogl::vector<std::pair<GLuint, GLenum> > handles_to_capture;
242 handles_to_capture.reserve(4096);
246 case cGLSTARBProgram:
248 for (vogl_handle_hash_map::const_iterator it = capture_params.m_arb_program_targets.begin(); it != capture_params.m_arb_program_targets.end(); ++it)
249 handles_to_capture.push_back(*it);
254 for (vogl_handle_hash_map::const_iterator it = capture_params.m_buffer_targets.begin(); it != capture_params.m_buffer_targets.end(); ++it)
255 handles_to_capture.push_back(*it);
260 for (uint i = 0; i < capture_params.m_textures.size(); i++)
262 const vogl_handle_tracker::handle_def &def = capture_params.m_textures[i];
264 handles_to_capture.push_back(std::make_pair(def.get_inv_handle(), def.get_target()));
268 case cGLSTRenderbuffer:
270 for (uint i = 0; i < capture_params.m_rbos.size(); i++)
272 const vogl_handle_tracker::handle_def &def = capture_params.m_rbos[i];
274 handles_to_capture.push_back(std::make_pair(def.get_inv_handle(), def.get_target()));
279 case cGLSTFramebuffer:
281 for (vogl_handle_hash_set::const_iterator it = capture_params.m_framebuffers.begin(); it != capture_params.m_framebuffers.end(); ++it)
282 handles_to_capture.push_back(std::make_pair(it->first, GL_NONE));
288 for (vogl_handle_hash_map::const_iterator it = capture_params.m_query_targets.begin(); it != capture_params.m_query_targets.end(); ++it)
289 handles_to_capture.push_back(*it);
294 for (uint i = 0; i < capture_params.m_objs.size(); i++)
296 const vogl_handle_tracker::handle_def &def = capture_params.m_objs[i];
297 if ((def.is_valid()) && (def.get_target() == VOGL_SHADER_OBJECT))
298 handles_to_capture.push_back(std::make_pair(def.get_inv_handle(), def.get_target()));
305 for (uint i = 0; i < capture_params.m_objs.size(); i++)
307 const vogl_handle_tracker::handle_def &def = capture_params.m_objs[i];
308 if ((def.is_valid()) && (def.get_target() == VOGL_PROGRAM_OBJECT))
310 GLuint handle = def.get_inv_handle();
312 if (capture_params.m_filter_program_handles)
314 if (!capture_params.m_program_handles_filter.contains(handle))
318 handles_to_capture.push_back(std::make_pair(handle, def.get_target()));
326 for (vogl_handle_hash_set::const_iterator it = capture_params.m_samplers.begin(); it != capture_params.m_samplers.end(); ++it)
327 handles_to_capture.push_back(std::make_pair(it->first, GL_NONE));
331 case cGLSTVertexArray:
333 for (vogl_handle_hash_set::const_iterator it = capture_params.m_vaos.begin(); it != capture_params.m_vaos.end(); ++it)
334 handles_to_capture.push_back(std::make_pair(it->first, GL_NONE));
345 m_object_ptrs.reserve(m_object_ptrs.size() + handles_to_capture.size());
347 for (uint i = 0; i < handles_to_capture.size(); ++i)
349 GLuint handle = handles_to_capture[i].first;
350 GLenum target = handles_to_capture[i].second;
352 vogl_gl_object_state *p = vogl_gl_object_state_factory(state_type);
355 bool success = p->snapshot(m_context_info, remapper, handle, target);
362 if (state_type == cGLSTProgram)
364 vogl_program_state *pProg = static_cast<vogl_program_state *>(p);
366 const vogl_program_state *pLink_snapshot = capture_params.m_linked_programs.find_snapshot(handle);
369 vogl_unique_ptr<vogl_program_state> pLink_snapshot_clone(vogl_new(vogl_program_state, *pLink_snapshot));
371 pProg->set_link_time_snapshot(pLink_snapshot_clone);
373 else if (pProg->get_link_status())
375 vogl_error_printf("%s: GL program %u was snapshotted with a successful link status, but the link snapshot shadow doesn't contain this program!\n", VOGL_METHOD_NAME, handle);
379 m_object_ptrs.push_back(p);
386 vogl_printf("Found %u %ss\n", total, get_gl_object_state_type_str(state_type));
391 static bool vogl_object_ptr_sorter(const vogl_gl_object_state *pLHS, vogl_gl_object_state *pRHS)
393 return pLHS->get_snapshot_handle() < pRHS->get_snapshot_handle();
396 void vogl_context_snapshot::get_all_objects_of_category(vogl_gl_object_state_type state_type, vogl_gl_object_state_ptr_vec &obj_ptr_vec) const
400 obj_ptr_vec.resize(0);
402 for (uint i = 0; i < m_object_ptrs.size(); i++)
403 if (m_object_ptrs[i]->get_type() == state_type)
404 obj_ptr_vec.push_back(m_object_ptrs[i]);
406 obj_ptr_vec.sort(vogl_object_ptr_sorter);
409 bool vogl_context_snapshot::serialize(json_node &node, vogl_blob_manager &blob_manager, const vogl_ctypes *pCtypes) const
416 if (!m_context_desc.serialize(node.add_object("context_desc"), blob_manager))
419 if (m_context_info.is_valid() && !m_context_info.serialize(node.add_object("context_info"), blob_manager))
422 if (!m_general_state.serialize(node.add_object("general_state"), blob_manager))
425 if (!m_display_list_state.serialize(node.add_object("display_lists"), blob_manager, pCtypes))
428 if (m_texenv_state.is_valid() && !m_texenv_state.serialize(node.add_object("tex_env_gen_state"), blob_manager))
431 if (m_material_state.is_valid() && !m_material_state.serialize(node.add_object("material_state"), blob_manager))
434 if (m_light_state.is_valid() && !m_light_state.serialize(node.add_object("light_state"), blob_manager))
437 if (m_matrix_state.is_valid() && !m_matrix_state.serialize(node.add_array("matrix_state"), blob_manager))
440 if (m_polygon_stipple_state.is_valid() && !m_polygon_stipple_state.serialize(node.add_array("polygon_stipple"), blob_manager))
443 if (m_current_vertex_attrib_state.is_valid() && !m_current_vertex_attrib_state.serialize(node.add_array("current_vertex_attribs"), blob_manager))
446 if (m_arb_program_environment_state.is_valid() && !m_arb_program_environment_state.serialize(node.add_array("arb_program_environment_state"), blob_manager))
449 if (m_object_ptrs.size())
451 json_node &objects_node = node.add_object("state_objects");
453 vogl_gl_object_state_ptr_vec obj_ptrs;
455 for (vogl_gl_object_state_type state_type = static_cast<vogl_gl_object_state_type>(0); state_type < cGLSTTotalTypes; state_type = static_cast<vogl_gl_object_state_type>(state_type + 1))
457 get_all_objects_of_category(state_type, obj_ptrs);
458 if (obj_ptrs.is_empty())
461 json_node &array_node = objects_node.add_array(get_gl_object_state_type_str(state_type));
463 for (uint i = 0; i < obj_ptrs.size(); i++)
465 json_node &new_obj = array_node.add_object();
466 if (!obj_ptrs[i]->serialize(new_obj, blob_manager))
475 bool vogl_context_snapshot::deserialize(const json_node &node, const vogl_blob_manager &blob_manager, const vogl_ctypes *pCtypes)
481 if ((!vogl_json_deserialize_obj(node, blob_manager, "context_desc", m_context_desc)) ||
482 (!vogl_json_deserialize_obj(node, blob_manager, "general_state", m_general_state)))
488 if (node.has_object("context_info") && !vogl_json_deserialize_obj(node, blob_manager, "context_info", m_context_info))
494 if (node.has_object("display_lists") && !m_display_list_state.deserialize(*node.find_child_object("display_lists"), blob_manager, pCtypes))
500 if (node.has_key("tex_env_gen_state") && !vogl_json_deserialize_obj(node, blob_manager, "tex_env_gen_state", m_texenv_state))
506 if (node.has_key("material_state") && !vogl_json_deserialize_obj(node, blob_manager, "material_state", m_material_state))
512 if (node.has_key("light_state") && !vogl_json_deserialize_obj(node, blob_manager, "light_state", m_light_state))
518 if (node.has_key("matrix_state") && !vogl_json_deserialize_array(node, blob_manager, "matrix_state", m_matrix_state))
524 if (node.has_key("polygon_stipple") && !vogl_json_deserialize_obj(node, blob_manager, "polygon_stipple", m_polygon_stipple_state))
530 if (node.has_key("current_vertex_attribs") && !vogl_json_deserialize_array(node, blob_manager, "current_vertex_attribs", m_current_vertex_attrib_state))
536 if (node.has_key("arb_program_environment_state") && !vogl_json_deserialize_obj(node, blob_manager, "arb_program_environment_state", m_arb_program_environment_state))
542 const json_node *pObjects_node = node.find_child_object("state_objects");
545 for (uint obj_iter = 0; obj_iter < pObjects_node->size(); obj_iter++)
547 const dynamic_string &obj_type_str = pObjects_node->get_key(obj_iter);
549 vogl_gl_object_state_type state_type = determine_gl_object_state_type_from_str(obj_type_str.get_ptr());
550 if (state_type == cGLSTInvalid)
552 vogl_warning_printf("%s: Unknown object state type \"%s\", skipping node\n", VOGL_METHOD_NAME, obj_type_str.get_ptr());
556 const json_node *pArray_node = pObjects_node->get_value_as_array(obj_iter);
563 m_object_ptrs.reserve(m_object_ptrs.size() + pArray_node->size());
565 for (uint i = 0; i < pArray_node->size(); i++)
567 const json_node *pObj_node = pArray_node->get_value_as_object(i);
574 vogl_gl_object_state *pState_obj = vogl_gl_object_state_factory(state_type);
581 if (!pState_obj->deserialize(*pObj_node, blob_manager))
583 vogl_delete(pState_obj);
589 m_object_ptrs.push_back(pState_obj);
599 vogl_gl_state_snapshot::vogl_gl_state_snapshot()
602 m_cur_trace_context(0),
604 m_gl_call_counter(0),
605 m_at_frame_boundary(false),
606 m_is_restorable(false),
607 m_captured_default_framebuffer(false),
615 vogl_gl_state_snapshot::~vogl_gl_state_snapshot()
622 void vogl_gl_state_snapshot::clear()
629 m_cur_trace_context = 0;
631 m_gl_call_counter = 0;
632 m_at_frame_boundary = false;
633 m_is_restorable = false;
635 m_client_side_vertex_attrib_ptrs.clear();
636 m_client_side_array_ptrs.clear();
637 m_client_side_texcoord_ptrs.clear();
641 m_default_framebuffer.clear();
643 m_captured_default_framebuffer = false;
647 // frame_index indicates the beginning of frame X, at a swap boundary
648 bool vogl_gl_state_snapshot::begin_capture(uint window_width, uint window_height, vogl_trace_ptr_value cur_context, uint frame_index, int64_t gl_call_counter, bool at_frame_boundary)
655 m_window_width = window_width;
656 m_window_height = window_height;
657 m_cur_trace_context = cur_context;
658 m_frame_index = frame_index;
659 m_gl_call_counter = gl_call_counter;
660 m_at_frame_boundary = at_frame_boundary;
661 m_is_restorable = true;
664 m_captured_default_framebuffer = false;
669 void vogl_gl_state_snapshot::add_client_side_array_ptrs(const vogl_client_side_array_desc_vec &client_side_vertex_attrib_ptrs, const vogl_client_side_array_desc_vec &client_side_array_ptrs, const vogl_client_side_array_desc_vec &client_side_texcoord_ptrs)
673 m_client_side_vertex_attrib_ptrs = client_side_vertex_attrib_ptrs;
674 m_client_side_array_ptrs = client_side_array_ptrs;
675 m_client_side_texcoord_ptrs = client_side_texcoord_ptrs;
678 // TODO: check if the context was ever made current yet (if not the context_info won't be valid), make sure this path works
679 bool vogl_gl_state_snapshot::capture_context(
680 const vogl_context_desc &desc, const vogl_context_info &info, vogl_handle_remapper &remapper,
681 const vogl_capture_context_params &capture_params)
685 if (!m_captured_default_framebuffer)
688 vogl_default_framebuffer_attribs fb_attribs;
692 if (vogl_get_default_framebuffer_attribs(fb_attribs, 0))
694 if ((fb_attribs.m_width) && (fb_attribs.m_height))
696 status = m_default_framebuffer.snapshot(info, fb_attribs);
702 vogl_error_printf("%s: Failed snapshotting default framebuffer!\n", VOGL_METHOD_NAME);
705 m_captured_default_framebuffer = false;
708 vogl_context_snapshot *pSnapshot = vogl_new(vogl_context_snapshot);
710 if (!pSnapshot->capture(desc, info, capture_params, remapper))
714 vogl_delete(pSnapshot);
719 if (!pSnapshot->remap_handles(remapper))
722 m_context_ptrs.push_back(pSnapshot);
727 bool vogl_gl_state_snapshot::end_capture()
734 bool vogl_gl_state_snapshot::serialize(json_node &node, vogl_blob_manager &blob_manager, const vogl_ctypes *pCtypes) const
741 m_uuid.json_serialize(node.add("uuid"));
742 node.add_key_value("window_width", m_window_width);
743 node.add_key_value("window_height", m_window_height);
744 node.add_key_value("cur_trace_context", m_cur_trace_context);
745 node.add_key_value("frame_index", m_frame_index);
746 node.add_key_value("gl_call_counter", m_gl_call_counter);
747 node.add_key_value("at_frame_boundary", m_at_frame_boundary);
748 node.add_key_value("is_restorable", m_is_restorable);
750 if (!vogl_json_serialize_vec(node, blob_manager, "client_side_vertex_attrib_ptrs", m_client_side_vertex_attrib_ptrs))
752 if (!vogl_json_serialize_vec(node, blob_manager, "client_side_array_ptrs", m_client_side_array_ptrs))
754 if (!vogl_json_serialize_vec(node, blob_manager, "client_side_texcoord_ptrs", m_client_side_texcoord_ptrs))
757 if (!vogl_json_serialize_ptr_vec(node, blob_manager, "context_snapshots", m_context_ptrs, pCtypes))
760 if (m_default_framebuffer.is_valid())
762 if (!m_default_framebuffer.serialize(node.add_object("default_framebuffer"), blob_manager))
769 bool vogl_gl_state_snapshot::deserialize(const json_node &node, const vogl_blob_manager &blob_manager, const vogl_ctypes *pCtypes)
775 if (!m_uuid.json_deserialize(node, "uuid"))
777 // For old trace files that don't have uuid's
781 m_window_width = node.value_as_uint32("window_width");
782 m_window_height = node.value_as_uint32("window_height");
784 if ((!m_window_width) || (!m_window_height))
790 m_cur_trace_context = static_cast<vogl_trace_ptr_value>(node.value_as_uint64("cur_trace_context"));
791 m_frame_index = node.value_as_uint32("frame_index");
792 m_gl_call_counter = node.value_as_int64("gl_call_counter");
793 m_at_frame_boundary = node.value_as_int64("at_frame_boundary");
794 m_is_restorable = node.value_as_bool("is_restorable", true);
796 if (!vogl_json_deserialize_vec(node, blob_manager, "client_side_vertex_attrib_ptrs", m_client_side_vertex_attrib_ptrs))
798 if (!vogl_json_deserialize_vec(node, blob_manager, "client_side_array_ptrs", m_client_side_array_ptrs))
800 if (!vogl_json_deserialize_vec(node, blob_manager, "client_side_texcoord_ptrs", m_client_side_texcoord_ptrs))
803 if (!vogl_json_deserialize_ptr_vec(node, blob_manager, "context_snapshots", m_context_ptrs, pCtypes))
806 if (node.has_object("default_framebuffer"))
808 if (!m_default_framebuffer.deserialize(*node.find_child_object("default_framebuffer"), blob_manager))