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);
378 else if (state_type == cGLSTBuffer)
380 // Determine if this buffer has been mapped. I don't expect this array to be very big (typically empty) so a simple search is fine.
382 for (j = 0; j < capture_params.m_mapped_buffers.size(); j++)
383 if (capture_params.m_mapped_buffers[j].m_buffer == handle)
386 if (j < capture_params.m_mapped_buffers.size())
388 const vogl_mapped_buffer_desc &map_desc = capture_params.m_mapped_buffers[j];
390 vogl_buffer_state *pBuf_state = static_cast<vogl_buffer_state *>(p);
392 pBuf_state->set_mapped_buffer_snapshot_state(map_desc);
396 m_object_ptrs.push_back(p);
403 vogl_printf("Found %u %ss\n", total, get_gl_object_state_type_str(state_type));
408 static bool vogl_object_ptr_sorter(const vogl_gl_object_state *pLHS, vogl_gl_object_state *pRHS)
410 return pLHS->get_snapshot_handle() < pRHS->get_snapshot_handle();
413 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
417 obj_ptr_vec.resize(0);
419 for (uint i = 0; i < m_object_ptrs.size(); i++)
420 if (m_object_ptrs[i]->get_type() == state_type)
421 obj_ptr_vec.push_back(m_object_ptrs[i]);
423 obj_ptr_vec.sort(vogl_object_ptr_sorter);
426 bool vogl_context_snapshot::serialize(json_node &node, vogl_blob_manager &blob_manager, const vogl_ctypes *pCtypes) const
433 if (!m_context_desc.serialize(node.add_object("context_desc"), blob_manager))
436 if (m_context_info.is_valid() && !m_context_info.serialize(node.add_object("context_info"), blob_manager))
439 if (!m_general_state.serialize(node.add_object("general_state"), blob_manager))
442 if (!m_display_list_state.serialize(node.add_object("display_lists"), blob_manager, pCtypes))
445 if (m_texenv_state.is_valid() && !m_texenv_state.serialize(node.add_object("tex_env_gen_state"), blob_manager))
448 if (m_material_state.is_valid() && !m_material_state.serialize(node.add_object("material_state"), blob_manager))
451 if (m_light_state.is_valid() && !m_light_state.serialize(node.add_object("light_state"), blob_manager))
454 if (m_matrix_state.is_valid() && !m_matrix_state.serialize(node.add_array("matrix_state"), blob_manager))
457 if (m_polygon_stipple_state.is_valid() && !m_polygon_stipple_state.serialize(node.add_array("polygon_stipple"), blob_manager))
460 if (m_current_vertex_attrib_state.is_valid() && !m_current_vertex_attrib_state.serialize(node.add_array("current_vertex_attribs"), blob_manager))
463 if (m_arb_program_environment_state.is_valid() && !m_arb_program_environment_state.serialize(node.add_array("arb_program_environment_state"), blob_manager))
466 if (m_object_ptrs.size())
468 json_node &objects_node = node.add_object("state_objects");
470 vogl_gl_object_state_ptr_vec obj_ptrs;
472 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))
474 get_all_objects_of_category(state_type, obj_ptrs);
475 if (obj_ptrs.is_empty())
478 json_node &array_node = objects_node.add_array(get_gl_object_state_type_str(state_type));
480 for (uint i = 0; i < obj_ptrs.size(); i++)
482 json_node &new_obj = array_node.add_object();
483 if (!obj_ptrs[i]->serialize(new_obj, blob_manager))
492 bool vogl_context_snapshot::deserialize(const json_node &node, const vogl_blob_manager &blob_manager, const vogl_ctypes *pCtypes)
498 if ((!vogl_json_deserialize_obj(node, blob_manager, "context_desc", m_context_desc)) ||
499 (!vogl_json_deserialize_obj(node, blob_manager, "general_state", m_general_state)))
505 if (node.has_object("context_info") && !vogl_json_deserialize_obj(node, blob_manager, "context_info", m_context_info))
511 if (node.has_object("display_lists") && !m_display_list_state.deserialize(*node.find_child_object("display_lists"), blob_manager, pCtypes))
517 if (node.has_key("tex_env_gen_state") && !vogl_json_deserialize_obj(node, blob_manager, "tex_env_gen_state", m_texenv_state))
523 if (node.has_key("material_state") && !vogl_json_deserialize_obj(node, blob_manager, "material_state", m_material_state))
529 if (node.has_key("light_state") && !vogl_json_deserialize_obj(node, blob_manager, "light_state", m_light_state))
535 if (node.has_key("matrix_state") && !vogl_json_deserialize_array(node, blob_manager, "matrix_state", m_matrix_state))
541 if (node.has_key("polygon_stipple") && !vogl_json_deserialize_obj(node, blob_manager, "polygon_stipple", m_polygon_stipple_state))
547 if (node.has_key("current_vertex_attribs") && !vogl_json_deserialize_array(node, blob_manager, "current_vertex_attribs", m_current_vertex_attrib_state))
553 if (node.has_key("arb_program_environment_state") && !vogl_json_deserialize_obj(node, blob_manager, "arb_program_environment_state", m_arb_program_environment_state))
559 const json_node *pObjects_node = node.find_child_object("state_objects");
562 for (uint obj_iter = 0; obj_iter < pObjects_node->size(); obj_iter++)
564 const dynamic_string &obj_type_str = pObjects_node->get_key(obj_iter);
566 vogl_gl_object_state_type state_type = determine_gl_object_state_type_from_str(obj_type_str.get_ptr());
567 if (state_type == cGLSTInvalid)
569 vogl_warning_printf("%s: Unknown object state type \"%s\", skipping node\n", VOGL_METHOD_NAME, obj_type_str.get_ptr());
573 const json_node *pArray_node = pObjects_node->get_value_as_array(obj_iter);
580 m_object_ptrs.reserve(m_object_ptrs.size() + pArray_node->size());
582 for (uint i = 0; i < pArray_node->size(); i++)
584 const json_node *pObj_node = pArray_node->get_value_as_object(i);
591 vogl_gl_object_state *pState_obj = vogl_gl_object_state_factory(state_type);
598 if (!pState_obj->deserialize(*pObj_node, blob_manager))
600 vogl_delete(pState_obj);
606 m_object_ptrs.push_back(pState_obj);
616 vogl_gl_state_snapshot::vogl_gl_state_snapshot()
619 m_cur_trace_context(0),
621 m_gl_call_counter(0),
622 m_at_frame_boundary(false),
623 m_is_restorable(false),
624 m_captured_default_framebuffer(false),
632 vogl_gl_state_snapshot::~vogl_gl_state_snapshot()
639 void vogl_gl_state_snapshot::clear()
646 m_cur_trace_context = 0;
648 m_gl_call_counter = 0;
649 m_at_frame_boundary = false;
650 m_is_restorable = false;
652 m_client_side_vertex_attrib_ptrs.clear();
653 m_client_side_array_ptrs.clear();
654 m_client_side_texcoord_ptrs.clear();
658 m_default_framebuffer.clear();
660 m_captured_default_framebuffer = false;
664 // frame_index indicates the beginning of frame X, at a swap boundary
665 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)
672 m_window_width = window_width;
673 m_window_height = window_height;
674 m_cur_trace_context = cur_context;
675 m_frame_index = frame_index;
676 m_gl_call_counter = gl_call_counter;
677 m_at_frame_boundary = at_frame_boundary;
678 m_is_restorable = true;
681 m_captured_default_framebuffer = false;
686 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)
690 m_client_side_vertex_attrib_ptrs = client_side_vertex_attrib_ptrs;
691 m_client_side_array_ptrs = client_side_array_ptrs;
692 m_client_side_texcoord_ptrs = client_side_texcoord_ptrs;
695 // TODO: check if the context was ever made current yet (if not the context_info won't be valid), make sure this path works
696 bool vogl_gl_state_snapshot::capture_context(
697 const vogl_context_desc &desc, const vogl_context_info &info, vogl_handle_remapper &remapper,
698 const vogl_capture_context_params &capture_params)
702 if (!m_captured_default_framebuffer)
705 vogl_default_framebuffer_attribs fb_attribs;
709 if (vogl_get_default_framebuffer_attribs(fb_attribs, 0))
711 if ((fb_attribs.m_width) && (fb_attribs.m_height))
713 status = m_default_framebuffer.snapshot(info, fb_attribs);
719 vogl_error_printf("%s: Failed snapshotting default framebuffer!\n", VOGL_METHOD_NAME);
722 m_captured_default_framebuffer = true;
725 vogl_context_snapshot *pSnapshot = vogl_new(vogl_context_snapshot);
727 if (!pSnapshot->capture(desc, info, capture_params, remapper))
731 vogl_delete(pSnapshot);
736 if (!pSnapshot->remap_handles(remapper))
739 m_context_ptrs.push_back(pSnapshot);
744 bool vogl_gl_state_snapshot::end_capture()
751 bool vogl_gl_state_snapshot::serialize(json_node &node, vogl_blob_manager &blob_manager, const vogl_ctypes *pCtypes) const
758 m_uuid.json_serialize(node.add("uuid"));
759 node.add_key_value("window_width", m_window_width);
760 node.add_key_value("window_height", m_window_height);
761 node.add_key_value("cur_trace_context", m_cur_trace_context);
762 node.add_key_value("frame_index", m_frame_index);
763 node.add_key_value("gl_call_counter", m_gl_call_counter);
764 node.add_key_value("at_frame_boundary", m_at_frame_boundary);
765 node.add_key_value("is_restorable", m_is_restorable);
767 if (!vogl_json_serialize_vec(node, blob_manager, "client_side_vertex_attrib_ptrs", m_client_side_vertex_attrib_ptrs))
769 if (!vogl_json_serialize_vec(node, blob_manager, "client_side_array_ptrs", m_client_side_array_ptrs))
771 if (!vogl_json_serialize_vec(node, blob_manager, "client_side_texcoord_ptrs", m_client_side_texcoord_ptrs))
774 if (!vogl_json_serialize_ptr_vec(node, blob_manager, "context_snapshots", m_context_ptrs, pCtypes))
777 if (m_default_framebuffer.is_valid())
779 if (!m_default_framebuffer.serialize(node.add_object("default_framebuffer"), blob_manager))
786 bool vogl_gl_state_snapshot::deserialize(const json_node &node, const vogl_blob_manager &blob_manager, const vogl_ctypes *pCtypes)
792 if (!m_uuid.json_deserialize(node, "uuid"))
794 // For old trace files that don't have uuid's
798 m_window_width = node.value_as_uint32("window_width");
799 m_window_height = node.value_as_uint32("window_height");
801 if ((!m_window_width) || (!m_window_height))
807 m_cur_trace_context = static_cast<vogl_trace_ptr_value>(node.value_as_uint64("cur_trace_context"));
808 m_frame_index = node.value_as_uint32("frame_index");
809 m_gl_call_counter = node.value_as_int64("gl_call_counter");
810 m_at_frame_boundary = node.value_as_int64("at_frame_boundary");
811 m_is_restorable = node.value_as_bool("is_restorable", true);
813 if (!vogl_json_deserialize_vec(node, blob_manager, "client_side_vertex_attrib_ptrs", m_client_side_vertex_attrib_ptrs))
815 if (!vogl_json_deserialize_vec(node, blob_manager, "client_side_array_ptrs", m_client_side_array_ptrs))
817 if (!vogl_json_deserialize_vec(node, blob_manager, "client_side_texcoord_ptrs", m_client_side_texcoord_ptrs))
820 if (!vogl_json_deserialize_ptr_vec(node, blob_manager, "context_snapshots", m_context_ptrs, pCtypes))
823 if (node.has_object("default_framebuffer"))
825 if (!m_default_framebuffer.deserialize(*node.find_child_object("default_framebuffer"), blob_manager))