video_manager::video_manager(running_machine &machine) : m_machine(machine) , m_screenless_frame_timer(nullptr) , m_output_changed(false) , m_throttle_last_ticks(0) , m_throttle_realtime(attotime::zero) , m_throttle_emutime(attotime::zero) , m_throttle_history(0) , m_speed_last_realtime(0) , m_speed_last_emutime(attotime::zero) , m_speed_percent(1.0) , m_overall_real_seconds(0) , m_overall_real_ticks(0) , m_overall_emutime(attotime::zero) , m_overall_valid_counter(0) , m_throttled(machine.options().throttle()) , m_throttle_rate(1.0f) , m_fastforward(false) , m_seconds_to_run(machine.options().seconds_to_run()) , m_auto_frameskip(machine.options().auto_frameskip()) , m_speed(original_speed_setting()) , m_empty_skip_count(0) , m_frameskip_level(machine.options().frameskip()) , m_frameskip_counter(0) , m_frameskip_adjust(0) , m_skipping_this_frame(false) , m_average_oversleep(0) , m_snap_target(nullptr) , m_snap_native(true) , m_snap_width(0) , m_snap_height(0) , m_timecode_enabled(false) , m_timecode_write(false) , m_timecode_text("") , m_timecode_start(attotime::zero) , m_timecode_total(attotime::zero) { // request a callback upon exiting machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&video_manager::exit, this)); machine.save().register_postload(save_prepost_delegate(FUNC(video_manager::postload), this)); // extract initial execution state from global configuration settings update_refresh_speed(); const unsigned screen_count(screen_device_iterator(machine.root_device()).count()); const bool no_screens(!screen_count); // create a render target for snapshots const char *viewname = machine.options().snap_view(); m_snap_native = !no_screens && (viewname[0] == 0 || strcmp(viewname, "native") == 0); if (m_snap_native) { // the native target is hard-coded to our internal layout and has all options disabled util::xml::file::ptr const root(util::xml::file::create()); if (!root) throw emu_fatalerror("Couldn't create XML document??"); util::xml::data_node *const layoutnode(root->add_child("mamelayout", nullptr)); if (!layoutnode) throw emu_fatalerror("Couldn't create XML node??"); layoutnode->set_attribute_int("version", 2); for (unsigned i = 0; screen_count > i; ++i) { util::xml::data_node *const viewnode(layoutnode->add_child("view", nullptr)); if (!viewnode) throw emu_fatalerror("Couldn't create XML node??"); viewnode->set_attribute("name", util::xml::normalize_string(util::string_format("s%1$u", i).c_str())); util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr)); if (!screennode) throw emu_fatalerror("Couldn't create XML node??"); screennode->set_attribute_int("index", i); util::xml::data_node *const boundsnode(screennode->add_child("bounds", nullptr)); if (!boundsnode) throw emu_fatalerror("Couldn't create XML node??"); boundsnode->set_attribute_int("left", 0); boundsnode->set_attribute_int("top", 0); boundsnode->set_attribute_int("right", 1); boundsnode->set_attribute_int("bottom", 1); } m_snap_target = machine.render().target_alloc(*root, RENDER_CREATE_SINGLE_FILE | RENDER_CREATE_HIDDEN); m_snap_target->set_backdrops_enabled(false); m_snap_target->set_overlays_enabled(false); m_snap_target->set_bezels_enabled(false); m_snap_target->set_cpanels_enabled(false); m_snap_target->set_marquees_enabled(false); m_snap_target->set_screen_overlay_enabled(false); m_snap_target->set_zoom_to_screen(false); } else { // otherwise, non-default targets select the specified view and turn off effects m_snap_target = machine.render().target_alloc(nullptr, RENDER_CREATE_HIDDEN); m_snap_target->set_view(m_snap_target->configured_view(viewname, 0, 1)); m_snap_target->set_screen_overlay_enabled(false); } // extract snap resolution if present if (sscanf(machine.options().snap_size(), "%dx%d", &m_snap_width, &m_snap_height) != 2) m_snap_width = m_snap_height = 0; // start recording movie if specified const char *filename = machine.options().mng_write(); if (filename[0] != 0) begin_recording(filename, MF_MNG); filename = machine.options().avi_write(); if (filename[0] != 0) begin_recording(filename, MF_AVI); // if no screens, create a periodic timer to drive updates if (no_screens) { m_screenless_frame_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(video_manager::screenless_update_callback), this)); m_screenless_frame_timer->adjust(screen_device::DEFAULT_FRAME_PERIOD, 0, screen_device::DEFAULT_FRAME_PERIOD); machine.output().set_notifier(nullptr, video_notifier_callback, this); } }
void sdl_window_info::update() { osd_ticks_t event_wait_ticks; // adjust the cursor state //sdlwindow_update_cursor_state(machine, window); update_cursor_state(); // if we're visible and running and not in the middle of a resize, draw if (m_target != nullptr) { int tempwidth, tempheight; // see if the games video mode has changed m_target->compute_minimum_size(tempwidth, tempheight); if (osd_dim(tempwidth, tempheight) != m_minimum_dim) { m_minimum_dim = osd_dim(tempwidth, tempheight); if (!this->m_fullscreen) { //Don't resize window without user interaction; //window_resize(blitwidth, blitheight); } else if (video_config.switchres) { osd_dim tmp = this->pick_best_mode(); resize(tmp.width(), tmp.height()); } } if (video_config.waitvsync && video_config.syncrefresh) event_wait_ticks = osd_ticks_per_second(); // block at most a second else event_wait_ticks = 0; if (m_rendered_event.wait(event_wait_ticks)) { const int update = 1; // ensure the target bounds are up-to-date, and then get the primitives render_primitive_list &primlist = *renderer().get_primitives(); // and redraw now // Some configurations require events to be polled in the worker thread downcast< sdl_osd_interface& >(machine().osd()).process_events_buf(); // Check whether window has vector screens { const screen_device *screen = screen_device_iterator(machine().root_device()).byindex(m_index); if ((screen != nullptr) && (screen->screen_type() == SCREEN_TYPE_VECTOR)) renderer().set_flags(osd_renderer::FLAG_HAS_VECTOR_SCREEN); else renderer().clear_flags(osd_renderer::FLAG_HAS_VECTOR_SCREEN); } m_primlist = &primlist; // if no bitmap, just fill if (m_primlist == nullptr) { } // otherwise, render with our drawing system else { if( video_config.perftest ) measure_fps(update); else renderer().draw(update); } /* all done, ready for next */ m_rendered_event.set(); } } }
void video_manager::record_frame() { // ignore if nothing to do if (!is_recording()) return; // start the profiler and get the current time g_profiler.start(PROFILER_MOVIE_REC); attotime curtime = machine().time(); screen_device_iterator device_iterator = screen_device_iterator(machine().root_device()); screen_device_iterator::auto_iterator iter = device_iterator.begin(); for (uint32_t index = 0; index < (std::max)(m_mngs.size(), m_avis.size()); index++, iter++) { // create the bitmap create_snapshot_bitmap(iter.current()); // handle an AVI recording if ((index < m_avis.size()) && m_avis[index].m_avi_file) { avi_info_t &avi_info = m_avis[index]; // loop until we hit the right time while (avi_info.m_avi_next_frame_time <= curtime) { // write the next frame avi_file::error avierr = avi_info.m_avi_file->append_video_frame(m_snap_bitmap); if (avierr != avi_file::error::NONE) { g_profiler.stop(); // FIXME: double exit if this happens? end_recording_avi(index); break; } // advance time avi_info.m_avi_next_frame_time += avi_info.m_avi_frame_period; avi_info.m_avi_frame++; } } // handle a MNG recording if ((index < m_mngs.size()) && m_mngs[index].m_mng_file) { mng_info_t &mng_info = m_mngs[index]; // loop until we hit the right time while (mng_info.m_mng_next_frame_time <= curtime) { // set up the text fields in the movie info png_info pnginfo; if (mng_info.m_mng_frame == 0) { std::string text1 = std::string(emulator_info::get_appname()).append(" ").append(emulator_info::get_build_version()); std::string text2 = std::string(machine().system().manufacturer).append(" ").append(machine().system().type.fullname()); pnginfo.add_text("Software", text1.c_str()); pnginfo.add_text("System", text2.c_str()); } // write the next frame screen_device *screen = iter.current(); const rgb_t *palette = (screen != nullptr && screen->has_palette()) ? screen->palette().palette()->entry_list_adjusted() : nullptr; int entries = (screen != nullptr && screen->has_palette()) ? screen->palette().entries() : 0; png_error error = mng_capture_frame(*mng_info.m_mng_file, pnginfo, m_snap_bitmap, entries, palette); if (error != PNGERR_NONE) { g_profiler.stop(); // FIXME: double exit if this happens? end_recording_mng(index); break; } // advance time mng_info.m_mng_next_frame_time += mng_info.m_mng_frame_period; mng_info.m_mng_frame++; } } if (!m_snap_native) { break; } } g_profiler.stop(); }