bool vogleditor_traceReplayer::recursive_replay_apicallTreeItem(vogleditor_apiCallTreeItem* pItem, vogleditor_gl_state_snapshot** ppNewSnapshot, uint64_t apiCallNumber) { bool bStatus = true; vogleditor_apiCallItem* pApiCall = pItem->apiCallItem(); if (pApiCall != NULL) { vogl_trace_packet* pTrace_packet = pApiCall->getTracePacket(); vogl_gl_replayer::status_t status = vogl_gl_replayer::cStatusOK; // See if a window resize or snapshot is pending. If a window resize is pending we must delay a while and pump X events until the window is resized. while (m_pTraceReplayer->get_has_pending_window_resize() || m_pTraceReplayer->get_pending_apply_snapshot()) { // Pump X events in case the window is resizing bStatus = process_x_events(); if (bStatus) { status = m_pTraceReplayer->process_pending_window_resize(); if (status != vogl_gl_replayer::cStatusResizeWindow) break; } else { // most likely the window wants to close, so let's return return false; } } // replay the trace packet if (status == vogl_gl_replayer::cStatusOK) status = m_pTraceReplayer->process_next_packet(*pTrace_packet); // if that was successful, check to see if a state snapshot is needed if ((status != vogl_gl_replayer::cStatusHardFailure) && (status != vogl_gl_replayer::cStatusAtEOF)) { if (ppNewSnapshot != NULL) { // get the snapshot after the selected api call if ((!*ppNewSnapshot) && (m_pTraceReplayer->get_last_processed_call_counter() == static_cast<int64_t>(apiCallNumber))) { vogl_printf("Taking snapshot on API call # %" PRIu64 "\n", apiCallNumber); vogl_gl_state_snapshot* pNewSnapshot = m_pTraceReplayer->snapshot_state(); if (pNewSnapshot == NULL) { vogl_error_printf("Taking new snapshot failed!\n"); } else { vogl_printf("Taking snapshot succeeded\n"); *ppNewSnapshot = vogl_new(vogleditor_gl_state_snapshot, pNewSnapshot); if (*ppNewSnapshot == NULL) { vogl_error_printf("Allocating memory for snapshot container failed!\n"); vogl_delete(pNewSnapshot); } } bStatus = false; } } } else { // replaying the trace packet failed, set as error vogl_error_printf("%s: unable to replay gl entrypoint at call %" PRIu64 "\n", VOGL_FUNCTION_NAME, pTrace_packet->get_call_counter()); bStatus = false; } } if (bStatus && pItem->has_snapshot() && pItem->get_snapshot()->is_edited() && pItem->get_snapshot()->is_valid()) { bStatus = applying_snapshot_and_process_resize(pItem->get_snapshot()->get_snapshot()); } if (bStatus) { for (int i = 0; i < pItem->childCount(); i++) { bStatus = recursive_replay_apicallTreeItem(pItem->child(i), ppNewSnapshot, apiCallNumber); if (!bStatus) break; } } return bStatus; }
bool vogleditor_traceReplayer::replay(vogl_trace_file_reader* m_pTraceReader, vogleditor_apiCallTreeItem* pRootItem, vogleditor_gl_state_snapshot** ppNewSnapshot, uint64_t apiCallNumber, bool endlessMode) { // reset to beginnning of trace file. m_pTraceReader->seek_to_frame(0); int initial_window_width = 1280; int initial_window_height = 1024; if (!m_window.open(initial_window_width, initial_window_height)) { vogl_error_printf("%s: Failed opening GL replayer window!\n", VOGL_FUNCTION_NAME); return false; } uint replayer_flags = cGLReplayerForceDebugContexts; if (!m_pTraceReplayer->init(replayer_flags, &m_window, m_pTraceReader->get_sof_packet(), m_pTraceReader->get_multi_blob_manager())) { vogl_error_printf("%s: Failed initializing GL replayer\n", VOGL_FUNCTION_NAME); m_window.close(); return false; } XSelectInput(m_window.get_display(), m_window.get_xwindow(), EnterWindowMask | LeaveWindowMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | StructureNotifyMask | KeymapStateMask); m_wmDeleteMessage = XInternAtom(m_window.get_display(), "WM_DELETE_WINDOW", False); XSetWMProtocols(m_window.get_display(), m_window.get_xwindow(), &m_wmDeleteMessage, 1); timer tm; tm.start(); bool bStatus = true; for ( ; ; ) { if (process_x_events() == false) { break; } if (pRootItem->childCount() > 0) { vogleditor_apiCallTreeItem* pFirstFrame = pRootItem->child(0); // if the first snapshot has not been edited, then restore it here, otherwise it will get restored in the recursive call below. if (pFirstFrame->has_snapshot() && !pFirstFrame->get_snapshot()->is_edited()) { bStatus = applying_snapshot_and_process_resize(pFirstFrame->get_snapshot()->get_snapshot()); } if (bStatus) { // replay each API call. bStatus = recursive_replay_apicallTreeItem(pRootItem, ppNewSnapshot, apiCallNumber); if (bStatus == false) { vogl_error_printf("%s: Replay ending abruptly at frame index %u, global api call %" PRIu64 "\n", VOGL_FUNCTION_NAME, m_pTraceReplayer->get_frame_index(), m_pTraceReplayer->get_last_processed_call_counter()); break; } else { vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_NAME, m_pTraceReplayer->get_frame_index()); if (!endlessMode) { break; } } } else { break; } } } m_pTraceReplayer->deinit(); m_window.close(); return bStatus; }
bool vogl_trace_file_writer::close() { VOGL_FUNC_TRACER vogl_debug_printf("%s\n", VOGL_FUNCTION_INFO_CSTR); if (!m_stream.is_opened()) return false; vogl_message_printf("%s: Flushing trace file %s (this could take some time), %u total frame file offsets\n", VOGL_FUNCTION_INFO_CSTR, m_filename.get_ptr(), m_frame_file_offsets.size()); bool success = true; dynamic_string trace_archive_filename; if (!write_eof_packet()) { vogl_error_printf("%s: Failed writing to trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, m_filename.get_ptr()); success = false; } else if (m_pTrace_archive.get()) { trace_archive_filename = m_pTrace_archive->get_archive_filename(); if ((!write_frame_file_offsets_to_archive()) || !m_pTrace_archive->deinit()) { vogl_error_printf("%s: Failed closing trace archive \"%s\"!\n", VOGL_FUNCTION_INFO_CSTR, trace_archive_filename.get_ptr()); success = false; } else { if (!file_utils::get_file_size(trace_archive_filename.get_ptr(), m_sof_packet.m_archive_size)) { vogl_error_printf("%s: Failed determining file size of archive file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, trace_archive_filename.get_ptr()); success = false; } else if (m_sof_packet.m_archive_size) { m_sof_packet.m_archive_offset = m_stream.get_size(); vogl_message_printf("Copying %" PRIu64 " archive bytes into output trace file\n", m_sof_packet.m_archive_size); if (!m_stream.write_file_data(trace_archive_filename.get_ptr())) { vogl_error_printf("%s: Failed copying source archive \"%s\" into trace file!\n", VOGL_FUNCTION_INFO_CSTR, trace_archive_filename.get_ptr()); success = false; } m_sof_packet.m_archive_size = m_stream.get_size() - m_sof_packet.m_archive_offset; } } if (success) { m_sof_packet.finalize(); VOGL_VERIFY(m_sof_packet.full_validation(sizeof(m_sof_packet))); if (!m_stream.seek(0, false) || (m_stream.write(&m_sof_packet, sizeof(m_sof_packet)) != sizeof(m_sof_packet))) { vogl_error_printf("%s: Failed writing to trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, m_filename.get_ptr()); success = false; } } } close_archive(trace_archive_filename.get_ptr()); uint64_t total_trace_file_size = m_stream.get_size(); if (!m_stream.close()) { vogl_error_printf("Failed writing to or closing trace file!\n"); success = false; } dynamic_string full_trace_filename(m_filename); file_utils::full_path(full_trace_filename); if (success) vogl_message_printf("%s: Successfully closed trace file %s, total file size: %s\n", VOGL_FUNCTION_INFO_CSTR, full_trace_filename.get_ptr(), uint64_to_string_with_commas(total_trace_file_size).get_ptr()); else vogl_error_printf("%s: Failed closing trace file %s! (Trace will probably not be valid.)\n", VOGL_FUNCTION_INFO_CSTR, full_trace_filename.get_ptr()); return success; }
// pTrace_archive may be NULL. Takes ownership of pTrace_archive. // TODO: Get rid of the demarcation packet, etc. Make the initial sequence of packets more explicit. bool vogl_trace_file_writer::open(const char *pFilename, vogl_archive_blob_manager *pTrace_archive, bool delete_archive, bool write_demarcation_packet, uint pointer_sizes) { VOGL_FUNC_TRACER close(); if (!pFilename) return false; m_filename = pFilename; if (!m_stream.open(pFilename, cDataStreamWritable | cDataStreamSeekable, false)) { vogl_error_printf("%s: Failed opening trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, pFilename); return false; } vogl_message_printf("%s: Prepping trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, pFilename); m_sof_packet.init(); m_sof_packet.m_pointer_sizes = pointer_sizes; m_sof_packet.m_first_packet_offset = sizeof(m_sof_packet); md5_hash h(gen_uuid()); VOGL_ASSUME(sizeof(h) == sizeof(m_sof_packet.m_uuid)); memcpy(&m_sof_packet.m_uuid, &h, sizeof(h)); m_sof_packet.finalize(); VOGL_VERIFY(m_sof_packet.full_validation(sizeof(m_sof_packet))); if (m_stream.write(&m_sof_packet, sizeof(m_sof_packet)) != sizeof(m_sof_packet)) { vogl_error_printf("%s: Failed writing to trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, pFilename); return false; } if (pTrace_archive) { m_pTrace_archive.reset(pTrace_archive); m_delete_archive = delete_archive; } else { m_pTrace_archive.reset(vogl_new(vogl_archive_blob_manager)); m_delete_archive = true; if (!m_pTrace_archive->init_file_temp(cBMFReadWrite)) { vogl_error_printf("%s: Failed opening temp archive!\n", VOGL_FUNCTION_INFO_CSTR); m_pTrace_archive.reset(); return false; } } // TODO: The trace reader records the first offset right after SOF, I would like to do this after the demarcation packet. m_frame_file_offsets.reserve(10000); m_frame_file_offsets.resize(0); m_frame_file_offsets.push_back(m_stream.get_ofs()); write_ctypes_packet(); write_entrypoints_packet(); if (write_demarcation_packet) { vogl_write_glInternalTraceCommandRAD(m_stream, m_pCTypes, cITCRDemarcation, 0, NULL); } vogl_message_printf("%s: Finished opening trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, pFilename); return true; }
bool file_utils::does_dir_exist(const char *pDir) { vogl_error_printf("Unimplemented\n"); return false; }
// This helper only works on files with valid file sizes (i.e. it won't work on some files under proc such as /proc/self/status). bool file_utils::read_text_file(const char *pPath, dynamic_string_array &lines, uint32_t flags) { cfile_stream stream; if (!stream.open(pPath, cDataStreamReadable)) { if (flags & cRTFPrintErrorMessages) vogl_error_printf("Failed opening text file \"%s\" for reading\n", pPath); else if (flags & cRTFPrintWarningMessages) vogl_warning_printf("Failed opening text file \"%s\" for reading\n", pPath); return false; } int line_count = 0; dynamic_string line_str; while (stream.get_remaining()) { ++line_count; if (!stream.read_line(line_str)) { if (flags & cRTFPrintErrorMessages) vogl_error_printf("Failed reading from text file \"%s\"\n", pPath); else if (flags & cRTFPrintWarningMessages) vogl_warning_printf("Failed reading from text file \"%s\"\n", pPath); break; } if (flags & cRTFTrim) line_str.trim(); if (flags & cRTFTrimEnd) line_str.trim_end(); if (flags & cRTFIgnoreEmptyLines) { if (line_str.is_empty()) continue; } if ((flags & cRTFIgnoreCommentedLines) && (line_str.get_len() >= 2)) { bool found_comment = false; for (int i = 0; i < static_cast<int>(line_str.get_len()); ++i) { char c = line_str[i]; if ((c == ' ') || (c == '\t')) continue; else if ((c == '/') && (line_str[i + 1] == '/')) found_comment = true; break; } if (found_comment) continue; } lines.push_back(line_str); } return true; }
bool file_utils::disable_read_only(const char *pFilename) { pFilename; vogl_error_printf("Unimplemented\n"); return false; }
bool vogl_default_framebuffer_state::restore(const vogl_context_info &context_info, bool restore_front_buffer) const { VOGL_NOTE_UNUSED(context_info); if (!m_valid) return false; // TODO: Test multisampled default framebuffers // TODO: Check to ensure the stored fb is compatible with the current framebuffer const GLenum tex_target = (m_fb_attribs.m_samples > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; vogl_scoped_state_saver framebuffer_state_saver(cGSTReadBuffer, cGSTDrawBuffer); vogl_scoped_binding_state orig_framebuffers(GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER, GL_TEXTURE_2D, GL_TEXTURE_2D_MULTISAMPLE); GL_ENTRYPOINT(glBindFramebuffer)(GL_DRAW_FRAMEBUFFER, 0); VOGL_CHECK_GL_ERROR; for (uint i = 0; i < cDefFramebufferTotal; i++) { if ((!restore_front_buffer) && ((i == cDefFramebufferFrontLeft) || (i == cDefFramebufferFrontRight))) continue; if (!m_textures[i].is_valid()) continue; GL_ENTRYPOINT(glDrawBuffer)((i == cDefFramebufferDepthStencil) ? GL_NONE : g_def_framebuffer_enums[i]); if (vogl_check_gl_error_internal(true)) continue; GLuint64 tex_handle64 = 0; vogl_handle_remapper def_handle_remapper; if (!m_textures[i].restore(context_info, def_handle_remapper, tex_handle64)) { vogl_error_printf("%s: Failed restoring texture %u\n", VOGL_METHOD_NAME, i); continue; } GLuint tex_handle = static_cast<GLuint>(tex_handle64); // Create FBO GLuint fbo_handle = 0; GL_ENTRYPOINT(glGenFramebuffers)(1, &fbo_handle); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glBindFramebuffer)(GL_READ_FRAMEBUFFER, fbo_handle); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glFramebufferTexture2D)(GL_READ_FRAMEBUFFER, (i == cDefFramebufferDepthStencil) ? GL_DEPTH_STENCIL_ATTACHMENT : GL_COLOR_ATTACHMENT0, tex_target, tex_handle, 0); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glReadBuffer)((i == cDefFramebufferDepthStencil) ? GL_NONE : GL_COLOR_ATTACHMENT0); VOGL_CHECK_GL_ERROR; GLenum cur_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_READ_FRAMEBUFFER); VOGL_CHECK_GL_ERROR; bool status = true; if (cur_status == GL_FRAMEBUFFER_COMPLETE) { GL_ENTRYPOINT(glBlitFramebuffer)( 0, 0, m_fb_attribs.m_width, m_fb_attribs.m_height, 0, 0, m_fb_attribs.m_width, m_fb_attribs.m_height, (i == cDefFramebufferDepthStencil) ? (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) : GL_COLOR_BUFFER_BIT, GL_NEAREST); if (vogl_check_gl_error_internal()) { status = false; } } if (!status) { vogl_warning_printf("%s: Failed blitting framebuffer %u\n", VOGL_METHOD_NAME, i); } // Delete FBO GL_ENTRYPOINT(glBindFramebuffer)(GL_READ_FRAMEBUFFER, 0); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glDeleteFramebuffers)(1, &fbo_handle); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glReadBuffer)(GL_FRONT_LEFT); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glBindTexture)(tex_target, 0); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glDeleteTextures)(1, &tex_handle); VOGL_CHECK_GL_ERROR; } return true; }
//---------------------------------------------------------------------------------------------------------------------- // voglsym_main_loop //---------------------------------------------------------------------------------------------------------------------- static bool voglsym_main_loop(char *argv[]) { VOGL_FUNC_TRACER dynamic_string tracefile_arch; dynamic_string actual_trace_filename; vector<addr_data_t> addr_data_arr; vector<btrace_module_info> module_infos; dynamic_string trace_filename(g_command_line_params().get_value_as_string_or_empty("", 1)); if (trace_filename.is_empty()) { vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_NAME); return false; } vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file( trace_filename, actual_trace_filename, g_command_line_params().get_value_as_string_or_empty("loose_file_path").get_ptr())); if (!pTrace_reader.get()) { vogl_error_printf("%s: File not found, or unable to determine file type of trace file \"%s\"\n", VOGL_FUNCTION_NAME, trace_filename.get_ptr()); return false; } bool resolve_symbols = g_command_line_params().get_value_as_bool("resolve_symbols"); if (resolve_symbols) vogl_printf("Resolving symbols in trace file %s\n\n", actual_trace_filename.get_ptr()); else vogl_printf("Reading trace file %s\n\n", actual_trace_filename.get_ptr()); // compiler_info.json dump_compiler_info(pTrace_reader.get(), tracefile_arch); if (resolve_symbols) { if (tracefile_arch.size()) { bool is_64bit = (sizeof(void *) == 8); bool trace_file_arch_is_64bits = !tracefile_arch.compare("64bit"); if (trace_file_arch_is_64bits != is_64bit) { const char *arch_str = is_64bit ? "64-bit" : "32-bit"; vogl_error_printf("ERROR: %s is %s, tracefile is %s.\n", argv[0], arch_str, tracefile_arch.c_str()); vogl_error_printf("ERROR: Same architecture required to resolve symbols.\n"); return -1; } } if (pTrace_reader->get_archive_blob_manager().does_exist(VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_SYMS_FILENAME)) { vogl_error_printf("ERROR: Symbols have already resolved for this tracefile.\n"); return -1; } } // machine_info.json dump_machine_info(pTrace_reader.get(), module_infos); // backtrace_map_addrs.json get_backtrace_map_addrs(pTrace_reader.get(), addr_data_arr); // backtrace_map_syms.json dump_backtrace_map_syms(pTrace_reader.get()); // Spew our module information if (module_infos.size()) { vogl_header1_printf("%s\n", std::string(78, '*').c_str()); vogl_header1_printf("%s\n", "Modules"); vogl_header1_printf("%s\n", std::string(78, '*').c_str()); for (uint i = 0; i < module_infos.size(); i++) { char uuid_str[41]; const btrace_module_info &module_info = module_infos[i]; btrace_uuid_to_str(uuid_str, module_info.uuid, module_info.uuid_len); vogl_printf("0x%" PRIxPTR " (%u) %s %s %s", module_info.base_address, module_info.address_size, uuid_str, module_info.filename, module_info.is_exe ? "(exe)" : ""); if (resolve_symbols) { const char *debug_filename = NULL; if (btrace_dlopen_add_module(module_info)) { debug_filename = btrace_get_debug_filename(module_info.filename); } if (debug_filename) { vogl_printf(" [%s]", debug_filename); } } vogl_printf("\n"); } vogl_printf("\n"); } // Spew our backtrace addresses if (addr_data_arr.size()) { vogl_header1_printf("%s\n", std::string(78, '*').c_str()); vogl_header1_printf("%s\n", VOGL_TRACE_ARCHIVE_BACKTRACE_MAP_ADDRS_FILENAME); vogl_header1_printf("%s\n", std::string(78, '*').c_str()); vogl_printf("index (count): addr0, addr1, ...\n"); for (uint i = 0; i < addr_data_arr.size(); i++) { const addr_data_t &addr_data = addr_data_arr[i]; vogl_printf("0x%x (%u): ", addr_data.index, addr_data.count); for (uint j = 0; j < addr_data.addrs.size(); j++) { vogl_printf("%" PRIxPTR " ", addr_data.addrs[j]); } vogl_printf("\n"); } vogl_printf("\n"); } // Resolve symbols if we're supposed to. //$ TODO: The resolve symbols should be added to the trace file? if (resolve_symbols) { json_document doc; json_node *pRoot = doc.get_root(); pRoot->init_array(); vogl_header1_printf("%s\n", std::string(78, '*').c_str()); vogl_header1_printf("%s\n", "Resolving symbols..."); vogl_header1_printf("%s\n", std::string(78, '*').c_str()); for (uint i = 0; i < addr_data_arr.size(); i++) { const addr_data_t &addr_data = addr_data_arr[i]; json_node &syms_arr = pRoot->add_array(); for (uint j = 0; j < addr_data.addrs.size(); j++) { btrace_info trace_info; uintptr_t addr = addr_data.addrs[j]; bool success = btrace_resolve_addr(&trace_info, addr, BTRACE_RESOLVE_ADDR_GET_FILENAME | BTRACE_RESOLVE_ADDR_DEMANGLE_FUNC); dynamic_string sym; if (!success) { sym = "?"; } else if (trace_info.function[0] && trace_info.filename[0]) { // Got function and/or filename. sym.format("%s (%s+0x%" PRIx64 ") at %s:%i", trace_info.function, trace_info.module[0] ? trace_info.module : "?", cast_val_to_uint64(trace_info.offset), trace_info.filename, trace_info.linenumber); } else if (trace_info.function[0]) { // Got function, no filename. sym.format("%s (%s+0x%" PRIx64 ")", trace_info.function, trace_info.module[0] ? trace_info.module : "?", cast_val_to_uint64(trace_info.offset)); } else { // Only got modulename (no debugging information found). sym.format("(%s+0x%" PRIx64 ")", trace_info.module[0] ? trace_info.module : "?", cast_val_to_uint64(trace_info.offset)); } syms_arr.add_value(sym); } } doc.print(true, 0, 0); } return true; }
bool file_utils::is_read_only(const char *pFilename) { VOGL_NOTE_UNUSED(pFilename); vogl_error_printf("Unimplemented\n"); return false; }
bool vogl_sampler_state::snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 handle, GLenum target) { VOGL_FUNC_TRACER VOGL_NOTE_UNUSED(remapper); VOGL_CHECK_GL_ERROR; (void)target; clear(); VOGL_ASSERT(handle <= cUINT32_MAX); m_snapshot_handle = static_cast<uint32>(handle); bool any_gl_errors = false; #define GET_INT(pname) \ do \ { \ int values[4] = { 0, 0, 0, 0 }; \ GL_ENTRYPOINT(glGetSamplerParameteriv)(m_snapshot_handle, pname, values); \ if (vogl_check_gl_error()) \ any_gl_errors = true; \ m_params.insert(pname, 0, values, sizeof(values[0])); \ } while (0) #define GET_FLOAT(pname) \ do \ { \ float values[4] = { 0, 0, 0, 0 }; \ GL_ENTRYPOINT(glGetSamplerParameterfv)(m_snapshot_handle, pname, values); \ if (vogl_check_gl_error()) \ any_gl_errors = true; \ m_params.insert(pname, 0, values, sizeof(values[0])); \ } while (0) GET_INT(GL_TEXTURE_MAG_FILTER); GET_INT(GL_TEXTURE_MIN_FILTER); GET_FLOAT(GL_TEXTURE_MIN_LOD); GET_FLOAT(GL_TEXTURE_MAX_LOD); GET_INT(GL_TEXTURE_WRAP_S); GET_INT(GL_TEXTURE_WRAP_T); GET_INT(GL_TEXTURE_WRAP_R); GET_FLOAT(GL_TEXTURE_BORDER_COLOR); GET_INT(GL_TEXTURE_COMPARE_MODE); GET_INT(GL_TEXTURE_COMPARE_FUNC); if (context_info.supports_extension("GL_EXT_texture_filter_anisotropic")) { GET_FLOAT(GL_TEXTURE_MAX_ANISOTROPY_EXT); } if (context_info.supports_extension("GL_EXT_texture_sRGB_decode")) { GET_INT(GL_TEXTURE_SRGB_DECODE_EXT); } #undef GET_INT #undef GET_FLOAT if (any_gl_errors) { clear(); vogl_error_printf("%s: GL error while enumerating sampler %" PRIu64 "'s' params\n", VOGL_METHOD_NAME, (uint64_t)handle); return false; } m_is_valid = true; return true; }
bool vogl_binary_trace_file_reader::open(const char *pFilename, const char *pLoose_file_path) { VOGL_FUNC_TRACER close(); if (!init_loose_file_blob_manager(pFilename, pLoose_file_path)) return false; if (!m_trace_stream.open(pFilename, cDataStreamReadable | cDataStreamSeekable, true)) { close(); return false; } m_trace_file_size = m_trace_stream.get_size(); if (m_trace_stream.read(&m_sof_packet, sizeof(m_sof_packet)) != sizeof(m_sof_packet)) { close(); return false; } if (!m_sof_packet.full_validation(sizeof(m_sof_packet))) { close(); return false; } if (m_sof_packet.m_version < static_cast<uint16>(VOGL_TRACE_FILE_VERSION)) { vogl_error_printf("%s: Trace file version is not supported, found version 0x%04X, expected version 0x%04X!\n", VOGL_FUNCTION_INFO_CSTR, m_sof_packet.m_version, VOGL_TRACE_FILE_VERSION); return false; } else if (m_sof_packet.m_version > static_cast<uint16>(VOGL_TRACE_FILE_VERSION)) { // TODO: Make this an error? Backwards compat? vogl_warning_printf("%s: Trace file version is 0x%04X, expected version 0x%04X, this may not work at all!\n", VOGL_FUNCTION_INFO_CSTR, m_sof_packet.m_version, VOGL_TRACE_FILE_VERSION); } if (m_sof_packet.m_archive_size) { if (!m_archive_blob_manager.init_file(cBMFReadable | cBMFOpenExisting, pFilename, m_sof_packet.m_archive_offset, m_sof_packet.m_archive_size)) { vogl_error_printf("%s: Failed reading in-trace archive!\n", VOGL_FUNCTION_INFO_CSTR); return false; } } m_packet_buf.reserve(512 * 1024); m_trace_stream.seek(m_sof_packet.m_first_packet_offset, false); if (!read_frame_file_offsets()) { // Keep this in sync with the offset pushed in vogl_init_tracefile()! m_frame_file_offsets.push_back(get_cur_file_ofs()); } return true; }
//---------------------------------------------------------------------------------------------------------------------- // tool_replay_mode //---------------------------------------------------------------------------------------------------------------------- static bool tool_replay_mode() { VOGL_FUNC_TRACER dynamic_string trace_filename(g_command_line_params().get_value_as_string_or_empty("", 1)); if (trace_filename.is_empty()) { vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_INFO_CSTR); return false; } dynamic_string actual_trace_filename; vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file( trace_filename, actual_trace_filename, g_command_line_params().get_value_as_string_or_empty("loose_file_path").get_ptr())); if (!pTrace_reader.get()) { vogl_error_printf("%s: File not found, or unable to determine file type of trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, trace_filename.get_ptr()); return false; } vogl_printf("Reading trace file %s\n", actual_trace_filename.get_ptr()); vogl_gl_replayer replayer; vogl_replay_window window; uint replayer_flags = get_replayer_flags_from_command_line_params(); // TODO: This will create a window with default attributes, which seems fine for the majority of traces. // Unfortunately, some GL call streams *don't* want an alpha channel, or depth, or stencil etc. in the default framebuffer so this may become a problem. // Also, this design only supports a single window, which is going to be a problem with multiple window traces. if (!window.open(g_command_line_params().get_value_as_int("width", 0, 1024, 1, 65535), g_command_line_params().get_value_as_int("height", 0, 768, 1, 65535), g_command_line_params().get_value_as_int("msaa", 0, 0, 0, 65535))) { vogl_error_printf("%s: Failed initializing replay window\n", VOGL_FUNCTION_INFO_CSTR); return false; } if (!replayer.init(replayer_flags, &window, pTrace_reader->get_sof_packet(), pTrace_reader->get_multi_blob_manager())) { vogl_error_printf("%s: Failed initializing GL replayer\n", VOGL_FUNCTION_INFO_CSTR); return false; } // Disable all glGetError() calls in vogl_utils.cpp. vogl_disable_gl_get_error(); XSelectInput(window.get_display(), window.get_xwindow(), EnterWindowMask | LeaveWindowMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | StructureNotifyMask | KeymapStateMask); Atom wmDeleteMessage = XInternAtom(window.get_display(), "WM_DELETE_WINDOW", False); XSetWMProtocols(window.get_display(), window.get_xwindow(), &wmDeleteMessage, 1); // Bool win_mapped = false; vogl_gl_state_snapshot *pSnapshot = NULL; int64_t snapshot_loop_start_frame = -1; int64_t snapshot_loop_end_frame = -1; vogl::hash_map<uint64_t> keys_pressed, keys_down; int loop_frame = g_command_line_params().get_value_as_int("loop_frame", 0, -1); int loop_len = math::maximum<int>(g_command_line_params().get_value_as_int("loop_len", 0, 1), 1); int loop_count = math::maximum<int>(g_command_line_params().get_value_as_int("loop_count", 0, cINT32_MAX), 1); bool endless_mode = g_command_line_params().get_value_as_bool("endless"); timer tm; tm.start(); for (;;) { tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Main Loop"); while (X11_Pending(window.get_display())) { XEvent newEvent; // Watch for new X eventsn XNextEvent(window.get_display(), &newEvent); switch (newEvent.type) { case KeyPress: { KeySym xsym = XLookupKeysym(&newEvent.xkey, 0); //printf("KeyPress 0%04llX %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym); keys_down.insert(xsym); keys_pressed.insert(xsym); break; } case KeyRelease: { KeySym xsym = XLookupKeysym(&newEvent.xkey, 0); //printf("KeyRelease 0x%04llX %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym); keys_down.erase(xsym); break; } case FocusIn: case FocusOut: { //printf("FocusIn/FocusOut\n"); keys_down.reset(); break; } case MappingNotify: { //XRefreshKeyboardMapping(&newEvent); break; } case UnmapNotify: { // printf("UnmapNotify\n"); // win_mapped = false; keys_down.reset(); break; } case MapNotify: { // printf("MapNotify\n"); // win_mapped = true; keys_down.reset(); if (!replayer.update_window_dimensions()) goto error_exit; break; } case ConfigureNotify: { if (!replayer.update_window_dimensions()) goto error_exit; break; } case DestroyNotify: { vogl_message_printf("Exiting\n"); goto normal_exit; } case ClientMessage: { if (newEvent.xclient.data.l[0] == (int)wmDeleteMessage) { vogl_message_printf("Exiting\n"); goto normal_exit; } break; } default: break; } } if (replayer.get_at_frame_boundary()) { if ((!pSnapshot) && (loop_frame != -1) && (static_cast<int64_t>(replayer.get_frame_index()) == loop_frame)) { vogl_debug_printf("%s: Capturing replayer state at start of frame %u\n", VOGL_FUNCTION_INFO_CSTR, replayer.get_frame_index()); pSnapshot = replayer.snapshot_state(); if (pSnapshot) { vogl_printf("Snapshot succeeded\n"); snapshot_loop_start_frame = pTrace_reader->get_cur_frame(); snapshot_loop_end_frame = pTrace_reader->get_cur_frame() + loop_len; vogl_debug_printf("%s: Loop start: %" PRIi64 " Loop end: %" PRIi64 "\n", VOGL_FUNCTION_INFO_CSTR, snapshot_loop_start_frame, snapshot_loop_end_frame); } else { vogl_error_printf("Snapshot failed!\n"); loop_frame = -1; } } } vogl_gl_replayer::status_t status = replayer.process_pending_window_resize(); if (status == vogl_gl_replayer::cStatusOK) { for (;;) { status = replayer.process_next_packet(*pTrace_reader); if ((status == vogl_gl_replayer::cStatusNextFrame) || (status == vogl_gl_replayer::cStatusResizeWindow) || (status == vogl_gl_replayer::cStatusAtEOF) || (status == vogl_gl_replayer::cStatusHardFailure)) { break; } } } if (status == vogl_gl_replayer::cStatusHardFailure) break; if (status == vogl_gl_replayer::cStatusAtEOF) { vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_INFO_CSTR, replayer.get_frame_index()); } if (replayer.get_at_frame_boundary() && pSnapshot && (loop_count > 0) && ((pTrace_reader->get_cur_frame() == snapshot_loop_end_frame) || (status == vogl_gl_replayer::cStatusAtEOF))) { status = replayer.begin_applying_snapshot(pSnapshot, false); if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow)) goto error_exit; pTrace_reader->seek_to_frame(static_cast<uint>(snapshot_loop_start_frame)); vogl_debug_printf("%s: Applying snapshot and seeking back to frame %" PRIi64 "\n", VOGL_FUNCTION_INFO_CSTR, snapshot_loop_start_frame); loop_count--; } else { bool print_progress = (status == vogl_gl_replayer::cStatusAtEOF) || ((replayer.get_at_frame_boundary()) && ((replayer.get_frame_index() % 100) == 0)); if (print_progress) { if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER) { vogl_binary_trace_file_reader &binary_trace_reader = *static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get()); vogl_printf("Replay now at frame index %u, trace file offet %" PRIu64 ", GL call counter %" PRIu64 ", %3.2f%% percent complete\n", replayer.get_frame_index(), binary_trace_reader.get_cur_file_ofs(), replayer.get_last_parsed_call_counter(), binary_trace_reader.get_trace_file_size() ? (binary_trace_reader.get_cur_file_ofs() * 100.0f) / binary_trace_reader.get_trace_file_size() : 0); } } if (status == vogl_gl_replayer::cStatusAtEOF) { if (!endless_mode) { double time_since_start = tm.get_elapsed_secs(); vogl_printf("%u total swaps, %.3f secs, %3.3f avg fps\n", replayer.get_total_swaps(), time_since_start, replayer.get_frame_index() / time_since_start); break; } vogl_printf("Resetting state and rewinding back to frame 0\n"); replayer.reset_state(); if (!pTrace_reader->seek_to_frame(0)) { vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_INFO_CSTR); goto error_exit; } } } telemetry_tick(); } normal_exit: return true; error_exit: return false; }
//---------------------------------------------------------------------------------------------------------------------- // Function vogl_init_gl_entrypoint_descs //---------------------------------------------------------------------------------------------------------------------- void vogl_init_gl_entrypoint_descs() { gl_entrypoint_param_desc_t *pDst = &g_vogl_entrypoint_param_descs[0][0]; #define DEF_FUNCTION_BEGIN(exported, category, ret, ret_type, num_params, name, args, params) #define DEF_FUNCTION_INFO(namespace_index, return_spectype, category, version, profile, deprecated, is_whitelisted, is_nullable, whitelisted_for_displaylists, listable) #define DEF_FUNCTION_BEGIN_PARAMS \ { \ gl_entrypoint_param_desc_t *pCur = pDst; #define DEF_FUNCTION_IN_VALUE_PARAM(namespace_index, spectype, type, ctype, name) \ { \ pCur->m_pSpec_type = #spectype; \ pCur->m_pName = #name; \ pCur->m_ctype = ctype; \ pCur->m_class = VOGL_VALUE_PARAM; \ pCur->m_input = true; \ pCur->m_namespace = (vogl_namespace_t)namespace_index; \ ++pCur; \ } #define DEF_FUNCTION_IN_REFERENCE_PARAM(namespace_index, spectype, type, ctype, name) \ { \ pCur->m_pSpec_type = #spectype; \ pCur->m_pName = #name; \ pCur->m_ctype = ctype; \ pCur->m_class = VOGL_REF_PARAM; \ pCur->m_input = true; \ pCur->m_namespace = (vogl_namespace_t)namespace_index; \ ++pCur; \ } #define DEF_FUNCTION_IN_ARRAY_PARAM(namespace_index, spectype, type, ctype, name, size) \ { \ pCur->m_pSpec_type = #spectype; \ pCur->m_pName = #name; \ pCur->m_ctype = ctype; \ pCur->m_class = VOGL_ARRAY_PARAM; \ pCur->m_input = true; \ pCur->m_pSize = #size; \ pCur->m_namespace = (vogl_namespace_t)namespace_index; \ ++pCur; \ } #define DEF_FUNCTION_OUT_REFERENCE_PARAM(namespace_index, spectype, type, ctype, name) \ { \ pCur->m_pSpec_type = #spectype; \ pCur->m_pName = #name; \ pCur->m_ctype = ctype; \ pCur->m_class = VOGL_ARRAY_PARAM; \ pCur->m_input = false; \ pCur->m_namespace = (vogl_namespace_t)namespace_index; \ ++pCur; \ } #define DEF_FUNCTION_OUT_ARRAY_PARAM(namespace_index, spectype, type, ctype, name, size) \ { \ pCur->m_pSpec_type = #spectype; \ pCur->m_pName = #name; \ pCur->m_ctype = ctype; \ pCur->m_class = VOGL_ARRAY_PARAM; \ pCur->m_input = false; \ pCur->m_pSize = #size; \ pCur->m_namespace = (vogl_namespace_t)namespace_index; \ ++pCur; \ } #define DEF_FUNCTION_END_PARAMS \ VOGL_ASSERT((pCur - pDst) <= VOGL_MAX_ENTRYPOINT_PARAMETERS); \ } #define DEF_FUNCTION_RETURN(spectype, type, ctype) #define DEF_FUNCTION_END(exported, category, ret, ret_type, num_params, name, args, params) pDst += VOGL_MAX_ENTRYPOINT_PARAMETERS; #include "gl_glx_wgl_func_descs.inc" for (uint32_t i = 0; i < VOGL_ARRAY_SIZE(g_custom_array_size_macro_indices); i++) { uint32_t idx = g_custom_array_size_macro_indices[i]; uint32_t func = idx >> 16; uint32_t param = idx & 0xFFFF; VOGL_ASSERT(func < VOGL_NUM_ENTRYPOINTS); VOGL_ASSERT(param < g_vogl_entrypoint_descs[func].m_num_params); g_vogl_entrypoint_param_descs[func][param].m_has_custom_array_size_macro = true; } for (uint32_t i = 0; i < VOGL_NUM_ENTRYPOINTS; i++) { gl_entrypoint_desc_t &desc = g_vogl_entrypoint_descs[i]; VOGL_ASSERT(desc.m_return_namespace >= VOGL_NAMESPACE_UNKNOWN); VOGL_ASSERT(desc.m_return_namespace < VOGL_TOTAL_NAMESPACES); if (strcmp(desc.m_pName, "glGenTextures") == 0) { if (g_vogl_entrypoint_param_descs[i][1].m_namespace != VOGL_NAMESPACE_TEXTURES) { vogl_error_printf("%s: vogl_namespace_t enum is bad, please rebuild", VOGL_FUNCTION_INFO_CSTR); exit(EXIT_FAILURE); } } if (desc.m_return_ctype != VOGL_VOID) { if ((size_t)get_vogl_process_gl_ctypes()[desc.m_return_ctype].m_size < 1) vogl_warning_printf("%s: function %s's return ctype %s is too small\n", VOGL_FUNCTION_INFO_CSTR, desc.m_pName, get_vogl_process_gl_ctypes()[desc.m_return_ctype].m_pName); if ((size_t)get_vogl_process_gl_ctypes()[desc.m_return_ctype].m_size > sizeof(uint64_t)) vogl_warning_printf("%s: function %s's return ctype %s is too large\n", VOGL_FUNCTION_INFO_CSTR, desc.m_pName, get_vogl_process_gl_ctypes()[desc.m_return_ctype].m_pName); } for (uint32_t j = 0; j < desc.m_num_params; j++) { if ((size_t)get_vogl_process_gl_ctypes()[g_vogl_entrypoint_param_descs[i][j].m_ctype].m_size < 1) vogl_warning_printf("%s: param %u of function %s ctype %s is too small\n", VOGL_FUNCTION_INFO_CSTR, j, desc.m_pName, get_vogl_process_gl_ctypes()[g_vogl_entrypoint_param_descs[i][j].m_ctype].m_pName); if ((size_t)get_vogl_process_gl_ctypes()[g_vogl_entrypoint_param_descs[i][j].m_ctype].m_size > sizeof(uint64_t)) vogl_warning_printf("%s: param %u of function %s ctype %s is too large\n", VOGL_FUNCTION_INFO_CSTR, j, desc.m_pName, get_vogl_process_gl_ctypes()[g_vogl_entrypoint_param_descs[i][j].m_ctype].m_pName); } desc.m_pAPI_prefix = "GL"; // FIXME: Add more prefixes as we add platforms. voglgen should probably supply this. // They must correspond with the GL enum prefixes in glx_enum_desc.inc, gl_enum_desc.inc, etc. if ((desc.m_pName[0] == 'w') && (desc.m_pName[1] == 'g') && (desc.m_pName[2] == 'l')) desc.m_pAPI_prefix = "WGL"; else if ((desc.m_pName[0] == 'g') && (desc.m_pName[1] == 'l') && (desc.m_pName[2] == 'X')) desc.m_pAPI_prefix = "GLX"; else if ((desc.m_pName[0] == 'g') && (desc.m_pName[1] == 'l')) desc.m_pAPI_prefix = "GL"; else { vogl_warning_printf("%s: Unknown function prefix: %s\n", VOGL_FUNCTION_INFO_CSTR, desc.m_pName); } } for (uint32_t i = 0; i < VOGL_NUM_ENTRYPOINTS; i++) { get_vogl_entrypoint_hashmap().insert(g_vogl_entrypoint_descs[i].m_pName, static_cast<gl_entrypoint_id_t>(i)); } }
bool file_utils::is_older_than(const char *pSrcFilename, const char *pDstFilename) { vogl_error_printf("Unimplemented\n"); return false; }
//---------------------------------------------------------------------------------------------------------------------- // tool_replay_mode //---------------------------------------------------------------------------------------------------------------------- static bool tool_replay_mode() { VOGL_FUNC_TRACER dynamic_string trace_filename(g_command_line_params().get_value_as_string_or_empty("", 1)); if (trace_filename.is_empty()) { vogl_error_printf("No trace file specified!\n"); return false; } dynamic_string actual_trace_filename; vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader( vogl_open_trace_file( trace_filename, actual_trace_filename, g_command_line_params().get_value_as_string_or_empty("loose_file_path").get_ptr() ) ); if (!pTrace_reader.get()) { vogl_error_printf("File not found, or unable to determine file type of trace file \"%s\"\n", trace_filename.get_ptr()); return false; } vogl_printf("Reading trace file %s\n", actual_trace_filename.get_ptr()); vogl_gl_replayer replayer; vogl_replay_window window; #if defined(PLATFORM_WINDOWS) // We need to get proc addresses for windows late. replayer.set_proc_address_helper(vogl_get_proc_address_helper, false); #endif uint replayer_flags = get_replayer_flags_from_command_line_params(); // TODO: This will create a window with default attributes, which seems fine for the majority of traces. // Unfortunately, some GL call streams *don't* want an alpha channel, or depth, or stencil etc. in the default framebuffer so this may become a problem. // Also, this design only supports a single window, which is going to be a problem with multiple window traces. if (!window.open(g_command_line_params().get_value_as_int("width", 0, 1024, 1, 65535), g_command_line_params().get_value_as_int("height", 0, 768, 1, 65535), g_command_line_params().get_value_as_int("msaa", 0, 0, 0, 65535))) { vogl_error_printf("Failed initializing replay window\n"); return false; } if (!replayer.init(replayer_flags, &window, pTrace_reader->get_sof_packet(), pTrace_reader->get_multi_blob_manager())) { vogl_error_printf("Failed initializing GL replayer\n"); return false; } // Disable all glGetError() calls in vogl_utils.cpp. vogl_disable_gl_get_error(); // Bool win_mapped = false; vogl_gl_state_snapshot *pSnapshot = NULL; int64_t snapshot_loop_start_frame = -1; int64_t snapshot_loop_end_frame = -1; vogl::hash_map<SDL_Keycode> keys_pressed, keys_down; int loop_frame = g_command_line_params().get_value_as_int("loop_frame", 0, -1); int loop_len = math::maximum<int>(g_command_line_params().get_value_as_int("loop_len", 0, 1), 1); int loop_count = math::maximum<int>(g_command_line_params().get_value_as_int("loop_count", 0, cINT32_MAX), 1); bool endless_mode = g_command_line_params().get_value_as_bool("endless"); timer tm; tm.start(); for (;;) { tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Main Loop"); SDL_Event wnd_event; while (SDL_PollEvent(&wnd_event)) { switch (wnd_event.type) { case SDL_KEYDOWN: { keys_down.insert(wnd_event.key.keysym.sym); keys_pressed.insert(wnd_event.key.keysym.sym); break; } case SDL_KEYUP: { keys_down.erase(wnd_event.key.keysym.sym); break; } case SDL_WINDOWEVENT: { switch(wnd_event.window.event) { case SDL_WINDOWEVENT_FOCUS_GAINED: case SDL_WINDOWEVENT_FOCUS_LOST: keys_down.reset(); break; case SDL_WINDOWEVENT_CLOSE: vogl_message_printf("Window told to close, exiting.\n"); goto normal_exit; break; default: break; }; break; } default: // TODO: Handle these somehow? break; }; } if (replayer.get_at_frame_boundary()) { if ((!pSnapshot) && (loop_frame != -1) && (static_cast<int64_t>(replayer.get_frame_index()) == loop_frame)) { vogl_debug_printf("Capturing replayer state at start of frame %u\n", replayer.get_frame_index()); pSnapshot = replayer.snapshot_state(); if (pSnapshot) { vogl_printf("Snapshot succeeded\n"); snapshot_loop_start_frame = pTrace_reader->get_cur_frame(); snapshot_loop_end_frame = pTrace_reader->get_cur_frame() + loop_len; vogl_debug_printf("Loop start: %" PRIi64 " Loop end: %" PRIi64 "\n", snapshot_loop_start_frame, snapshot_loop_end_frame); } else { vogl_error_printf("Snapshot failed!\n"); loop_frame = -1; } } } vogl_gl_replayer::status_t status = replayer.process_pending_window_resize(); if (status == vogl_gl_replayer::cStatusOK) { for (;;) { status = replayer.process_pending_packets(); if (status == vogl_gl_replayer::cStatusOK) { status = replayer.process_next_packet(*pTrace_reader); } if ((status == vogl_gl_replayer::cStatusNextFrame) || (status == vogl_gl_replayer::cStatusResizeWindow) || (status == vogl_gl_replayer::cStatusAtEOF) || (status == vogl_gl_replayer::cStatusHardFailure)) { break; } } } if (status == vogl_gl_replayer::cStatusHardFailure) break; if (status == vogl_gl_replayer::cStatusAtEOF) { vogl_message_printf("At trace EOF, frame index %u\n", replayer.get_frame_index()); } if (replayer.get_at_frame_boundary() && pSnapshot && (loop_count > 0) && ((pTrace_reader->get_cur_frame() == snapshot_loop_end_frame) || (status == vogl_gl_replayer::cStatusAtEOF))) { status = replayer.begin_applying_snapshot(pSnapshot, false); if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow)) goto error_exit; pTrace_reader->seek_to_frame(static_cast<uint>(snapshot_loop_start_frame)); vogl_debug_printf("Applying snapshot and seeking back to frame %" PRIi64 "\n", snapshot_loop_start_frame); loop_count--; } else { bool print_progress = (status == vogl_gl_replayer::cStatusAtEOF) || ((replayer.get_at_frame_boundary()) && ((replayer.get_frame_index() % 100) == 0)); if (print_progress) { if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER) { vogl_binary_trace_file_reader &binary_trace_reader = *static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get()); vogl_printf("Replay now at frame index %u, trace file offet %" PRIu64 ", GL call counter %" PRIu64 ", %3.2f%% percent complete\n", replayer.get_frame_index(), binary_trace_reader.get_cur_file_ofs(), replayer.get_last_parsed_call_counter(), binary_trace_reader.get_trace_file_size() ? (binary_trace_reader.get_cur_file_ofs() * 100.0f) / binary_trace_reader.get_trace_file_size() : 0); } } if (status == vogl_gl_replayer::cStatusAtEOF) { if (!endless_mode) { double time_since_start = tm.get_elapsed_secs(); if (replayer.get_flags() & cGLReplayerFSPreprocessor) { double fs_pp_time = replayer.get_fs_pp_time(); vogl_printf("Ran with FS Preprocessor (FSPP) enabled:\n"); vogl_printf("FSPP Time: %.3f secs\n", fs_pp_time); vogl_printf("Overall Stats: %u total swaps, %.3f secs, %3.3f avg fps\n", replayer.get_total_swaps(), time_since_start, replayer.get_frame_index() / time_since_start); vogl_printf("Stats Excluding FSPP time: %u total swaps, %.3f secs, %3.3f avg fps\n", replayer.get_total_swaps(), time_since_start-fs_pp_time, replayer.get_frame_index() / (time_since_start-fs_pp_time)); break; } else { vogl_printf("%u total swaps, %.3f secs, %3.3f avg fps\n", replayer.get_total_swaps(), time_since_start, replayer.get_frame_index() / time_since_start); break; } } vogl_printf("Resetting state and rewinding back to frame 0\n"); replayer.reset_state(); if (!pTrace_reader->seek_to_frame(0)) { vogl_error_printf("Failed rewinding trace reader!\n"); goto error_exit; } } } telemetry_tick(); } normal_exit: return true; error_exit: return false; }
bool vogl_default_framebuffer_state::snapshot(const vogl_context_info &context_info, const vogl_default_framebuffer_attribs &fb_attribs) { VOGL_NOTE_UNUSED(context_info); clear(); m_fb_attribs = fb_attribs; // Create compatible GL texture // Attach this texture to an FBO // Blit default framebuffer to this FBO // Capture this texture's state vogl_scoped_state_saver framebuffer_state_saver(cGSTReadBuffer, cGSTDrawBuffer); vogl_scoped_binding_state orig_framebuffers(GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER, GL_TEXTURE_2D, GL_TEXTURE_2D_MULTISAMPLE); GL_ENTRYPOINT(glBindFramebuffer)(GL_READ_FRAMEBUFFER, 0); VOGL_CHECK_GL_ERROR; vogl_scoped_binding_state orig_bindings(GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER); GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_PACK_BUFFER, 0); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_UNPACK_BUFFER, 0); VOGL_CHECK_GL_ERROR; vogl_scoped_state_saver pixelstore_state_saver(cGSTPixelStore); vogl_scoped_state_saver pixeltransfer_state_saver; if (!context_info.is_core_profile()) pixeltransfer_state_saver.save(cGSTPixelTransfer); vogl_reset_pixel_store_states(); if (!context_info.is_core_profile()) vogl_reset_pixel_transfer_states(); // TODO: Test multisampled default framebuffers const GLenum tex_target = (fb_attribs.m_samples > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; for (uint i = 0; i < cDefFramebufferTotal; i++) { GLenum internal_fmt, pixel_fmt, pixel_type; // TODO: This uses fixed pixel formats, and assumes there's always a depth/stencil buffer. if (i == cDefFramebufferDepthStencil) { if ((fb_attribs.m_depth_size + fb_attribs.m_stencil_size) == 0) continue; GL_ENTRYPOINT(glReadBuffer)(fb_attribs.m_double_buffered ? GL_BACK_LEFT : GL_FRONT_LEFT); internal_fmt = GL_DEPTH_STENCIL; pixel_fmt = GL_DEPTH_STENCIL; pixel_type = GL_UNSIGNED_INT_24_8; } else { if ((fb_attribs.m_r_size + fb_attribs.m_g_size + fb_attribs.m_b_size + fb_attribs.m_a_size) == 0) continue; GL_ENTRYPOINT(glReadBuffer)(g_def_framebuffer_enums[i]); internal_fmt = GL_RGBA; pixel_fmt = GL_RGBA; pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV; } if (vogl_check_gl_error_internal(true)) continue; // Create texture GLuint tex_handle = 0; GL_ENTRYPOINT(glGenTextures)(1, &tex_handle); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glBindTexture)(tex_target, tex_handle); VOGL_CHECK_GL_ERROR; if (fb_attribs.m_samples > 1) { GL_ENTRYPOINT(glTexImage2DMultisample)(tex_target, fb_attribs.m_samples, internal_fmt, fb_attribs.m_width, fb_attribs.m_height, GL_TRUE); } else { GL_ENTRYPOINT(glTexImage2D)(tex_target, 0, internal_fmt, fb_attribs.m_width, fb_attribs.m_height, 0, pixel_fmt, pixel_type, NULL); } if (vogl_check_gl_error_internal()) { GL_ENTRYPOINT(glBindTexture)(tex_target, 0); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glDeleteTextures)(1, &tex_handle); VOGL_CHECK_GL_ERROR; continue; } GL_ENTRYPOINT(glTexParameteri)(tex_target, GL_TEXTURE_MAX_LEVEL, 0); VOGL_CHECK_GL_ERROR; // Create FBO GLuint fbo_handle = 0; GL_ENTRYPOINT(glGenFramebuffers)(1, &fbo_handle); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glBindFramebuffer)(GL_DRAW_FRAMEBUFFER, fbo_handle); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glFramebufferTexture2D)(GL_DRAW_FRAMEBUFFER, (i == cDefFramebufferDepthStencil) ? GL_DEPTH_STENCIL_ATTACHMENT : GL_COLOR_ATTACHMENT0, tex_target, tex_handle, 0); VOGL_CHECK_GL_ERROR; GLenum draw_buf = (i == cDefFramebufferDepthStencil) ? GL_NONE : GL_COLOR_ATTACHMENT0; GL_ENTRYPOINT(glDrawBuffers)(1, &draw_buf); VOGL_CHECK_GL_ERROR; GLenum cur_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_DRAW_FRAMEBUFFER); VOGL_CHECK_GL_ERROR; bool status = true; if (cur_status == GL_FRAMEBUFFER_COMPLETE) { GL_ENTRYPOINT(glBlitFramebuffer)( 0, 0, fb_attribs.m_width, fb_attribs.m_height, 0, 0, fb_attribs.m_width, fb_attribs.m_height, (i == cDefFramebufferDepthStencil) ? (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) : GL_COLOR_BUFFER_BIT, GL_NEAREST); if (vogl_check_gl_error_internal()) { status = false; } } if (status) { vogl_handle_remapper def_handle_remapper; status = m_textures[i].snapshot(context_info, def_handle_remapper, tex_handle, tex_target); if (!status) { vogl_error_printf("%s: Failed snapshotting texture for default framebuffer %s\n", VOGL_METHOD_NAME, g_gl_enums.find_gl_name(g_def_framebuffer_enums[i])); } } else { vogl_warning_printf("%s: Failed blitting framebuffer %u\n", VOGL_METHOD_NAME, i); } // Delete FBO GL_ENTRYPOINT(glBindFramebuffer)(GL_DRAW_FRAMEBUFFER, 0); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glDeleteFramebuffers)(1, &fbo_handle); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glDrawBuffer)(GL_FRONT_LEFT); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glBindTexture)(tex_target, 0); VOGL_CHECK_GL_ERROR; GL_ENTRYPOINT(glDeleteTextures)(1, &tex_handle); VOGL_CHECK_GL_ERROR; } m_valid = true; return true; }