diff --git a/src/emu/crsshair.cpp b/src/emu/crsshair.cpp index f4e69d26d50..11bde091d15 100644 --- a/src/emu/crsshair.cpp +++ b/src/emu/crsshair.cpp @@ -118,7 +118,7 @@ render_crosshair::render_crosshair(running_machine &machine, int player) , m_time(0) { // for now, use the main screen - m_screen = machine.first_screen(); + m_screen = screen_device_iterator(machine.root_device()).first(); } @@ -314,8 +314,9 @@ crosshair_manager::crosshair_manager(running_machine &machine) machine.configuration().config_register("crosshairs", config_load_delegate(&crosshair_manager::config_load, this), config_save_delegate(&crosshair_manager::config_save, this)); /* register the animation callback */ - if (machine.first_screen() != nullptr) - machine.first_screen()->register_vblank_callback(vblank_state_delegate(&crosshair_manager::animate, this)); + screen_device *first_screen = screen_device_iterator(machine.root_device()).first(); + if (first_screen != nullptr) + first_screen->register_vblank_callback(vblank_state_delegate(&crosshair_manager::animate, this)); } /*------------------------------------------------- diff --git a/src/emu/debug/debugcpu.cpp b/src/emu/debug/debugcpu.cpp index 52fb2f784f8..afd8884cad9 100644 --- a/src/emu/debug/debugcpu.cpp +++ b/src/emu/debug/debugcpu.cpp @@ -59,8 +59,6 @@ debugger_cpu::debugger_cpu(running_machine &machine) , m_last_periodic_update_time(0) , m_comments_loaded(false) { - screen_device *first_screen = m_machine.first_screen(); - m_tempvar = make_unique_clear(NUM_TEMP_VARIABLES); /* create a global symbol table */ @@ -75,9 +73,28 @@ debugger_cpu::debugger_cpu(running_machine &machine) using namespace std::placeholders; m_symtable->add("cpunum", std::bind(&debugger_cpu::get_cpunum, this, _1)); - m_symtable->add("beamx", std::bind(&debugger_cpu::get_beamx, this, _1, first_screen)); - m_symtable->add("beamy", std::bind(&debugger_cpu::get_beamy, this, _1, first_screen)); - m_symtable->add("frame", std::bind(&debugger_cpu::get_frame, this, _1, first_screen)); + + screen_device_iterator screen_iterator = screen_device_iterator(m_machine.root_device()); + screen_device_iterator::auto_iterator iter = screen_iterator.begin(); + const uint32_t count = (uint32_t)screen_iterator.count(); + + if (count == 1) + { + m_symtable->add("beamx", std::bind(&debugger_cpu::get_beamx, this, _1, iter.current())); + m_symtable->add("beamy", std::bind(&debugger_cpu::get_beamy, this, _1, iter.current())); + m_symtable->add("frame", std::bind(&debugger_cpu::get_frame, this, _1, iter.current())); + iter.current()->register_vblank_callback(vblank_state_delegate(&debugger_cpu::on_vblank, this)); + } + else if (count > 1) + { + for (uint32_t i = 0; i < count; i++, iter++) + { + m_symtable->add(string_format("beamx%d", i).c_str(), std::bind(&debugger_cpu::get_beamx, this, _1, iter.current())); + m_symtable->add(string_format("beamy%d", i).c_str(), std::bind(&debugger_cpu::get_beamy, this, _1, iter.current())); + m_symtable->add(string_format("frame%d", i).c_str(), std::bind(&debugger_cpu::get_frame, this, _1, iter.current())); + iter.current()->register_vblank_callback(vblank_state_delegate(&debugger_cpu::on_vblank, this)); + } + } /* add the temporary variables to the global symbol table */ for (int regnum = 0; regnum < NUM_TEMP_VARIABLES; regnum++) @@ -97,10 +114,6 @@ debugger_cpu::debugger_cpu(running_machine &machine) break; } } - - /* add callback for breaking on VBLANK */ - if (m_machine.first_screen() != nullptr) - m_machine.first_screen()->register_vblank_callback(vblank_state_delegate(&debugger_cpu::on_vblank, this)); } void debugger_cpu::configure_memory(symbol_table &table) diff --git a/src/emu/debug/dvstate.cpp b/src/emu/debug/dvstate.cpp index bf6212ec3b4..092c6885ca0 100644 --- a/src/emu/debug/dvstate.cpp +++ b/src/emu/debug/dvstate.cpp @@ -119,14 +119,30 @@ void debug_view_state::recompute() // add a cycles entry: cycles:99999999 m_state_list.emplace_back(REG_CYCLES, "cycles", 8); - // add a beam entry: beamx:1234 - m_state_list.emplace_back(REG_BEAMX, "beamx", 4); + screen_device_iterator screen_iterator = screen_device_iterator(machine().root_device()); + screen_device_iterator::auto_iterator iter = screen_iterator.begin(); + const int screen_count = screen_iterator.count(); - // add a beam entry: beamy:5678 - m_state_list.emplace_back(REG_BEAMY, "beamy", 4); + if (screen_count == 1) + { + // add a beam entry: beamx:1234 + m_state_list.emplace_back(REG_BEAMX, "beamx", 4); - // add a beam entry: frame:123456 - m_state_list.emplace_back(REG_FRAME, "frame", 6); + // add a beam entry: beamy:5678 + m_state_list.emplace_back(REG_BEAMY, "beamy", 4); + + // add a beam entry: frame:123456 + m_state_list.emplace_back(REG_FRAME, "frame", 6); + } + else if (screen_count > 1) + { + for (int i = 0; i < screen_count; i++, iter++) + { + m_state_list.emplace_back(REG_BEAMX_S0 - i, string_format("beamx%d", i).c_str(), 4); + m_state_list.emplace_back(REG_BEAMY_S0 - i, string_format("beamy%d", i).c_str(), 4); + m_state_list.emplace_back(REG_FRAME_S0 - i, string_format("frame%d", i).c_str(), 6); + } + } // add a flags entry: flags:xxxxxxxx m_state_list.emplace_back(STATE_GENFLAGS, "flags", source.m_stateintf->state_string_max_length(STATE_GENFLAGS)); @@ -196,7 +212,6 @@ void debug_view_state::view_update() // loop over rows auto it(m_state_list.begin()); - screen_device const *const screen(machine().first_screen()); debug_view_char *dest(&m_viewdata[0]); for (s32 index = 0, limit = m_topleft.y + m_visible.y; (index < limit) || (it != m_state_list.end()); ++index) { @@ -222,18 +237,21 @@ void debug_view_state::view_update() valstr = string_format("%-8d", curitem.value()); break; - case REG_BEAMX: - curitem.update(screen ? screen->hpos() : 0, cycles_changed); + case REG_BEAMX_S0: case REG_BEAMX_S1: case REG_BEAMX_S2: case REG_BEAMX_S3: + case REG_BEAMX_S4: case REG_BEAMX_S5: case REG_BEAMX_S6: case REG_BEAMX_S7: + curitem.update(screen_device_iterator(machine().root_device()).byindex(-(curitem.index() - REG_BEAMX_S0))->hpos(), cycles_changed); valstr = string_format("%4d", curitem.value()); break; - case REG_BEAMY: - curitem.update(screen ? screen->vpos() : 0, cycles_changed); + case REG_BEAMY_S0: case REG_BEAMY_S1: case REG_BEAMY_S2: case REG_BEAMY_S3: + case REG_BEAMY_S4: case REG_BEAMY_S5: case REG_BEAMY_S6: case REG_BEAMY_S7: + curitem.update(screen_device_iterator(machine().root_device()).byindex(-(curitem.index() - REG_BEAMY_S0))->vpos(), cycles_changed); valstr = string_format("%4d", curitem.value()); break; - case REG_FRAME: - curitem.update(screen ? screen->frame_number() : 0, cycles_changed); + case REG_FRAME_S0: case REG_FRAME_S1: case REG_FRAME_S2: case REG_FRAME_S3: + case REG_FRAME_S4: case REG_FRAME_S5: case REG_FRAME_S6: case REG_FRAME_S7: + curitem.update(screen_device_iterator(machine().root_device()).byindex(-(curitem.index() - REG_FRAME_S0))->frame_number(), cycles_changed); valstr = string_format("%-6d", curitem.value()); break; diff --git a/src/emu/debug/dvstate.h b/src/emu/debug/dvstate.h index 22ffb8be86f..8239a53f5db 100644 --- a/src/emu/debug/dvstate.h +++ b/src/emu/debug/dvstate.h @@ -89,8 +89,32 @@ private: static constexpr int REG_DIVIDER = -10; static constexpr int REG_CYCLES = -11; static constexpr int REG_BEAMX = -12; - static constexpr int REG_BEAMY = -13; - static constexpr int REG_FRAME = -14; + static constexpr int REG_BEAMX_S0 = -12; + static constexpr int REG_BEAMX_S1 = -13; + static constexpr int REG_BEAMX_S2 = -14; + static constexpr int REG_BEAMX_S3 = -15; + static constexpr int REG_BEAMX_S4 = -16; + static constexpr int REG_BEAMX_S5 = -17; + static constexpr int REG_BEAMX_S6 = -18; + static constexpr int REG_BEAMX_S7 = -19; + static constexpr int REG_BEAMY = -20; + static constexpr int REG_BEAMY_S0 = -20; + static constexpr int REG_BEAMY_S1 = -21; + static constexpr int REG_BEAMY_S2 = -22; + static constexpr int REG_BEAMY_S3 = -23; + static constexpr int REG_BEAMY_S4 = -24; + static constexpr int REG_BEAMY_S5 = -25; + static constexpr int REG_BEAMY_S6 = -26; + static constexpr int REG_BEAMY_S7 = -27; + static constexpr int REG_FRAME = -28; + static constexpr int REG_FRAME_S0 = -28; + static constexpr int REG_FRAME_S1 = -29; + static constexpr int REG_FRAME_S2 = -30; + static constexpr int REG_FRAME_S3 = -31; + static constexpr int REG_FRAME_S4 = -32; + static constexpr int REG_FRAME_S5 = -33; + static constexpr int REG_FRAME_S6 = -34; + static constexpr int REG_FRAME_S7 = -35; }; diff --git a/src/emu/inpttype.h b/src/emu/inpttype.h index cf7cef9150d..6cc9440c669 100644 --- a/src/emu/inpttype.h +++ b/src/emu/inpttype.h @@ -836,7 +836,8 @@ inline void construct_core_types_UI(simple_list &typelist) INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SHOW_FPS, "Show FPS", input_seq(KEYCODE_F11, input_seq::not_code, KEYCODE_LSHIFT) ) INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SNAPSHOT, "Save Snapshot", input_seq(KEYCODE_F12, input_seq::not_code, KEYCODE_LSHIFT) ) INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TIMECODE, "Write current timecode", input_seq(KEYCODE_F12, input_seq::not_code, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RECORD_MOVIE, "Record Movie", input_seq(KEYCODE_F12, KEYCODE_LSHIFT) ) + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RECORD_MNG, "Record MNG", input_seq(KEYCODE_F12, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_LCONTROL) ) + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RECORD_AVI, "Record AVI", input_seq(KEYCODE_F12, KEYCODE_LSHIFT, KEYCODE_LCONTROL) ) INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TOGGLE_CHEAT, "Toggle Cheat", input_seq(KEYCODE_F6) ) INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TOGGLE_AUTOFIRE, "Toggle Autofire", input_seq() ) INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_UP, "UI Up", input_seq(KEYCODE_UP, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(0)) ) diff --git a/src/emu/ioport.h b/src/emu/ioport.h index a9f25e29184..72ea16e81d5 100644 --- a/src/emu/ioport.h +++ b/src/emu/ioport.h @@ -333,7 +333,8 @@ enum ioport_type IPT_UI_SHOW_FPS, IPT_UI_SNAPSHOT, IPT_UI_TIMECODE, - IPT_UI_RECORD_MOVIE, + IPT_UI_RECORD_MNG, + IPT_UI_RECORD_AVI, IPT_UI_TOGGLE_CHEAT, IPT_UI_UP, IPT_UI_DOWN, diff --git a/src/emu/screen.cpp b/src/emu/screen.cpp index 849a353ddb2..feb21251f23 100644 --- a/src/emu/screen.cpp +++ b/src/emu/screen.cpp @@ -1368,7 +1368,7 @@ void screen_device::vblank_begin() m_vblank_end_time = m_vblank_start_time + attotime(0, m_vblank_period); // if this is the primary screen and we need to update now - if (this == machine().first_screen() && !(m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK)) + if (this == screen_device_iterator(machine().root_device()).first() && !(m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK)) machine().video().frame_update(); // call the screen specific callbacks @@ -1400,7 +1400,7 @@ void screen_device::vblank_end() m_screen_vblank(0); // if this is the primary screen and we need to update now - if (this == machine().first_screen() && (m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK)) + if (this == screen_device_iterator(machine().root_device()).first() && (m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK)) machine().video().frame_update(); // increment the frame number counter diff --git a/src/emu/screen.h b/src/emu/screen.h index a66a2f90f53..405951d9541 100644 --- a/src/emu/screen.h +++ b/src/emu/screen.h @@ -43,6 +43,8 @@ enum texture_format // screen_update callback flags constexpr u32 UPDATE_HAS_NOT_CHANGED = 0x0001; // the video has not changed +constexpr u32 MAX_NUM_SCREENS = 9; + /*! @defgroup flags for video_attributes @{ diff --git a/src/emu/video.cpp b/src/emu/video.cpp index dca8c938926..6cc6af39454 100644 --- a/src/emu/video.cpp +++ b/src/emu/video.cpp @@ -100,13 +100,6 @@ video_manager::video_manager(running_machine &machine) m_snap_native(true), m_snap_width(0), m_snap_height(0), - m_mng_frame_period(attotime::zero), - m_mng_next_frame_time(attotime::zero), - m_mng_frame(0), - m_avi_file(nullptr), - m_avi_frame_period(attotime::zero), - m_avi_next_frame_time(attotime::zero), - m_avi_frame(0), m_timecode_enabled(false), m_timecode_write(false), m_timecode_text(""), @@ -121,13 +114,15 @@ video_manager::video_manager(running_machine &machine) // extract initial execution state from global configuration settings update_refresh_speed(); + const bool no_screens = screen_device_iterator(machine.root_device()).count() == 0; + // create a render target for snapshots const char *viewname = machine.options().snap_view(); - m_snap_native = (machine.first_screen() != nullptr && (viewname[0] == 0 || strcmp(viewname, "native") == 0)); + m_snap_native = !no_screens && (viewname[0] == 0 || strcmp(viewname, "native") == 0); - // the native target is hard-coded to our internal layout and has all options disabled if (m_snap_native) { + // the native target is hard-coded to our internal layout and has all options disabled m_snap_target = machine.render().target_alloc(&layout_snap, RENDER_CREATE_SINGLE_FILE | RENDER_CREATE_HIDDEN); m_snap_target->set_backdrops_enabled(false); m_snap_target->set_overlays_enabled(false); @@ -137,10 +132,9 @@ video_manager::video_manager(running_machine &machine) m_snap_target->set_screen_overlay_enabled(false); m_snap_target->set_zoom_to_screen(false); } - - // other targets select the specified view and turn off effects 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); @@ -160,7 +154,7 @@ video_manager::video_manager(running_machine &machine) begin_recording(filename, MF_AVI); // if no screens, create a periodic timer to drive updates - if (machine.first_screen() == nullptr) + 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); @@ -247,7 +241,7 @@ void video_manager::frame_update(bool from_debugger) if (phase == machine_phase::RUNNING) { // reset partial updates if we're paused or if the debugger is active - screen_device *const screen = machine().first_screen(); + screen_device *const screen = screen_device_iterator(machine().root_device()).first(); bool const debugger_enabled = machine().debug_flags & DEBUG_FLAG_ENABLED; bool const within_instruction_hook = debugger_enabled && machine().debugger().within_instruction_hook(); if (screen && (machine().paused() || from_debugger || within_instruction_hook)) @@ -396,7 +390,138 @@ std::string &video_manager::timecode_total_text(std::string &str) return str; } +//------------------------------------------------- +// begin_recording_mng - begin recording a MNG +//------------------------------------------------- +void video_manager::begin_recording_mng(const char *name, uint32_t index, screen_device *screen) +{ + // stop any existing recording + end_recording_mng(index); + + mng_info_t &info = m_mngs[index]; + + // reset the state + info.m_mng_frame = 0; + info.m_mng_next_frame_time = machine().time(); + + // create a new movie file and start recording + info.m_mng_file = std::make_unique(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + osd_file::error filerr; + if (name != nullptr) + { + std::string full_name(name); + + if (index > 0) + { + char name_buf[256] = { 0 }; + snprintf(name_buf, 256, "%s%d", name, index); + full_name = name_buf; + } + + filerr = info.m_mng_file->open(full_name.c_str()); + } + else + { + filerr = open_next(*info.m_mng_file, "mng"); + } + + if (filerr == osd_file::error::NONE) + { + // start the capture + int rate = ATTOSECONDS_TO_HZ(screen->frame_period().attoseconds()); + png_error pngerr = mng_capture_start(*info.m_mng_file, m_snap_bitmap, rate); + if (pngerr != PNGERR_NONE) + { + osd_printf_error("Error capturing MNG, png_error=%d\n", pngerr); + return end_recording_mng(index); + } + + // compute the frame time + info.m_mng_frame_period = attotime::from_hz(rate); + } + else + { + osd_printf_error("Error creating MNG, osd_file::error=%d\n", int(filerr)); + info.m_mng_file.reset(); + } +} + +//------------------------------------------------- +// begin_recording_avi - begin recording an AVI +//------------------------------------------------- + +void video_manager::begin_recording_avi(const char *name, uint32_t index, screen_device *screen) +{ + // stop any existing recording + end_recording_avi(index); + + avi_info_t &avi_info = m_avis[index]; + + // reset the state + avi_info.m_avi_frame = 0; + avi_info.m_avi_next_frame_time = machine().time(); + + // build up information about this new movie + avi_file::movie_info info; + info.video_format = 0; + info.video_timescale = 1000 * ATTOSECONDS_TO_HZ(screen->frame_period().attoseconds()); + info.video_sampletime = 1000; + info.video_numsamples = 0; + info.video_width = m_snap_bitmap.width(); + info.video_height = m_snap_bitmap.height(); + info.video_depth = 24; + + info.audio_format = 0; + info.audio_timescale = machine().sample_rate(); + info.audio_sampletime = 1; + info.audio_numsamples = 0; + info.audio_channels = 2; + info.audio_samplebits = 16; + info.audio_samplerate = machine().sample_rate(); + + // create a new temporary movie file + osd_file::error filerr; + std::string fullpath; + { + emu_file tempfile(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (name != nullptr) + { + std::string full_name(name); + + if (index > 0) + { + char name_buf[256] = { 0 }; + snprintf(name_buf, 256, "%s%d", name, index); + full_name = name_buf; + } + + filerr = tempfile.open(full_name.c_str()); + } + else + { + filerr = open_next(tempfile, "avi"); + } + + // if we succeeded, make a copy of the name and create the real file over top + if (filerr == osd_file::error::NONE) + fullpath = tempfile.fullpath(); + } + + if (filerr == osd_file::error::NONE) + { + // compute the frame time + avi_info.m_avi_frame_period = attotime::from_seconds(1000) / info.video_timescale; + + // create the file and free the string + avi_file::error avierr = avi_file::create(fullpath, info, avi_info.m_avi_file); + if (avierr != avi_file::error::NONE) + { + osd_printf_error("Error creating AVI: %s\n", avi_file::error_string(avierr)); + return end_recording_avi(index); + } + } +} //------------------------------------------------- // begin_recording - begin recording of a movie @@ -405,141 +530,84 @@ std::string &video_manager::timecode_total_text(std::string &str) void video_manager::begin_recording(const char *name, movie_format format) { // create a snapshot bitmap so we know what the target size is - create_snapshot_bitmap(nullptr); + screen_device_iterator iterator = screen_device_iterator(machine().root_device()); + screen_device_iterator::auto_iterator iter = iterator.begin(); + const uint32_t count = (uint32_t)iterator.count(); - // start up an AVI recording - if (format == MF_AVI) + switch (format) { - // stop any existing recording - end_recording(format); - - // reset the state - m_avi_frame = 0; - m_avi_next_frame_time = machine().time(); - - // build up information about this new movie - screen_device *screen = machine().first_screen(); - avi_file::movie_info info; - info.video_format = 0; - info.video_timescale = 1000 * ((screen != nullptr) ? ATTOSECONDS_TO_HZ(screen->frame_period().attoseconds()) : screen_device::DEFAULT_FRAME_RATE); - info.video_sampletime = 1000; - info.video_numsamples = 0; - info.video_width = m_snap_bitmap.width(); - info.video_height = m_snap_bitmap.height(); - info.video_depth = 24; - - info.audio_format = 0; - info.audio_timescale = machine().sample_rate(); - info.audio_sampletime = 1; - info.audio_numsamples = 0; - info.audio_channels = 2; - info.audio_samplebits = 16; - info.audio_samplerate = machine().sample_rate(); - - // create a new temporary movie file - osd_file::error filerr; - std::string fullpath; - { - emu_file tempfile(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - if (name != nullptr) - filerr = tempfile.open(name); + case MF_AVI: + if (m_snap_native) + { + for (uint32_t index = 0; index < count; index++, iter++) + { + create_snapshot_bitmap(iter.current()); + begin_recording_avi(name, index, iter.current()); + } + } else - filerr = open_next(tempfile, "avi"); - - // if we succeeded, make a copy of the name and create the real file over top - if (filerr == osd_file::error::NONE) - fullpath = tempfile.fullpath(); - } - - if (filerr == osd_file::error::NONE) - { - // compute the frame time - m_avi_frame_period = attotime::from_seconds(1000) / info.video_timescale; - - // create the file and free the string - avi_file::error avierr = avi_file::create(fullpath, info, m_avi_file); - if (avierr != avi_file::error::NONE) { - osd_printf_error("Error creating AVI: %s\n", avi_file::error_string(avierr)); - return end_recording(format); + create_snapshot_bitmap(nullptr); + begin_recording_avi(name, 0, iter.current()); } - } - } + break; - // start up a MNG recording - else if (format == MF_MNG) + case MF_MNG: + if (m_snap_native) + { + for (uint32_t index = 0; index < count; index++, iter++) + { + create_snapshot_bitmap(iter.current()); + begin_recording_mng(name, index, iter.current()); + } + } + else + { + create_snapshot_bitmap(nullptr); + begin_recording_avi(name, 0, iter.current()); + } + break; + + default: + osd_printf_error("Unknown movie format: %d\n", format); + break; + } +} + + +//-------------------------------------------------- +// end_recording_avi - stop recording an AVI movie +//-------------------------------------------------- + +void video_manager::end_recording_avi(uint32_t index) +{ + avi_info_t &info = m_avis[index]; + if (info.m_avi_file) { - // stop any existing recording - end_recording(format); + info.m_avi_file.reset(); // reset the state - m_mng_frame = 0; - m_mng_next_frame_time = machine().time(); - - // create a new movie file and start recording - m_mng_file = std::make_unique(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr; - if (name != nullptr) - filerr = m_mng_file->open(name); - else - filerr = open_next(*m_mng_file, "mng"); - - if (filerr == osd_file::error::NONE) - { - // start the capture - screen_device *screen = machine().first_screen(); - int rate = (screen != nullptr) ? ATTOSECONDS_TO_HZ(screen->frame_period().attoseconds()) : screen_device::DEFAULT_FRAME_RATE; - png_error pngerr = mng_capture_start(*m_mng_file, m_snap_bitmap, rate); - if (pngerr != PNGERR_NONE) - { - osd_printf_error("Error capturing MNG, png_error=%d\n", pngerr); - return end_recording(format); - } - - // compute the frame time - m_mng_frame_period = attotime::from_hz(rate); - } - else - { - osd_printf_error("Error creating MNG, osd_file::error=%d\n", int(filerr)); - m_mng_file.reset(); - } + info.m_avi_frame = 0; } } +//-------------------------------------------------- +// end_recording_mng - stop recording a MNG movie +//-------------------------------------------------- -//------------------------------------------------- -// end_recording - stop recording of a movie -//------------------------------------------------- - -void video_manager::end_recording(movie_format format) +void video_manager::end_recording_mng(uint32_t index) { - if (format == MF_AVI) + mng_info_t &info = m_mngs[index]; + if (info.m_mng_file != nullptr) { - // close the file if it exists - if (m_avi_file) - { - m_avi_file.reset(); + mng_capture_stop(*info.m_mng_file); + info.m_mng_file.reset(); - // reset the state - m_avi_frame = 0; - } - } - else if (format == MF_MNG) - { - // close the file if it exists - if (m_mng_file != nullptr) - { - mng_capture_stop(*m_mng_file); - m_mng_file.reset(); - - // reset the state - m_mng_frame = 0; - } + // reset the state + info.m_mng_frame = 0; } } - //------------------------------------------------- // add_sound_to_recording - add sound to a movie // recording @@ -547,24 +615,44 @@ void video_manager::end_recording(movie_format format) void video_manager::add_sound_to_recording(const s16 *sound, int numsamples) { + if (m_snap_native) + { + const uint32_t count = (uint32_t)screen_device_iterator(machine().root_device()).count(); + for (uint32_t index = 0; index < count; index++) + { + add_sound_to_avi_recording(sound, numsamples, index); + } + } + else + { + add_sound_to_avi_recording(sound, numsamples, 0); + } +} + +//------------------------------------------------- +// add_sound_to_avi_recording - add sound to an +// AVI recording for a given screen +//------------------------------------------------- + +void video_manager::add_sound_to_avi_recording(const s16 *sound, int numsamples, uint32_t index) +{ + avi_info_t &info = m_avis[index]; // only record if we have a file - if (m_avi_file != nullptr) + if (info.m_avi_file != nullptr) { g_profiler.start(PROFILER_MOVIE_REC); // write the next frame - avi_file::error avierr = m_avi_file->append_sound_samples(0, sound + 0, numsamples, 1); + avi_file::error avierr = info.m_avi_file->append_sound_samples(0, sound + 0, numsamples, 1); if (avierr == avi_file::error::NONE) - avierr = m_avi_file->append_sound_samples(1, sound + 1, numsamples, 1); + avierr = info.m_avi_file->append_sound_samples(1, sound + 1, numsamples, 1); if (avierr != avi_file::error::NONE) - end_recording(MF_AVI); + end_recording_avi(index); g_profiler.stop(); } } - - //------------------------------------------------- // video_exit - close down the video system //------------------------------------------------- @@ -572,8 +660,20 @@ void video_manager::add_sound_to_recording(const s16 *sound, int numsamples) void video_manager::exit() { // stop recording any movie - end_recording(MF_AVI); - end_recording(MF_MNG); + + screen_device_iterator device_iterator = screen_device_iterator(machine().root_device()); + screen_device_iterator::auto_iterator iter = device_iterator.begin(); + const uint32_t count = (uint32_t)device_iterator.count(); + + for (uint32_t index = 0; index < count; index++, iter++) + { + end_recording_avi(index); + end_recording_mng(index); + if (!m_snap_native) + { + break; + } + } // free the snapshot target machine().render().target_free(m_snap_target); @@ -609,11 +709,48 @@ void video_manager::screenless_update_callback(void *ptr, int param) void video_manager::postload() { - m_avi_next_frame_time = machine().time(); - m_mng_next_frame_time = machine().time(); + screen_device_iterator device_iterator = screen_device_iterator(machine().root_device()); + screen_device_iterator::auto_iterator iter = device_iterator.begin(); + const uint32_t count = (uint32_t)device_iterator.count(); + + for (uint32_t index = 0; index < count; index++, iter++) + { + m_avis[index].m_avi_next_frame_time = machine().time(); + m_mngs[index].m_mng_next_frame_time = machine().time(); + if (!m_snap_native) + { + break; + } + } } +//------------------------------------------------- +// is_recording - returns whether or not any +// screen is currently recording +//------------------------------------------------- + +bool video_manager::is_recording() const +{ + screen_device_iterator device_iterator = screen_device_iterator(machine().root_device()); + screen_device_iterator::auto_iterator iter = device_iterator.begin(); + const uint32_t count = (uint32_t)device_iterator.count(); + + for (uint32_t index = 0; index < count; index++, iter++) + { + if (m_mngs[index].m_mng_file || m_avis[index].m_avi_file) + { + return true; + } + if (!m_snap_native) + { + break; + } + } + + return false; +} + //------------------------------------------------- // effective_autoframeskip - return the effective // autoframeskip value, accounting for fast @@ -1128,7 +1265,7 @@ void video_manager::create_snapshot_bitmap(screen_device *screen) // scheme //------------------------------------------------- -osd_file::error video_manager::open_next(emu_file &file, const char *extension) +osd_file::error video_manager::open_next(emu_file &file, const char *extension, uint32_t added_index) { u32 origflags = file.openflags(); @@ -1229,7 +1366,7 @@ osd_file::error video_manager::open_next(emu_file &file, const char *extension) else { // try until we succeed - file.set_openflags(OPEN_FLAG_READ); + file.set_openflags(OPEN_FLAG_WRITE); for (int seq = 0; ; seq++) { // build up the filename @@ -1238,8 +1375,10 @@ osd_file::error video_manager::open_next(emu_file &file, const char *extension) // try to open the file; stop when we fail osd_file::error filerr = file.open(fname.c_str()); - if (filerr != osd_file::error::NONE) + if (filerr == osd_file::error::NOT_FOUND) + { break; + } } } @@ -1256,68 +1395,88 @@ osd_file::error video_manager::open_next(emu_file &file, const char *extension) void video_manager::record_frame() { // ignore if nothing to do - if (m_mng_file == nullptr && m_avi_file == nullptr) + bool any_recording = false; + for (uint32_t index = 0; index < MAX_NUM_SCREENS && !any_recording; index++) + { + if (m_mngs[index].m_mng_file != nullptr || m_avis[index].m_avi_file != nullptr) + any_recording = true; + } + if (!any_recording) return; // start the profiler and get the current time g_profiler.start(PROFILER_MOVIE_REC); attotime curtime = machine().time(); - // create the bitmap - create_snapshot_bitmap(nullptr); + screen_device_iterator device_iterator = screen_device_iterator(machine().root_device()); + screen_device_iterator::auto_iterator iter = device_iterator.begin(); + const uint32_t count = (uint32_t)device_iterator.count(); - // handle an AVI recording - if (m_avi_file != nullptr) + for (uint32_t index = 0; index < count; index++, iter++) { - // loop until we hit the right time - while (m_avi_next_frame_time <= curtime) - { - // write the next frame - avi_file::error avierr = m_avi_file->append_video_frame(m_snap_bitmap); - if (avierr != avi_file::error::NONE) - { - g_profiler.stop(); - end_recording(MF_AVI); - break; - } + // create the bitmap + create_snapshot_bitmap(iter.current()); - // advance time - m_avi_next_frame_time += m_avi_frame_period; - m_avi_frame++; + // handle an AVI recording + avi_info_t &avi_info = m_avis[index]; + if (avi_info.m_avi_file != nullptr) + { + // 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(); + 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 (m_mng_file != nullptr) - { - // loop until we hit the right time - while (m_mng_next_frame_time <= curtime) + // handle a MNG recording + mng_info_t &mng_info = m_mngs[index]; + if (mng_info.m_mng_file != nullptr) { - // set up the text fields in the movie info - png_info pnginfo; - if (m_mng_frame == 0) + // loop until we hit the right time + while (mng_info.m_mng_next_frame_time <= curtime) { - 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()); - } + // 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 = machine().first_screen(); - 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(*m_mng_file, pnginfo, m_snap_bitmap, entries, palette); - if (error != PNGERR_NONE) - { - g_profiler.stop(); - end_recording(MF_MNG); - break; - } + // 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(); + end_recording_mng(index); + break; + } - // advance time - m_mng_next_frame_time += m_mng_frame_period; - m_mng_frame++; + // 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; } } @@ -1338,16 +1497,52 @@ void video_manager::toggle_throttle() // toggle_record_movie //------------------------------------------------- -void video_manager::toggle_record_movie() +void video_manager::toggle_record_movie(movie_format format) { if (!is_recording()) { - begin_recording(nullptr, MF_MNG); - machine().popmessage("REC START"); + begin_recording(nullptr, format); + machine().popmessage("REC START (%s)", format == MF_MNG ? "MNG" : "AVI"); } else { - end_recording(MF_MNG); - machine().popmessage("REC STOP"); + end_recording(format); + machine().popmessage("REC STOP (%s)", format == MF_MNG ? "MNG" : "AVI"); + } +} + +void video_manager::end_recording(movie_format format) +{ + screen_device_iterator device_iterator = screen_device_iterator(machine().root_device()); + screen_device_iterator::auto_iterator iter = device_iterator.begin(); + const uint32_t count = (uint32_t)device_iterator.count(); + + switch (format) + { + case MF_AVI: + for (uint32_t index = 0; index < count; index++, iter++) + { + end_recording_avi(index); + if (!m_snap_native) + { + break; + } + } + break; + + case MF_MNG: + for (uint32_t index = 0; index < count; index++, iter++) + { + end_recording_mng(index); + if (!m_snap_native) + { + break; + } + } + break; + + default: + osd_printf_error("Unknown movie format: %d\n", format); + break; } } diff --git a/src/emu/video.h b/src/emu/video.h index 7e19de3c5ab..ac8de0064cd 100644 --- a/src/emu/video.h +++ b/src/emu/video.h @@ -59,7 +59,7 @@ public: bool throttled() const { return m_throttled; } float throttle_rate() const { return m_throttle_rate; } bool fastforward() const { return m_fastforward; } - bool is_recording() const { return (m_mng_file || m_avi_file); } + bool is_recording() const; // setters void set_frameskip(int frameskip); @@ -70,8 +70,10 @@ public: // misc void toggle_throttle(); - void toggle_record_movie(); - osd_file::error open_next(emu_file &file, const char *extension); + void toggle_record_movie(movie_format format); + void toggle_record_mng() { toggle_record_movie(MF_MNG); } + void toggle_record_avi() { toggle_record_movie(MF_AVI); } + osd_file::error open_next(emu_file &file, const char *extension, uint32_t index = 0); // render a frame void frame_update(bool from_debugger = false); @@ -87,8 +89,13 @@ public: // movies void begin_recording(const char *name, movie_format format); + void begin_recording_mng(const char *name, uint32_t index, screen_device *screen); + void begin_recording_avi(const char *name, uint32_t index, screen_device *screen); void end_recording(movie_format format); + void end_recording_mng(uint32_t index); + void end_recording_avi(uint32_t index); void add_sound_to_recording(const s16 *sound, int numsamples); + void add_sound_to_avi_recording(const s16 *sound, int numsamples, uint32_t index); void set_timecode_enabled(bool value) { m_timecode_enabled = value; } bool get_timecode_enabled() { return m_timecode_enabled; } @@ -173,16 +180,37 @@ private: s32 m_snap_height; // height of snapshots (0 == auto) // movie recording - MNG - std::unique_ptr m_mng_file; // handle to the open movie file - attotime m_mng_frame_period; // period of a single movie frame - attotime m_mng_next_frame_time; // time of next frame - u32 m_mng_frame; // current movie frame number + class mng_info_t + { + public: + mng_info_t() + : m_mng_frame_period(attotime::zero) + , m_mng_next_frame_time(attotime::zero) + , m_mng_frame(0) { } + + std::unique_ptr m_mng_file; // handle to the open movie file + attotime m_mng_frame_period; // period of a single movie frame + attotime m_mng_next_frame_time; // time of next frame + u32 m_mng_frame; // current movie frame number + }; + mng_info_t m_mngs[9]; // movie recording - AVI - avi_file::ptr m_avi_file; // handle to the open movie file - attotime m_avi_frame_period; // period of a single movie frame - attotime m_avi_next_frame_time; // time of next frame - u32 m_avi_frame; // current movie frame number + class avi_info_t + { + public: + avi_info_t() + : m_avi_file(nullptr) + , m_avi_frame_period(attotime::zero) + , m_avi_next_frame_time(attotime::zero) + , m_avi_frame(0) { } + + avi_file::ptr m_avi_file; // handle to the open movie file + attotime m_avi_frame_period; // period of a single movie frame + attotime m_avi_next_frame_time; // time of next frame + u32 m_avi_frame; // current movie frame number + }; + avi_info_t m_avis[9]; static const bool s_skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS]; diff --git a/src/frontend/mame/ui/cheatopt.cpp b/src/frontend/mame/ui/cheatopt.cpp index 0e911aa0972..3982a7c7a33 100644 --- a/src/frontend/mame/ui/cheatopt.cpp +++ b/src/frontend/mame/ui/cheatopt.cpp @@ -176,7 +176,7 @@ menu_cheat::~menu_cheat() menu_autofire::menu_autofire(mame_ui_manager &mui, render_container &container) : menu(mui, container), last_toggle(false) { - const screen_device *screen = mui.machine().first_screen(); + const screen_device *screen = screen_device_iterator(mui.machine().root_device()).first(); if (screen == nullptr) { diff --git a/src/frontend/mame/ui/ui.cpp b/src/frontend/mame/ui/ui.cpp index df5089cb1c0..9fe360d3891 100644 --- a/src/frontend/mame/ui/ui.cpp +++ b/src/frontend/mame/ui/ui.cpp @@ -1213,9 +1213,13 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container) if (machine().ui_input().pressed(IPT_UI_TOGGLE_CHEAT)) mame_machine_manager::instance()->cheat().set_enable(!mame_machine_manager::instance()->cheat().enabled()); - // toggle movie recording - if (machine().ui_input().pressed(IPT_UI_RECORD_MOVIE)) - machine().video().toggle_record_movie(); + // toggle MNG recording + if (machine().ui_input().pressed(IPT_UI_RECORD_MNG)) + machine().video().toggle_record_mng(); + + // toggle MNG recording + if (machine().ui_input().pressed(IPT_UI_RECORD_AVI)) + machine().video().toggle_record_avi(); // toggle profiler display if (machine().ui_input().pressed(IPT_UI_SHOW_PROFILER)) @@ -1676,9 +1680,10 @@ int32_t mame_ui_manager::slider_refresh(running_machine &machine, void *arg, int const rectangle &visarea = screen->visible_area(); screen->configure(width, height, visarea, HZ_TO_ATTOSECONDS(defrefresh + (double)newval * 0.001)); } + if (str) - *str = string_format(_("%1$.3ffps"), ATTOSECONDS_TO_HZ(machine.first_screen()->frame_period().attoseconds())); - refresh = ATTOSECONDS_TO_HZ(machine.first_screen()->frame_period().attoseconds()); + *str = string_format(_("%1$.3ffps"), ATTOSECONDS_TO_HZ(screen->frame_period().attoseconds())); + refresh = ATTOSECONDS_TO_HZ(screen->frame_period().attoseconds()); return floor((refresh - defrefresh) * 1000.0 + 0.5); } diff --git a/src/frontend/mame/ui/viewgfx.cpp b/src/frontend/mame/ui/viewgfx.cpp index 560dc30d0f9..0625caa1ada 100644 --- a/src/frontend/mame/ui/viewgfx.cpp +++ b/src/frontend/mame/ui/viewgfx.cpp @@ -1320,7 +1320,11 @@ static void tilemap_update_bitmap(running_machine &machine, ui_gfx_state &state, { state.bitmap->fill(0); tilemap_t *tilemap = machine.tilemap().find(state.tilemap.which); - tilemap->draw_debug(*machine.first_screen(), *state.bitmap, state.tilemap.xoffs, state.tilemap.yoffs, state.tilemap.flags); + screen_device *first_screen = screen_device_iterator(machine.root_device()).first(); + if (first_screen) + { + tilemap->draw_debug(*first_screen, *state.bitmap, state.tilemap.xoffs, state.tilemap.yoffs, state.tilemap.flags); + } // reset the texture to force an update state.texture->set_bitmap(*state.bitmap, state.bitmap->cliprect(), TEXFORMAT_RGB32); diff --git a/src/osd/modules/input/input_windows.cpp b/src/osd/modules/input/input_windows.cpp index 91c546baf4a..164ee0e3c31 100644 --- a/src/osd/modules/input/input_windows.cpp +++ b/src/osd/modules/input/input_windows.cpp @@ -137,8 +137,13 @@ void windows_osd_interface::customize_input_type_list(simple_list