diff options
38 files changed, 2467 insertions, 2650 deletions
diff --git a/.gitattributes b/.gitattributes index 82f78cf2d51..a8193165b36 100644 --- a/.gitattributes +++ b/.gitattributes @@ -845,6 +845,8 @@ src/emu/romload.c svneol=native#text/plain src/emu/romload.h svneol=native#text/plain src/emu/schedule.c svneol=native#text/plain src/emu/schedule.h svneol=native#text/plain +src/emu/screen.c svneol=native#text/plain +src/emu/screen.h svneol=native#text/plain src/emu/softlist.c svneol=native#text/plain src/emu/softlist.h svneol=native#text/plain src/emu/sound.c svneol=native#text/plain diff --git a/src/emu/debug/debugcmd.c b/src/emu/debug/debugcmd.c index 75cf528cd18..2e267930769 100644 --- a/src/emu/debug/debugcmd.c +++ b/src/emu/debug/debugcmd.c @@ -2418,7 +2418,7 @@ static void execute_snap(running_machine *machine, int ref, int params, const ch /* if no params, use the default behavior */ if (params == 0) { - video_save_active_screen_snapshots(machine); + machine->video().save_active_screen_snapshots(); debug_console_printf(machine, "Saved snapshot\n"); } @@ -2449,7 +2449,7 @@ static void execute_snap(running_machine *machine, int ref, int params, const ch return; } - screen_save_snapshot(screen->machine, screen, fp); + screen->machine->video().save_snapshot(screen, *fp); mame_fclose(fp); debug_console_printf(machine, "Saved screen #%d snapshot as '%s'\n", scrnum, filename); } diff --git a/src/emu/debug/debugcpu.c b/src/emu/debug/debugcpu.c index 83cd78db618..2729703e2a4 100644 --- a/src/emu/debug/debugcpu.c +++ b/src/emu/debug/debugcpu.c @@ -142,7 +142,7 @@ static UINT64 get_frame(symbol_table &table, void *ref); void debug_cpu_init(running_machine *machine) { - screen_device *first_screen = screen_first(*machine); + screen_device *first_screen = machine->first_screen(); debugcpu_private *global; int regnum; diff --git a/src/emu/debugger.c b/src/emu/debugger.c index c1ff23863aa..519b08dcd1d 100644 --- a/src/emu/debugger.c +++ b/src/emu/debugger.c @@ -104,7 +104,7 @@ void debugger_init(running_machine *machine) void debugger_refresh_display(running_machine *machine) { - video_frame_update(machine, TRUE); + machine->video().frame_update(true); } diff --git a/src/emu/diexec.c b/src/emu/diexec.c index b1d0706ecd1..d3a32d62493 100644 --- a/src/emu/diexec.c +++ b/src/emu/diexec.c @@ -219,7 +219,7 @@ bool device_config_execute_interface::interface_validity_check(const game_driver /* validate the interrupts */ if (m_vblank_interrupt != NULL) { - if (screen_count(m_machine_config) == 0) + if (m_machine_config.m_devicelist.count(SCREEN) == 0) { mame_printf_error("%s: %s device '%s' has a VBLANK interrupt, but the driver is screenless!\n", driver.source_file, driver.name, devconfig->tag()); error = true; @@ -629,7 +629,7 @@ void device_execute_interface::interface_post_reset() // old style 'hack' setup - use screen #0 else - screen = screen_first(m_machine); + screen = m_machine.first_screen(); assert(screen != NULL); screen->register_vblank_callback(static_on_vblank, NULL); diff --git a/src/emu/emu.h b/src/emu/emu.h index 2a7fb322947..aa94a8a70b4 100644 --- a/src/emu/emu.h +++ b/src/emu/emu.h @@ -121,6 +121,7 @@ typedef device_config * (*machine_config_constructor)(machine_config &config, de #include "drawgfx.h" #include "tilemap.h" #include "emupal.h" +#include "screen.h" #include "video.h" // sound-related diff --git a/src/emu/emu.mak b/src/emu/emu.mak index 6e48b248a00..e540cd699d6 100644 --- a/src/emu/emu.mak +++ b/src/emu/emu.mak @@ -89,6 +89,7 @@ EMUOBJS = \ $(EMUOBJ)/rendutil.o \ $(EMUOBJ)/romload.o \ $(EMUOBJ)/schedule.o \ + $(EMUOBJ)/screen.o \ $(EMUOBJ)/softlist.o \ $(EMUOBJ)/sound.o \ $(EMUOBJ)/state.o \ diff --git a/src/emu/emupal.c b/src/emu/emupal.c index 5e462e19c84..2950b7de3e8 100644 --- a/src/emu/emupal.c +++ b/src/emu/emupal.c @@ -97,7 +97,7 @@ static void configure_rgb_shadows(running_machine *machine, int mode, float fact void palette_init(running_machine *machine) { palette_private *palette = auto_alloc_clear(machine, palette_private); - screen_device *device = screen_first(*machine); + screen_device *device = machine->first_screen(); /* get the format from the first screen, or use BITMAP_FORMAT_INVALID, if screenless */ bitmap_format format = (device != NULL) ? device->format() : BITMAP_FORMAT_INVALID; diff --git a/src/emu/info.c b/src/emu/info.c index 0e150bc7951..6bc88e15260 100644 --- a/src/emu/info.c +++ b/src/emu/info.c @@ -726,7 +726,7 @@ static void print_game_display(FILE *out, const game_driver *game, const machine const screen_device_config *devconfig; /* iterate over screens */ - for (devconfig = screen_first(config); devconfig != NULL; devconfig = screen_next(devconfig)) + for (devconfig = config.first_screen(); devconfig != NULL; devconfig = devconfig->next_screen()) { fprintf(out, "\t\t<display"); diff --git a/src/emu/inptport.c b/src/emu/inptport.c index 6bba1595049..260c2412fd3 100644 --- a/src/emu/inptport.c +++ b/src/emu/inptport.c @@ -4667,7 +4667,7 @@ static void record_frame(running_machine *machine, attotime curtime) record_write_uint64(machine, curtime.attoseconds); /* then the current speed */ - record_write_uint32(machine, video_get_speed_percent(machine) * (double)(1 << 20)); + record_write_uint32(machine, machine->video().speed_percent() * (double)(1 << 20)); } } diff --git a/src/emu/machine.c b/src/emu/machine.c index 349c7241998..92c0c30aab3 100644 --- a/src/emu/machine.c +++ b/src/emu/machine.c @@ -192,6 +192,7 @@ running_machine::running_machine(const machine_config &_config, osd_interface &o m_driver_device(NULL), m_cheat(NULL), m_render(NULL), + m_video(NULL), m_debug_view(NULL) { memset(gfx, 0, sizeof(gfx)); @@ -210,7 +211,7 @@ running_machine::running_machine(const machine_config &_config, osd_interface &o assert(m_driver_device != NULL); // find devices - primary_screen = screen_first(*this); + primary_screen = downcast<screen_device *>(m_devicelist.first(SCREEN)); for (device_t *device = m_devicelist.first(); device != NULL; device = device->next()) if (dynamic_cast<cpu_device *>(device) != NULL) { @@ -230,7 +231,7 @@ running_machine::running_machine(const machine_config &_config, osd_interface &o running_machine::~running_machine() { - /* clear flag for added devices */ + // clear flag for added devices options_set_bool(&m_options, OPTION_ADDED_DEVICE_OPTIONS, FALSE, OPTION_PRIORITY_CMDLINE); } @@ -285,6 +286,9 @@ void running_machine::start() // init the osd layer m_osd.init(*this); + + // create the video manager + m_video = auto_alloc(this, video_manager(*this)); ui_init(this); // initialize the base time (needed for doing record/playback) @@ -320,7 +324,6 @@ void running_machine::start() tilemap_init(this); crosshair_init(this); sound_init(this); - video_init(this); // initialize the debugger if ((debug_flags & DEBUG_FLAG_ENABLED) != 0) @@ -399,7 +402,7 @@ int running_machine::run(bool firstrun) // otherwise, just pump video updates through else - video_frame_update(this, false); + m_video->frame_update(); // handle save/load if (m_saveload_schedule != SLS_NONE) diff --git a/src/emu/machine.h b/src/emu/machine.h index a9fd8b815e3..39ca122c5f7 100644 --- a/src/emu/machine.h +++ b/src/emu/machine.h @@ -182,6 +182,7 @@ class gfx_element; class colortable_t; class cheat_manager; class render_manager; +class video_manager; class debug_view_manager; class osd_interface; @@ -362,6 +363,7 @@ public: const char *new_driver_name() const { return m_new_driver_pending->name; } device_scheduler &scheduler() { return m_scheduler; } osd_interface &osd() const { return m_osd; } + screen_device *first_screen() const { return primary_screen; } // immediate operations int run(bool firstrun); @@ -390,6 +392,7 @@ public: // managers cheat_manager &cheat() const { assert(m_cheat != NULL); return *m_cheat; } render_manager &render() const { assert(m_render != NULL); return *m_render; } + video_manager &video() const { assert(m_video != NULL); return *m_video; } debug_view_manager &debug_view() const { assert(m_debug_view != NULL); return *m_debug_view; } // misc @@ -530,6 +533,7 @@ private: driver_device * m_driver_device; cheat_manager * m_cheat; // internal data from cheat.c render_manager * m_render; // internal data from render.c + video_manager * m_video; // internal data from video.c debug_view_manager * m_debug_view; // internal data from debugvw.c }; diff --git a/src/emu/mame.c b/src/emu/mame.c index a1089342e5c..0ed9b964353 100644 --- a/src/emu/mame.c +++ b/src/emu/mame.c @@ -529,7 +529,7 @@ void mame_parse_ini_files(core_options *options, const game_driver *driver) /* parse "vector.ini" for vector games */ { machine_config config(*driver); - for (const screen_device_config *devconfig = screen_first(config); devconfig != NULL; devconfig = screen_next(devconfig)) + for (const screen_device_config *devconfig = config.first_screen(); devconfig != NULL; devconfig = devconfig->next_screen()) if (devconfig->screen_type() == SCREEN_TYPE_VECTOR) { parse_ini_file(options, "vector", OPTION_PRIORITY_VECTOR_INI); diff --git a/src/emu/mconfig.c b/src/emu/mconfig.c index ccf153ac680..a218f7e630b 100644 --- a/src/emu/mconfig.c +++ b/src/emu/mconfig.c @@ -101,6 +101,17 @@ machine_config::~machine_config() //------------------------------------------------- +// first_screen - return a pointer to the first +// screen device +//------------------------------------------------- + +screen_device_config *machine_config::first_screen() const +{ + return downcast<screen_device_config *>(m_devicelist.first(SCREEN)); +} + + +//------------------------------------------------- // device_add - configuration helper to add a // new device //------------------------------------------------- diff --git a/src/emu/mconfig.h b/src/emu/mconfig.h index 3646b2c4995..2bcdc4fcd44 100644 --- a/src/emu/mconfig.h +++ b/src/emu/mconfig.h @@ -105,6 +105,7 @@ // forward references struct gfx_decode_entry; class driver_device; +class screen_device_config; @@ -124,11 +125,15 @@ class machine_config friend class running_machine; public: + // construction/destruction machine_config(const game_driver &gamedrv); ~machine_config(); + // getters const game_driver &gamedrv() const { return m_gamedrv; } + screen_device_config *first_screen() const; + // public state attotime m_minimum_quantum; // minimum scheduling quantum const char * m_perfect_cpu_quantum; // tag of CPU to use for "perfect" scheduling INT32 m_watchdog_vblank_count; // number of VBLANKs until the watchdog kills us diff --git a/src/emu/render.c b/src/emu/render.c index 91da225c469..9cedebe102c 100644 --- a/src/emu/render.c +++ b/src/emu/render.c @@ -1152,6 +1152,78 @@ void render_target::set_max_texture_size(int maxwidth, int maxheight) //------------------------------------------------- +// configured_view - select a view for this +// target based on the configuration parameters +//------------------------------------------------- + +int render_target::configured_view(const char *viewname, int targetindex, int numtargets) +{ + layout_view *view = NULL; + int viewindex; + + // auto view just selects the nth view + if (strcmp(viewname, "auto") != 0) + { + // scan for a matching view name + for (view = view_by_index(viewindex = 0); view != NULL; view = view_by_index(++viewindex)) + if (mame_strnicmp(view->name(), viewname, strlen(viewname)) == 0) + break; + } + + // if we don't have a match, default to the nth view + int scrcount = m_manager.machine().m_devicelist.count(SCREEN); + if (view == NULL && scrcount > 0) + { + // if we have enough targets to be one per screen, assign in order + if (numtargets >= scrcount) + { + int ourindex = index() % scrcount; + screen_device *screen; + for (screen = m_manager.machine().first_screen(); screen != NULL; screen = screen->next_screen()) + if (ourindex-- == 0) + break; + + // find the first view with this screen and this screen only + for (view = view_by_index(viewindex = 0); view != NULL; view = view_by_index(++viewindex)) + { + const render_screen_list &viewscreens = view->screens(); + if (viewscreens.count() == 1 && viewscreens.contains(*screen)) + break; + if (viewscreens.count() == 0) + { + view = NULL; + break; + } + } + } + + // otherwise, find the first view that has all the screens + if (view == NULL) + { + for (view = view_by_index(viewindex = 0); view != NULL; view = view_by_index(++viewindex)) + { + const render_screen_list &viewscreens = view->screens(); + if (viewscreens.count() == 0) + break; + if (viewscreens.count() >= scrcount) + { + screen_device *screen; + for (screen = m_manager.machine().first_screen(); screen != NULL; screen = screen->next_screen()) + if (!viewscreens.contains(*screen)) + break; + if (screen == NULL) + break; + } + } + } + } + + // make sure it's a valid view + return (view != NULL) ? view_index(*view) : 0; +} + + +//------------------------------------------------- // view_name - return the name of the given view //------------------------------------------------- @@ -1548,7 +1620,7 @@ void render_target::load_layout_files(const char *layoutfile, bool singlefile) load_layout_file(cloneof->name, "default"); // now do the built-in layouts for single-screen games - if (screen_count(m_manager.machine().m_config) == 1) + if (m_manager.machine().m_devicelist.count(SCREEN) == 1) { if (gamedrv->flags & ORIENTATION_SWAP_XY) load_layout_file(NULL, layout_vertical); @@ -2366,7 +2438,7 @@ render_manager::render_manager(running_machine &machine) config_register(&machine, "video", config_load_static, config_save_static); // create one container per screen - for (screen_device *screen = screen_first(machine); screen != NULL; screen = screen_next(screen)) + for (screen_device *screen = machine.first_screen(); screen != NULL; screen = screen->next_screen()) screen->set_container(*container_alloc(screen)); } diff --git a/src/emu/render.h b/src/emu/render.h index 56d77928f48..398acd9c678 100644 --- a/src/emu/render.h +++ b/src/emu/render.h @@ -627,6 +627,9 @@ public: void set_screen_overlay_enabled(bool enable) { m_layerconfig.set_screen_overlay_enabled(enable); update_layer_config(); } void set_zoom_to_screen(bool zoom) { m_layerconfig.set_zoom_to_screen(zoom); update_layer_config(); } + // view configuration helper + int configured_view(const char *viewname, int targetindex, int numtargets); + // view information const char *view_name(int viewindex); const render_screen_list &view_screens(int viewindex); diff --git a/src/emu/rendlay.c b/src/emu/rendlay.c index 30c52fd05c2..3bfca450982 100644 --- a/src/emu/rendlay.c +++ b/src/emu/rendlay.c @@ -193,7 +193,7 @@ static int get_variable_value(running_machine &machine, const char *string, char char temp[100]; // screen 0 parameters - for (const screen_device_config *devconfig = screen_first(machine.m_config); devconfig != NULL; devconfig = screen_next(devconfig)) + for (const screen_device_config *devconfig = machine.m_config.first_screen(); devconfig != NULL; devconfig = devconfig->next_screen()) { int scrnum = machine.m_config.m_devicelist.index(SCREEN, devconfig->tag()); diff --git a/src/emu/screen.c b/src/emu/screen.c new file mode 100644 index 00000000000..6ff3088f95e --- /dev/null +++ b/src/emu/screen.c @@ -0,0 +1,1076 @@ +/*************************************************************************** + + screen.c + + Core MAME screen device. + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#include "emu.h" +#include "emuopts.h" +#include "png.h" +#include "rendutil.h" + + + +//************************************************************************** +// DEBUGGING +//************************************************************************** + +#define VERBOSE (0) +#define LOG_PARTIAL_UPDATES(x) do { if (VERBOSE) logerror x; } while (0) + + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type SCREEN = screen_device_config::static_alloc_device_config; + +const attotime screen_device::DEFAULT_FRAME_PERIOD = STATIC_ATTOTIME_IN_HZ(DEFAULT_FRAME_RATE); + + + +//************************************************************************** +// SCREEN DEVICE CONFIGURATION +//************************************************************************** + +//------------------------------------------------- +// screen_device_config - constructor +//------------------------------------------------- + +screen_device_config::screen_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock) + : device_config(mconfig, static_alloc_device_config, "Video Screen", tag, owner, clock), + m_type(SCREEN_TYPE_RASTER), + m_width(0), + m_height(0), + m_oldstyle_vblank_supplied(false), + m_refresh(0), + m_vblank(0), + m_format(BITMAP_FORMAT_INVALID), + m_xoffset(0.0f), + m_yoffset(0.0f), + m_xscale(1.0f), + m_yscale(1.0f) +{ +} + + +//------------------------------------------------- +// static_alloc_device_config - allocate a new +// configuration object +//------------------------------------------------- + +device_config *screen_device_config::static_alloc_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock) +{ + return global_alloc(screen_device_config(mconfig, tag, owner, clock)); +} + + +//------------------------------------------------- +// alloc_device - allocate a new device object +//------------------------------------------------- + +device_t *screen_device_config::alloc_device(running_machine &machine) const +{ + return auto_alloc(&machine, screen_device(machine, *this)); +} + + +//------------------------------------------------- +// static_set_format - configuration helper +// to set the bitmap format +//------------------------------------------------- + +void screen_device_config::static_set_format(device_config *device, bitmap_format format) +{ + screen_device_config *screen = downcast<screen_device_config *>(device); + screen->m_format = format; +} + + +//------------------------------------------------- +// static_set_type - configuration helper +// to set the screen type +//------------------------------------------------- + +void screen_device_config::static_set_type(device_config *device, screen_type_enum type) +{ + screen_device_config *screen = downcast<screen_device_config *>(device); + screen->m_type = type; +} + + +//------------------------------------------------- +// static_set_raw - configuration helper +// to set the raw screen parameters +//------------------------------------------------- + +void screen_device_config::static_set_raw(device_config *device, UINT32 pixclock, UINT16 htotal, UINT16 hbend, UINT16 hbstart, UINT16 vtotal, UINT16 vbend, UINT16 vbstart) +{ + screen_device_config *screen = downcast<screen_device_config *>(device); + screen->m_refresh = HZ_TO_ATTOSECONDS(pixclock) * htotal * vtotal; + screen->m_vblank = screen->m_refresh / vtotal * (vtotal - (vbstart - vbend)); + screen->m_width = htotal; + screen->m_height = vtotal; + screen->m_visarea.min_x = hbend; + screen->m_visarea.max_x = hbstart - 1; + screen->m_visarea.min_y = vbend; + screen->m_visarea.max_y = vbstart - 1; +} + + +//------------------------------------------------- +// static_set_refresh - configuration helper +// to set the refresh rate +//------------------------------------------------- + +void screen_device_config::static_set_refresh(device_config *device, attoseconds_t rate) +{ + screen_device_config *screen = downcast<screen_device_config *>(device); + screen->m_refresh = rate; +} + + +//------------------------------------------------- +// static_set_vblank_time - configuration helper +// to set the VBLANK duration +//------------------------------------------------- + +void screen_device_config::static_set_vblank_time(device_config *device, attoseconds_t time) +{ + screen_device_config *screen = downcast<screen_device_config *>(device); + screen->m_vblank = time; + screen->m_oldstyle_vblank_supplied = true; +} + + +//------------------------------------------------- +// static_set_size - configuration helper to set +// the width/height of the screen +//------------------------------------------------- + +void screen_device_config::static_set_size(device_config *device, UINT16 width, UINT16 height) +{ + screen_device_config *screen = downcast<screen_device_config *>(device); + screen->m_width = width; + screen->m_height = height; +} + + +//------------------------------------------------- +// static_set_visarea - configuration helper to +// set the visible area of the screen +//------------------------------------------------- + +void screen_device_config::static_set_visarea(device_config *device, INT16 minx, INT16 maxx, INT16 miny, INT16 maxy) +{ + screen_device_config *screen = downcast<screen_device_config *>(device); + screen->m_visarea.min_x = minx; + screen->m_visarea.max_x = maxx; + screen->m_visarea.min_y = miny; + screen->m_visarea.max_y = maxy; +} + + +//------------------------------------------------- +// static_set_default_position - configuration +// helper to set the default position and scale +// factors for the screen +//------------------------------------------------- + +void screen_device_config::static_set_default_position(device_config *device, double xscale, double xoffs, double yscale, double yoffs) +{ + screen_device_config *screen = downcast<screen_device_config *>(device); + screen->m_xscale = xscale; + screen->m_xoffset = xoffs; + screen->m_yscale = yscale; + screen->m_yoffset = yoffs; +} + + +//------------------------------------------------- +// device_validity_check - verify device +// configuration +//------------------------------------------------- + +bool screen_device_config::device_validity_check(const game_driver &driver) const +{ + bool error = false; + + // sanity check dimensions + if (m_width <= 0 || m_height <= 0) + { + mame_printf_error("%s: %s screen '%s' has invalid display dimensions\n", driver.source_file, driver.name, tag()); + error = true; + } + + // sanity check display area + if (m_type != SCREEN_TYPE_VECTOR) + { + if ((m_visarea.max_x < m_visarea.min_x) || + (m_visarea.max_y < m_visarea.min_y) || + (m_visarea.max_x >= m_width) || + (m_visarea.max_y >= m_height)) + { + mame_printf_error("%s: %s screen '%s' has an invalid display area\n", driver.source_file, driver.name, tag()); + error = true; + } + + // sanity check screen formats + if (m_format != BITMAP_FORMAT_INDEXED16 && + m_format != BITMAP_FORMAT_RGB15 && + m_format != BITMAP_FORMAT_RGB32) + { + mame_printf_error("%s: %s screen '%s' has unsupported format\n", driver.source_file, driver.name, tag()); + error = true; + } + } + + // check for zero frame rate + if (m_refresh == 0) + { + mame_printf_error("%s: %s screen '%s' has a zero refresh rate\n", driver.source_file, driver.name, tag()); + error = true; + } + return error; +} + + + +//************************************************************************** +// SCREEN DEVICE +//************************************************************************** + +//------------------------------------------------- +// screen_device - constructor +//------------------------------------------------- + +screen_device::screen_device(running_machine &_machine, const screen_device_config &config) + : device_t(_machine, config), + m_config(config), + m_container(NULL), + m_width(m_config.m_width), + m_height(m_config.m_height), + m_visarea(m_config.m_visarea), + m_burnin(NULL), + m_curbitmap(0), + m_curtexture(0), + m_texture_format(0), + m_changed(true), + m_last_partial_scan(0), + m_screen_overlay_bitmap(NULL), + m_frame_period(m_config.m_refresh), + m_scantime(1), + m_pixeltime(1), + m_vblank_period(0), + m_vblank_start_time(attotime_zero), + m_vblank_end_time(attotime_zero), + m_vblank_begin_timer(NULL), + m_vblank_end_timer(NULL), + m_scanline0_timer(NULL), + m_scanline_timer(NULL), + m_frame_number(0), + m_partial_updates_this_frame(0), + m_callback_list(NULL) +{ + memset(m_texture, 0, sizeof(m_texture)); + memset(m_bitmap, 0, sizeof(m_bitmap)); +} + + +//------------------------------------------------- +// ~screen_device - destructor +//------------------------------------------------- + +screen_device::~screen_device() +{ + m_machine.render().texture_free(m_texture[0]); + m_machine.render().texture_free(m_texture[1]); + if (m_burnin != NULL) + finalize_burnin(); + global_free(m_screen_overlay_bitmap); +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void screen_device::device_start() +{ + // configure the default cliparea + render_container::user_settings settings; + m_container->get_user_settings(settings); + settings.m_xoffset = m_config.m_xoffset; + settings.m_yoffset = m_config.m_yoffset; + settings.m_xscale = m_config.m_xscale; + settings.m_yscale = m_config.m_yscale; + m_container->set_user_settings(settings); + + // allocate the VBLANK timers + m_vblank_begin_timer = timer_alloc(machine, static_vblank_begin_callback, (void *)this); + m_vblank_end_timer = timer_alloc(machine, static_vblank_end_callback, (void *)this); + + // allocate a timer to reset partial updates + m_scanline0_timer = timer_alloc(machine, static_scanline0_callback, (void *)this); + + // allocate a timer to generate per-scanline updates + if ((machine->config->m_video_attributes & VIDEO_UPDATE_SCANLINE) != 0) + m_scanline_timer = timer_alloc(machine, static_scanline_update_callback, (void *)this); + + // configure the screen with the default parameters + configure(m_config.m_width, m_config.m_height, m_config.m_visarea, m_config.m_refresh); + + // reset VBLANK timing + m_vblank_start_time = attotime_zero; + m_vblank_end_time = attotime_make(0, m_vblank_period); + + // start the timer to generate per-scanline updates + if ((machine->config->m_video_attributes & VIDEO_UPDATE_SCANLINE) != 0) + timer_adjust_oneshot(m_scanline_timer, time_until_pos(0), 0); + + // create burn-in bitmap + if (options_get_int(machine->options(), OPTION_BURNIN) > 0) + { + int width, height; + if (sscanf(options_get_string(machine->options(), OPTION_SNAPSIZE), "%dx%d", &width, &height) != 2 || width == 0 || height == 0) + width = height = 300; + m_burnin = auto_alloc(machine, bitmap_t(width, height, BITMAP_FORMAT_INDEXED64)); + if (m_burnin == NULL) + fatalerror("Error allocating burn-in bitmap for screen at (%dx%d)\n", width, height); + bitmap_fill(m_burnin, NULL, 0); + } + + // load the effect overlay + const char *overname = options_get_string(machine->options(), OPTION_EFFECT); + if (overname != NULL && strcmp(overname, "none") != 0) + load_effect_overlay(overname); + + state_save_register_device_item(this, 0, m_width); + state_save_register_device_item(this, 0, m_height); + state_save_register_device_item(this, 0, m_visarea.min_x); + state_save_register_device_item(this, 0, m_visarea.min_y); + state_save_register_device_item(this, 0, m_visarea.max_x); + state_save_register_device_item(this, 0, m_visarea.max_y); + state_save_register_device_item(this, 0, m_last_partial_scan); + state_save_register_device_item(this, 0, m_frame_period); + state_save_register_device_item(this, 0, m_scantime); + state_save_register_device_item(this, 0, m_pixeltime); + state_save_register_device_item(this, 0, m_vblank_period); + state_save_register_device_item(this, 0, m_vblank_start_time.seconds); + state_save_register_device_item(this, 0, m_vblank_start_time.attoseconds); + state_save_register_device_item(this, 0, m_vblank_end_time.seconds); + state_save_register_device_item(this, 0, m_vblank_end_time.attoseconds); + state_save_register_device_item(this, 0, m_frame_number); +} + + +//------------------------------------------------- +// device_post_load - device-specific update +// after a save state is loaded +//------------------------------------------------- + +void screen_device::device_post_load() +{ + realloc_screen_bitmaps(); +} + + +//------------------------------------------------- +// configure - configure screen parameters +//------------------------------------------------- + +void screen_device::configure(int width, int height, const rectangle &visarea, attoseconds_t frame_period) +{ + // validate arguments + assert(width > 0); + assert(height > 0); + assert(visarea.min_x >= 0); + assert(visarea.min_y >= 0); + assert(m_config.m_type == SCREEN_TYPE_VECTOR || visarea.min_x < width); + assert(m_config.m_type == SCREEN_TYPE_VECTOR || visarea.min_y < height); + assert(frame_period > 0); + + // fill in the new parameters + m_width = width; + m_height = height; + m_visarea = visarea; + + // reallocate bitmap if necessary + realloc_screen_bitmaps(); + + // compute timing parameters + m_frame_period = frame_period; + m_scantime = frame_period / height; + m_pixeltime = frame_period / (height * width); + + // if there has been no VBLANK time specified in the MACHINE_DRIVER, compute it now + // from the visible area, otherwise just used the supplied value + if (m_config.m_vblank == 0 && !m_config.m_oldstyle_vblank_supplied) + m_vblank_period = m_scantime * (height - (visarea.max_y + 1 - visarea.min_y)); + else + m_vblank_period = m_config.m_vblank; + + // if we are on scanline 0 already, reset the update timer immediately + // otherwise, defer until the next scanline 0 + if (vpos() == 0) + timer_adjust_oneshot(m_scanline0_timer, attotime_zero, 0); + else + timer_adjust_oneshot(m_scanline0_timer, time_until_pos(0), 0); + + // start the VBLANK timer + timer_adjust_oneshot(m_vblank_begin_timer, time_until_vblank_start(), 0); + + // adjust speed if necessary + m_machine.video().update_refresh_speed(); +} + + +//------------------------------------------------- +// reset_origin - reset the timing such that the +// given (x,y) occurs at the current time +//------------------------------------------------- + +void screen_device::reset_origin(int beamy, int beamx) +{ + // compute the effective VBLANK start/end times + attotime curtime = timer_get_time(machine); + m_vblank_end_time = attotime_sub_attoseconds(curtime, beamy * m_scantime + beamx * m_pixeltime); + m_vblank_start_time = attotime_sub_attoseconds(m_vblank_end_time, m_vblank_period); + + // if we are resetting relative to (0,0) == VBLANK end, call the + // scanline 0 timer by hand now; otherwise, adjust it for the future + if (beamy == 0 && beamx == 0) + scanline0_callback(); + else + timer_adjust_oneshot(m_scanline0_timer, time_until_pos(0), 0); + + // if we are resetting relative to (visarea.max_y + 1, 0) == VBLANK start, + // call the VBLANK start timer now; otherwise, adjust it for the future + if (beamy == m_visarea.max_y + 1 && beamx == 0) + vblank_begin_callback(); + else + timer_adjust_oneshot(m_vblank_begin_timer, time_until_vblank_start(), 0); +} + + +//------------------------------------------------- +// realloc_screen_bitmaps - reallocate bitmaps +// and textures as necessary +//------------------------------------------------- + +void screen_device::realloc_screen_bitmaps() +{ + if (m_config.m_type == SCREEN_TYPE_VECTOR) + return; + + int curwidth = 0, curheight = 0; + + // extract the current width/height from the bitmap + if (m_bitmap[0] != NULL) + { + curwidth = m_bitmap[0]->width; + curheight = m_bitmap[0]->height; + } + + // if we're too small to contain this width/height, reallocate our bitmaps and textures + if (m_width > curwidth || m_height > curheight) + { + // free what we have currently + m_machine.render().texture_free(m_texture[0]); + m_machine.render().texture_free(m_texture[1]); + auto_free(machine, m_bitmap[0]); + auto_free(machine, m_bitmap[1]); + + // compute new width/height + curwidth = MAX(m_width, curwidth); + curheight = MAX(m_height, curheight); + + // choose the texture format - convert the screen format to a texture format + palette_t *palette = NULL; + switch (m_config.m_format) + { + case BITMAP_FORMAT_INDEXED16: m_texture_format = TEXFORMAT_PALETTE16; palette = machine->palette; break; + case BITMAP_FORMAT_RGB15: m_texture_format = TEXFORMAT_RGB15; palette = NULL; break; + case BITMAP_FORMAT_RGB32: m_texture_format = TEXFORMAT_RGB32; palette = NULL; break; + default: fatalerror("Invalid bitmap format!"); break; + } + + // allocate bitmaps + m_bitmap[0] = auto_alloc(machine, bitmap_t(curwidth, curheight, m_config.m_format)); + bitmap_set_palette(m_bitmap[0], machine->palette); + m_bitmap[1] = auto_alloc(machine, bitmap_t(curwidth, curheight, m_config.m_format)); + bitmap_set_palette(m_bitmap[1], machine->palette); + + // allocate textures + m_texture[0] = m_machine.render().texture_alloc(); + m_texture[0]->set_bitmap(m_bitmap[0], &m_visarea, m_texture_format, palette); + m_texture[1] = m_machine.render().texture_alloc(); + m_texture[1]->set_bitmap(m_bitmap[1], &m_visarea, m_texture_format, palette); + } +} + + +//------------------------------------------------- +// set_visible_area - just set the visible area +//------------------------------------------------- + +void screen_device::set_visible_area(int min_x, int max_x, int min_y, int max_y) +{ + // validate arguments + assert(min_x >= 0); + assert(min_y >= 0); + assert(min_x < max_x); + assert(min_y < max_y); + + rectangle visarea; + visarea.min_x = min_x; + visarea.max_x = max_x; + visarea.min_y = min_y; + visarea.max_y = max_y; + + configure(m_width, m_height, visarea, m_frame_period); +} + + +//------------------------------------------------- +// update_partial - perform a partial update from +// the last scanline up to and including the +// specified scanline +//-----------------------------------------------*/ + +bool screen_device::update_partial(int scanline) +{ + // validate arguments + assert(scanline >= 0); + + LOG_PARTIAL_UPDATES(("Partial: update_partial(%s, %d): ", tag(), scanline)); + + // these two checks only apply if we're allowed to skip frames + if (!(machine->config->m_video_attributes & VIDEO_ALWAYS_UPDATE)) + { + // if skipping this frame, bail + if (m_machine.video().skip_this_frame()) + { + LOG_PARTIAL_UPDATES(("skipped due to frameskipping\n")); + return FALSE; + } + + // skip if this screen is not visible anywhere + if (!m_machine.render().is_live(*this)) + { + LOG_PARTIAL_UPDATES(("skipped because screen not live\n")); + return FALSE; + } + } + + // skip if less than the lowest so far + if (scanline < m_last_partial_scan) + { + LOG_PARTIAL_UPDATES(("skipped because less than previous\n")); + return FALSE; + } + + // set the start/end scanlines + rectangle clip = m_visarea; + if (m_last_partial_scan > clip.min_y) + clip.min_y = m_last_partial_scan; + if (scanline < clip.max_y) + clip.max_y = scanline; + + // render if necessary + bool result = false; + if (clip.min_y <= clip.max_y) + { + UINT32 flags = UPDATE_HAS_NOT_CHANGED; + + g_profiler.start(PROFILER_VIDEO); + LOG_PARTIAL_UPDATES(("updating %d-%d\n", clip.min_y, clip.max_y)); + + flags = machine->driver_data<driver_device>()->video_update(*this, *m_bitmap[m_curbitmap], clip); + m_partial_updates_this_frame++; + g_profiler.stop(); + + // if we modified the bitmap, we have to commit + m_changed |= ~flags & UPDATE_HAS_NOT_CHANGED; + result = true; + } + + // remember where we left off + m_last_partial_scan = scanline + 1; + return result; +} + + +//------------------------------------------------- +// update_now - perform an update from the last +// beam position up to the current beam position +//------------------------------------------------- + +void screen_device::update_now() +{ + int current_vpos = vpos(); + int current_hpos = hpos(); + + // since we can currently update only at the scanline + // level, we are trying to do the right thing by + // updating including the current scanline, only if the + // beam is past the halfway point horizontally. + // If the beam is in the first half of the scanline, + // we only update up to the previous scanline. + // This minimizes the number of pixels that might be drawn + // incorrectly until we support a pixel level granularity + if (current_hpos < (m_width / 2) && current_vpos > 0) + current_vpos = current_vpos - 1; + + update_partial(current_vpos); +} + + +//------------------------------------------------- +// vpos - returns the current vertical position +// of the beam +//------------------------------------------------- + +int screen_device::vpos() const +{ + attoseconds_t delta = attotime_to_attoseconds(attotime_sub(timer_get_time(machine), m_vblank_start_time)); + int vpos; + + // round to the nearest pixel + delta += m_pixeltime / 2; + + // compute the v position relative to the start of VBLANK + vpos = delta / m_scantime; + + // adjust for the fact that VBLANK starts at the bottom of the visible area + return (m_visarea.max_y + 1 + vpos) % m_height; +} + + +//------------------------------------------------- +// hpos - returns the current horizontal position +// of the beam +//------------------------------------------------- + +int screen_device::hpos() const +{ + attoseconds_t delta = attotime_to_attoseconds(attotime_sub(timer_get_time(machine), m_vblank_start_time)); + + // round to the nearest pixel + delta += m_pixeltime / 2; + + // compute the v position relative to the start of VBLANK + int vpos = delta / m_scantime; + + // subtract that from the total time + delta -= vpos * m_scantime; + + // return the pixel offset from the start of this scanline + return delta / m_pixeltime; +} + + +//------------------------------------------------- +// time_until_pos - returns the amount of time +// remaining until the beam is at the given +// hpos,vpos +//------------------------------------------------- + +attotime screen_device::time_until_pos(int vpos, int hpos) const +{ + // validate arguments + assert(vpos >= 0); + assert(hpos >= 0); + + // since we measure time relative to VBLANK, compute the scanline offset from VBLANK + vpos += m_height - (m_visarea.max_y + 1); + vpos %= m_height; + + // compute the delta for the given X,Y position + attoseconds_t targetdelta = (attoseconds_t)vpos * m_scantime + (attoseconds_t)hpos * m_pixeltime; + + // if we're past that time (within 1/2 of a pixel), head to the next frame + attoseconds_t curdelta = attotime_to_attoseconds(attotime_sub(timer_get_time(machine), m_vblank_start_time)); + if (targetdelta <= curdelta + m_pixeltime / 2) + targetdelta += m_frame_period; + while (targetdelta <= curdelta) + targetdelta += m_frame_period; + + // return the difference + return attotime_make(0, targetdelta - curdelta); +} + + +//------------------------------------------------- +// time_until_vblank_end - returns the amount of +// time remaining until the end of the current +// VBLANK (if in progress) or the end of the next +// VBLANK +//------------------------------------------------- + +attotime screen_device::time_until_vblank_end() const +{ + // if we are in the VBLANK region, compute the time until the end of the current VBLANK period + attotime target_time = m_vblank_end_time; + if (!vblank()) + target_time = attotime_add_attoseconds(target_time, m_frame_period); + return attotime_sub(target_time, timer_get_time(machine)); +} + + +//------------------------------------------------- +// register_vblank_callback - registers a VBLANK +// callback +//------------------------------------------------- + +void screen_device::register_vblank_callback(vblank_state_changed_func vblank_callback, void *param) +{ + // validate arguments + assert(vblank_callback != NULL); + + // check if we already have this callback registered + callback_item **itemptr; + for (itemptr = &m_callback_list; *itemptr != NULL; itemptr = &(*itemptr)->m_next) + if ((*itemptr)->m_callback == vblank_callback) + break; + + // if not found, register + if (*itemptr == NULL) + { + *itemptr = auto_alloc(machine, callback_item); + (*itemptr)->m_next = NULL; + (*itemptr)->m_callback = vblank_callback; + (*itemptr)->m_param = param; + } +} + + +//------------------------------------------------- +// vblank_begin_callback - call any external +// callbacks to signal the VBLANK period has begun +//------------------------------------------------- + +void screen_device::vblank_begin_callback() +{ + // reset the starting VBLANK time + m_vblank_start_time = timer_get_time(machine); + m_vblank_end_time = attotime_add_attoseconds(m_vblank_start_time, m_vblank_period); + + // call the screen specific callbacks + for (callback_item *item = m_callback_list; item != NULL; item = item->m_next) + (*item->m_callback)(*this, item->m_param, true); + + // if this is the primary screen and we need to update now + if (this == machine->primary_screen && !(machine->config->m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK)) + machine->video().frame_update(); + + // reset the VBLANK start timer for the next frame + timer_adjust_oneshot(m_vblank_begin_timer, time_until_vblank_start(), 0); + + // if no VBLANK period, call the VBLANK end callback immedietely, otherwise reset the timer + if (m_vblank_period == 0) + vblank_end_callback(); + else + timer_adjust_oneshot(m_vblank_end_timer, time_until_vblank_end(), 0); +} + + +//------------------------------------------------- +// vblank_end_callback - call any external +// callbacks to signal the VBLANK period has ended +//------------------------------------------------- + +void screen_device::vblank_end_callback() +{ + // call the screen specific callbacks + for (callback_item *item = m_callback_list; item != NULL; item = item->m_next) + (*item->m_callback)(*this, item->m_param, false); + + // if this is the primary screen and we need to update now + if (this == machine->primary_screen && (machine->config->m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK)) + machine->video().frame_update(); + + // increment the frame number counter + m_frame_number++; +} + + +//------------------------------------------------- +// scanline0_callback - reset partial updates +// for a screen +//------------------------------------------------- + +void screen_device::scanline0_callback() +{ + // reset partial updates + m_last_partial_scan = 0; + m_partial_updates_this_frame = 0; + + timer_adjust_oneshot(m_scanline0_timer, time_until_pos(0), 0); +} + + +//------------------------------------------------- +// scanline_update_callback - perform partial +// updates on each scanline +//------------------------------------------------- + +void screen_device::scanline_update_callback(int scanline) +{ + // force a partial update to the current scanline + update_partial(scanline); + + // compute the next visible scanline + scanline++; + if (scanline > m_visarea.max_y) + scanline = m_visarea.min_y; + timer_adjust_oneshot(m_scanline_timer, time_until_pos(scanline), scanline); +} + + +//------------------------------------------------- +// update_quads - set up the quads for this +// screen +//------------------------------------------------- + +bool screen_device::update_quads() +{ + // only update if live + if (m_machine.render().is_live(*this)) + { + // only update if empty and not a vector game; otherwise assume the driver did it directly + if (m_config.m_type != SCREEN_TYPE_VECTOR && (machine->config->m_video_attributes & VIDEO_SELF_RENDER) == 0) + { + // if we're not skipping the frame and if the screen actually changed, then update the texture + if (!m_machine.video().skip_this_frame() && m_changed) + { + rectangle fixedvis = m_visarea; + fixedvis.max_x++; + fixedvis.max_y++; + + palette_t *palette = (m_texture_format == TEXFORMAT_PALETTE16) ? machine->palette : NULL; + m_texture[m_curbitmap]->set_bitmap(m_bitmap[m_curbitmap], &fixedvis, m_texture_format, palette); + + m_curtexture = m_curbitmap; + m_curbitmap = 1 - m_curbitmap; + } + + // create an empty container with a single quad + m_container->empty(); + m_container->add_quad(0.0f, 0.0f, 1.0f, 1.0f, MAKE_ARGB(0xff,0xff,0xff,0xff), m_texture[m_curtexture], PRIMFLAG_BLENDMODE(BLENDMODE_NONE) | PRIMFLAG_SCREENTEX(1)); + } + } + + // reset the screen changed flags + bool result = m_changed; + m_changed = false; + return result; +} + + +//------------------------------------------------- +// update_burnin - update the burnin bitmap +//------------------------------------------------- + +void screen_device::update_burnin() +{ +#undef rand + if (m_burnin == NULL) + return; + + bitmap_t *srcbitmap = m_bitmap[m_curtexture]; + if (srcbitmap == NULL) + return; + + int srcwidth = srcbitmap->width; + int srcheight = srcbitmap->height; + int dstwidth = m_burnin->width; + int dstheight = m_burnin->height; + int xstep = (srcwidth << 16) / dstwidth; + int ystep = (srcheight << 16) / dstheight; + int xstart = ((UINT32)rand() % 32767) * xstep / 32767; + int ystart = ((UINT32)rand() % 32767) * ystep / 32767; + int srcx, srcy; + int x, y; + + // iterate over rows in the destination + for (y = 0, srcy = ystart; y < dstheight; y++, srcy += ystep) + { + UINT64 *dst = BITMAP_ADDR64(m_burnin, y, 0); + + // handle the 16-bit palettized case + if (srcbitmap->format == BITMAP_FORMAT_INDEXED16) + { + const UINT16 *src = BITMAP_ADDR16(srcbitmap, srcy >> 16, 0); + const rgb_t *palette = palette_entry_list_adjusted(machine->palette); + for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep) + { + rgb_t pixel = palette[src[srcx >> 16]]; + dst[x] += RGB_GREEN(pixel) + RGB_RED(pixel) + RGB_BLUE(pixel); + } + } + + // handle the 15-bit RGB case + else if (srcbitmap->format == BITMAP_FORMAT_RGB15) + { + const UINT16 *src = BITMAP_ADDR16(srcbitmap, srcy >> 16, 0); + for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep) + { + rgb15_t pixel = src[srcx >> 16]; + dst[x] += ((pixel >> 10) & 0x1f) + ((pixel >> 5) & 0x1f) + ((pixel >> 0) & 0x1f); + } + } + + // handle the 32-bit RGB case + else if (srcbitmap->format == BITMAP_FORMAT_RGB32) + { + const UINT32 *src = BITMAP_ADDR32(srcbitmap, srcy >> 16, 0); + for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep) + { + rgb_t pixel = src[srcx >> 16]; + dst[x] += RGB_GREEN(pixel) + RGB_RED(pixel) + RGB_BLUE(pixel); + } + } + } +} + + +//------------------------------------------------- +// finalize_burnin - finalize the burnin bitmap +//------------------------------------------------- + +void screen_device::finalize_burnin() +{ + if (m_burnin == NULL) + return; + + // compute the scaled visible region + rectangle scaledvis; + scaledvis.min_x = m_visarea.min_x * m_burnin->width / m_width; + scaledvis.max_x = m_visarea.max_x * m_burnin->width / m_width; + scaledvis.min_y = m_visarea.min_y * m_burnin->height / m_height; + scaledvis.max_y = m_visarea.max_y * m_burnin->height / m_height; + + // wrap a bitmap around the subregion we care about + bitmap_t *finalmap = auto_alloc(machine, bitmap_t(scaledvis.max_x + 1 - scaledvis.min_x, + scaledvis.max_y + 1 - scaledvis.min_y, + BITMAP_FORMAT_ARGB32)); + + int srcwidth = m_burnin->width; + int srcheight = m_burnin->height; + int dstwidth = finalmap->width; + int dstheight = finalmap->height; + int xstep = (srcwidth << 16) / dstwidth; + int ystep = (srcheight << 16) / dstheight; + + // find the maximum value + UINT64 minval = ~(UINT64)0; + UINT64 maxval = 0; + for (int y = 0; y < srcheight; y++) + { + UINT64 *src = BITMAP_ADDR64(m_burnin, y, 0); + for (int x = 0; x < srcwidth; x++) + { + minval = MIN(minval, src[x]); + maxval = MAX(maxval, src[x]); + } + } + + if (minval == maxval) + return; + + // now normalize and convert to RGB + for (int y = 0, srcy = 0; y < dstheight; y++, srcy += ystep) + { + UINT64 *src = BITMAP_ADDR64(m_burnin, srcy >> 16, 0); + UINT32 *dst = BITMAP_ADDR32(finalmap, y, 0); + for (int x = 0, srcx = 0; x < dstwidth; x++, srcx += xstep) + { + int brightness = (UINT64)(maxval - src[srcx >> 16]) * 255 / (maxval - minval); + dst[x] = MAKE_ARGB(0xff, brightness, brightness, brightness); + } + } + + // write the final PNG + + // compute the name and create the file + astring fname; + fname.printf("%s" PATH_SEPARATOR "burnin-%s.png", machine->basename(), tag()); + mame_file *file; + file_error filerr = mame_fopen(SEARCHPATH_SCREENSHOT, fname, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &file); + if (filerr == FILERR_NONE) + { + png_info pnginfo = { 0 }; + png_error pngerr; + char text[256]; + + // add two text entries describing the image + sprintf(text, APPNAME " %s", build_version); + png_add_text(&pnginfo, "Software", text); + sprintf(text, "%s %s", machine->m_game.manufacturer, machine->m_game.description); + png_add_text(&pnginfo, "System", text); + + // now do the actual work + pngerr = png_write_bitmap(mame_core_file(file), &pnginfo, finalmap, 0, NULL); + + // free any data allocated + png_free(&pnginfo); + mame_fclose(file); + } +} + + +//------------------------------------------------- +// finalize_burnin - finalize the burnin bitmap +//------------------------------------------------- + +void screen_device::load_effect_overlay(const char *filename) +{ + // ensure that there is a .png extension + astring fullname(filename); + int extension = fullname.rchr(0, '.'); + if (extension != -1) + fullname.del(extension, -1); + fullname.cat(".png"); + + // load the file + m_screen_overlay_bitmap = render_load_png(OPTION_ARTPATH, NULL, fullname, NULL, NULL); + if (m_screen_overlay_bitmap != NULL) + m_container->set_overlay(m_screen_overlay_bitmap); + else + mame_printf_warning("Unable to load effect PNG file '%s'\n", fullname.cstr()); +} diff --git a/src/emu/screen.h b/src/emu/screen.h new file mode 100644 index 00000000000..4e61e71afa6 --- /dev/null +++ b/src/emu/screen.h @@ -0,0 +1,305 @@ +/*************************************************************************** + + screen.h + + Core MAME screen device. + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#pragma once + +#ifndef __EMU_H__ +#error Dont include this file directly; include emu.h instead. +#endif + +#ifndef __SCREEN_H__ +#define __SCREEN_H__ + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +// screen types +enum screen_type_enum +{ + SCREEN_TYPE_INVALID = 0, + SCREEN_TYPE_RASTER, + SCREEN_TYPE_VECTOR, + SCREEN_TYPE_LCD +}; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// forward references +class render_texture; +class screen_device; + + +// device type definition +extern const device_type SCREEN; + + +// callback that is called to notify of a change in the VBLANK state +typedef void (*vblank_state_changed_func)(screen_device &device, void *param, bool vblank_state); + + +// ======================> screen_device_config + +class screen_device_config : public device_config +{ + friend class screen_device; + + // construction/destruction + screen_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock); + +public: + // allocators + static device_config *static_alloc_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock); + virtual device_t *alloc_device(running_machine &machine) const; + + // configuration readers + screen_device_config *next_screen() const { return downcast<screen_device_config *>(typenext()); } + screen_type_enum screen_type() const { return m_type; } + int width() const { return m_width; } + int height() const { return m_height; } + const rectangle &visible_area() const { return m_visarea; } + bool oldstyle_vblank_supplied() const { return m_oldstyle_vblank_supplied; } + attoseconds_t refresh() const { return m_refresh; } + attoseconds_t vblank() const { return m_vblank; } + bitmap_format format() const { return m_format; } + float xoffset() const { return m_xoffset; } + float yoffset() const { return m_yoffset; } + float xscale() const { return m_xscale; } + float yscale() const { return m_yscale; } + + // inline configuration helpers + static void static_set_format(device_config *device, bitmap_format format); + static void static_set_type(device_config *device, screen_type_enum type); + static void static_set_raw(device_config *device, UINT32 pixclock, UINT16 htotal, UINT16 hbend, UINT16 hbstart, UINT16 vtotal, UINT16 vbend, UINT16 vbstart); + static void static_set_refresh(device_config *device, attoseconds_t rate); + static void static_set_vblank_time(device_config *device, attoseconds_t time); + static void static_set_size(device_config *device, UINT16 width, UINT16 height); + static void static_set_visarea(device_config *device, INT16 minx, INT16 maxx, INT16 miny, INT16 maxy); + static void static_set_default_position(device_config *device, double xscale, double xoffs, double yscale, double yoffs); + +private: + // device_config overrides + virtual bool device_validity_check(const game_driver &driver) const; + + // inline configuration data + screen_type_enum m_type; // type of screen + int m_width, m_height; // default total width/height (HTOTAL, VTOTAL) + rectangle m_visarea; // default visible area (HBLANK end/start, VBLANK end/start) + bool m_oldstyle_vblank_supplied; // MDRV_SCREEN_VBLANK_TIME macro used + attoseconds_t m_refresh; // default refresh period + attoseconds_t m_vblank; // duration of a VBLANK + bitmap_format m_format; // bitmap format + float m_xoffset, m_yoffset; // default X/Y offsets + float m_xscale, m_yscale; // default X/Y scale factor +}; + + +// ======================> screen_device + +class screen_device : public device_t +{ + friend class screen_device_config; + friend class render_manager; + friend resource_pool_object<screen_device>::~resource_pool_object(); + + // construction/destruction + screen_device(running_machine &_machine, const screen_device_config &config); + virtual ~screen_device(); + +public: + // information getters + const screen_device_config &config() const { return m_config; } + screen_device *next_screen() const { return downcast<screen_device *>(typenext()); } + screen_type_enum screen_type() const { return m_config.m_type; } + int width() const { return m_width; } + int height() const { return m_height; } + const rectangle &visible_area() const { return m_visarea; } + bitmap_format format() const { return m_config.m_format; } + render_container &container() const { assert(m_container != NULL); return *m_container; } + + // dynamic configuration + void configure(int width, int height, const rectangle &visarea, attoseconds_t frame_period); + void reset_origin(int beamy = 0, int beamx = 0); + void set_visible_area(int min_x, int max_x, int min_y, int max_y); + + // beam positioning and state + int vpos() const; + int hpos() const; + bool vblank() const { return attotime_compare(timer_get_time(machine), m_vblank_end_time) < 0; } + bool hblank() const { int curpos = hpos(); return (curpos < m_visarea.min_x || curpos > m_visarea.max_x); } + + // timing + attotime time_until_pos(int vpos, int hpos = 0) const; + attotime time_until_vblank_start() const { return time_until_pos(m_visarea.max_y + 1); } + attotime time_until_vblank_end() const; + attotime time_until_update() const { return (machine->config->m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK) ? time_until_vblank_end() : time_until_vblank_start(); } + attotime scan_period() const { return attotime_make(0, m_scantime); } + attotime frame_period() const { return (this == NULL || !started()) ? DEFAULT_FRAME_PERIOD : attotime_make(0, m_frame_period); }; + UINT64 frame_number() const { return m_frame_number; } + int partial_updates() const { return m_partial_updates_this_frame; } + + // updating + bool update_partial(int scanline); + void update_now(); + + // additional helpers + void register_vblank_callback(vblank_state_changed_func vblank_callback, void *param); + bitmap_t *alloc_compatible_bitmap(int width = 0, int height = 0) { return auto_bitmap_alloc(machine, (width == 0) ? m_width : width, (height == 0) ? m_height : height, m_config.m_format); } + + // internal to the video system + bool update_quads(); + void update_burnin(); + + // globally accessible constants + static const int DEFAULT_FRAME_RATE = 60; + static const attotime DEFAULT_FRAME_PERIOD; + +private: + // device-level overrides + virtual void device_start(); + virtual void device_post_load(); + + // internal helpers + void set_container(render_container &container) { m_container = &container; } + void realloc_screen_bitmaps(); + + static TIMER_CALLBACK( static_vblank_begin_callback ) { reinterpret_cast<screen_device *>(ptr)->vblank_begin_callback(); } + void vblank_begin_callback(); + + static TIMER_CALLBACK( static_vblank_end_callback ) { reinterpret_cast<screen_device *>(ptr)->vblank_end_callback(); } + void vblank_end_callback(); + + static TIMER_CALLBACK( static_scanline0_callback ) { reinterpret_cast<screen_device *>(ptr)->scanline0_callback(); } +public: // temporary + void scanline0_callback(); +private: + + static TIMER_CALLBACK( static_scanline_update_callback ) { reinterpret_cast<screen_device *>(ptr)->scanline_update_callback(param); } + void scanline_update_callback(int scanline); + + void finalize_burnin(); + void load_effect_overlay(const char *filename); + + // internal state + const screen_device_config &m_config; + render_container * m_container; // pointer to our container + + // dimensions + int m_width; // current width (HTOTAL) + int m_height; // current height (VTOTAL) + rectangle m_visarea; // current visible area (HBLANK end/start, VBLANK end/start) + + // textures and bitmaps + render_texture * m_texture[2]; // 2x textures for the screen bitmap + bitmap_t * m_bitmap[2]; // 2x bitmaps for rendering + bitmap_t * m_burnin; // burn-in bitmap + UINT8 m_curbitmap; // current bitmap index + UINT8 m_curtexture; // current texture index + INT32 m_texture_format; // texture format of bitmap for this screen + bool m_changed; // has this bitmap changed? + INT32 m_last_partial_scan; // scanline of last partial update + bitmap_t * m_screen_overlay_bitmap;// screen overlay bitmap + + // screen timing + attoseconds_t m_frame_period; // attoseconds per frame + attoseconds_t m_scantime; // attoseconds per scanline + attoseconds_t m_pixeltime; // attoseconds per pixel + attoseconds_t m_vblank_period; // attoseconds per VBLANK period + attotime m_vblank_start_time; // time of last VBLANK start + attotime m_vblank_end_time; // time of last VBLANK end + emu_timer * m_vblank_begin_timer; // timer to signal VBLANK start + emu_timer * m_vblank_end_timer; // timer to signal VBLANK end + emu_timer * m_scanline0_timer; // scanline 0 timer + emu_timer * m_scanline_timer; // scanline timer + UINT64 m_frame_number; // the current frame number + UINT32 m_partial_updates_this_frame;// partial update counter this frame + + struct callback_item + { + callback_item * m_next; + vblank_state_changed_func m_callback; + void * m_param; + }; + callback_item * m_callback_list; // list of VBLANK callbacks +}; + + + +//************************************************************************** +// SCREEN DEVICE CONFIGURATION MACROS +//************************************************************************** + +#define MDRV_SCREEN_ADD(_tag, _type) \ + MDRV_DEVICE_ADD(_tag, SCREEN, 0) \ + MDRV_SCREEN_TYPE(_type) \ + +#define MDRV_SCREEN_MODIFY(_tag) \ + MDRV_DEVICE_MODIFY(_tag) + +#define MDRV_SCREEN_FORMAT(_format) \ + screen_device_config::static_set_format(device, _format); \ + +#define MDRV_SCREEN_TYPE(_type) \ + screen_device_config::static_set_type(device, SCREEN_TYPE_##_type); \ + +#define MDRV_SCREEN_RAW_PARAMS(_pixclock, _htotal, _hbend, _hbstart, _vtotal, _vbend, _vbstart) \ + screen_device_config::static_set_raw(device, _pixclock, _htotal, _hbend, _hbstart, _vtotal, _vbend, _vbstart); + +#define MDRV_SCREEN_REFRESH_RATE(_rate) \ + screen_device_config::static_set_refresh(device, HZ_TO_ATTOSECONDS(_rate)); \ + +#define MDRV_SCREEN_VBLANK_TIME(_time) \ + screen_device_config::static_set_vblank_time(device, _time); \ + +#define MDRV_SCREEN_SIZE(_width, _height) \ + screen_device_config::static_set_size(device, _width, _height); \ + +#define MDRV_SCREEN_VISIBLE_AREA(_minx, _maxx, _miny, _maxy) \ + screen_device_config::static_set_visarea(device, _minx, _maxx, _miny, _maxy); \ + +#define MDRV_SCREEN_DEFAULT_POSITION(_xscale, _xoffs, _yscale, _yoffs) \ + screen_device_config::static_set_default_position(device, _xscale, _xoffs, _yscale, _yoffs); \ + + +#endif /* __SCREEN_H__ */ diff --git a/src/emu/sound.c b/src/emu/sound.c index 9917678a368..ce85a74c4e3 100644 --- a/src/emu/sound.c +++ b/src/emu/sound.c @@ -379,7 +379,7 @@ static TIMER_CALLBACK( sound_update ) speaker->mix(leftmix, rightmix, samples_this_update, !global->enabled); /* now downmix the final result */ - finalmix_step = video_get_speed_factor(); + finalmix_step = machine->video().speed_factor(); finalmix_offset = 0; for (sample = global->finalmix_leftover; sample < samples_this_update * 100; sample += finalmix_step) { @@ -409,7 +409,7 @@ static TIMER_CALLBACK( sound_update ) { if (!global->nosound_mode) machine->osd().update_audio_stream(finalmix, finalmix_offset / 2); - video_avi_add_sound(machine, finalmix, finalmix_offset / 2); + machine->video().add_sound_to_recording(finalmix, finalmix_offset / 2); if (global->wavfile != NULL) wav_add_data_16(global->wavfile, finalmix, finalmix_offset); } diff --git a/src/emu/state.h b/src/emu/state.h index e8114eb6ac5..6ed3c887855 100644 --- a/src/emu/state.h +++ b/src/emu/state.h @@ -56,6 +56,21 @@ typedef enum _state_save_error state_save_error; #define STATE_POSTLOAD(name) void name(running_machine *machine, void *param) +template<class T, void (T::*func)()> +void state_presave_stub(running_machine *machine, void *param) +{ + T *target = reinterpret_cast<T *>(param); + (target->*func)(); +} + +template<class T, void (T::*func)()> +void state_postload_stub(running_machine *machine, void *param) +{ + T *target = reinterpret_cast<T *>(param); + (target->*func)(); +} + + #ifdef __GNUC__ #define IS_VALID_SAVE_TYPE(_var) \ (std::tr1::is_arithmetic<typeof(_var)>::value || std::tr1::is_enum<typeof(_var)>::value || \ diff --git a/src/emu/ui.c b/src/emu/ui.c index 19aa346ede6..4f17ce2df37 100644 --- a/src/emu/ui.c +++ b/src/emu/ui.c @@ -324,11 +324,11 @@ int ui_display_startup_screens(running_machine *machine, int first_time, int sho /* loop while we have a handler */ while (ui_handler_callback != handler_ingame && !machine->scheduled_event_pending() && !ui_menu_is_force_game_select()) - video_frame_update(machine, FALSE); + machine->video().frame_update(); /* clear the handler and force an update */ ui_set_handler(handler_ingame, 0); - video_frame_update(machine, FALSE); + machine->video().frame_update(); } /* if we're the empty driver, force the menus on */ @@ -357,7 +357,7 @@ void ui_set_startup_text(running_machine *machine, const char *text, int force) if (force || (curtime - lastupdatetime) > osd_ticks_per_second() / 4) { lastupdatetime = curtime; - video_frame_update(machine, FALSE); + machine->video().frame_update(); } } @@ -1000,7 +1000,7 @@ static astring &warnings_string(running_machine *machine, astring &string) astring &game_info_astring(running_machine *machine, astring &string) { - int scrcount = screen_count(*machine->config); + int scrcount = machine->m_devicelist.count(SCREEN); int found_sound = FALSE; /* print description, manufacturer, and CPU: */ @@ -1077,7 +1077,7 @@ astring &game_info_astring(running_machine *machine, astring &string) string.cat("None\n"); else { - for (screen_device *screen = screen_first(*machine); screen != NULL; screen = screen_next(screen)) + for (screen_device *screen = machine->first_screen(); screen != NULL; screen = screen->next_screen()) { if (scrcount > 1) { @@ -1295,7 +1295,8 @@ static UINT32 handler_ingame(running_machine *machine, render_container *contain /* first draw the FPS counter */ if (showfps || osd_ticks() < showfps_end) { - ui_draw_text_full(container, video_get_speed_text(machine), 0.0f, 0.0f, 1.0f, + astring tempstring; + ui_draw_text_full(container, machine->video().speed_text(tempstring), 0.0f, 0.0f, 1.0f, JUSTIFY_RIGHT, WRAP_WORD, DRAW_OPAQUE, ARGB_WHITE, ARGB_BLACK, NULL, NULL); } else @@ -1409,7 +1410,7 @@ static UINT32 handler_ingame(running_machine *machine, render_container *contain /* handle a save snapshot request */ if (ui_input_pressed(machine, IPT_UI_SNAPSHOT)) - video_save_active_screen_snapshots(machine); + machine->video().save_active_screen_snapshots(); /* toggle pause */ if (ui_input_pressed(machine, IPT_UI_PAUSE)) @@ -1433,14 +1434,14 @@ static UINT32 handler_ingame(running_machine *machine, render_container *contain /* toggle movie recording */ if (ui_input_pressed(machine, IPT_UI_RECORD_MOVIE)) { - if (!video_mng_is_movie_active(machine)) + if (!machine->video().is_recording()) { - video_mng_begin_recording(machine, NULL); + machine->video().begin_recording(NULL, video_manager::MF_MNG); popmessage("REC START"); } else { - video_mng_end_recording(machine); + machine->video().end_recording(); popmessage("REC STOP"); } } @@ -1457,10 +1458,10 @@ static UINT32 handler_ingame(running_machine *machine, render_container *contain if (ui_input_pressed(machine, IPT_UI_FRAMESKIP_INC)) { /* get the current value and increment it */ - int newframeskip = video_get_frameskip() + 1; + int newframeskip = machine->video().frameskip() + 1; if (newframeskip > MAX_FRAMESKIP) newframeskip = -1; - video_set_frameskip(newframeskip); + machine->video().set_frameskip(newframeskip); /* display the FPS counter for 2 seconds */ ui_show_fps_temp(2.0); @@ -1470,10 +1471,10 @@ static UINT32 handler_ingame(running_machine *machine, render_container *contain if (ui_input_pressed(machine, IPT_UI_FRAMESKIP_DEC)) { /* get the current value and decrement it */ - int newframeskip = video_get_frameskip() - 1; + int newframeskip = machine->video().frameskip() - 1; if (newframeskip < -1) newframeskip = MAX_FRAMESKIP; - video_set_frameskip(newframeskip); + machine->video().set_frameskip(newframeskip); /* display the FPS counter for 2 seconds */ ui_show_fps_temp(2.0); @@ -1481,16 +1482,16 @@ static UINT32 handler_ingame(running_machine *machine, render_container *contain /* toggle throttle? */ if (ui_input_pressed(machine, IPT_UI_THROTTLE)) - video_set_throttle(!video_get_throttle()); + machine->video().set_throttled(!machine->video().throttled()); /* check for fast forward */ if (input_type_pressed(machine, IPT_UI_FAST_FORWARD, 0)) { - video_set_fastforward(TRUE); + machine->video().set_fastforward(true); ui_show_fps_temp(0.5); } else - video_set_fastforward(FALSE); + machine->video().set_fastforward(false); return 0; } @@ -1659,7 +1660,7 @@ static slider_state *slider_init(running_machine *machine) } /* add screen parameters */ - for (screen_device *screen = screen_first(*machine); screen != NULL; screen = screen_next(screen)) + for (screen_device *screen = machine->first_screen(); screen != NULL; screen = screen->next_screen()) { int defxscale = floor(screen->config().xscale() * 1000.0f + 0.5f); int defyscale = floor(screen->config().yscale() * 1000.0f + 0.5f); @@ -1728,7 +1729,7 @@ static slider_state *slider_init(running_machine *machine) } } - for (screen_device *screen = screen_first(*machine); screen != NULL; screen = screen_next(screen)) + for (screen_device *screen = machine->first_screen(); screen != NULL; screen = screen->next_screen()) if (screen->screen_type() == SCREEN_TYPE_VECTOR) { /* add flicker control */ @@ -2129,7 +2130,7 @@ static INT32 slider_beam(running_machine *machine, void *arg, astring *string, I static char *slider_get_screen_desc(screen_device &screen) { - int scrcount = screen_count(*screen.machine->config); + int scrcount = screen.machine->m_devicelist.count(SCREEN); static char descbuf[256]; if (scrcount > 1) diff --git a/src/emu/validity.c b/src/emu/validity.c index 567b33d7ba2..bfcaa928ae7 100644 --- a/src/emu/validity.c +++ b/src/emu/validity.c @@ -586,7 +586,7 @@ static bool validate_display(const machine_config &config) bool palette_modes = false; bool error = false; - for (const screen_device_config *scrconfig = screen_first(config); scrconfig != NULL; scrconfig = screen_next(scrconfig)) + for (const screen_device_config *scrconfig = config.first_screen(); scrconfig != NULL; scrconfig = scrconfig->next_screen()) if (scrconfig->format() == BITMAP_FORMAT_INDEXED16) palette_modes = true; diff --git a/src/emu/video.c b/src/emu/video.c index ee45042a9e1..70e400ac7b5 100644 --- a/src/emu/video.c +++ b/src/emu/video.c @@ -39,11 +39,9 @@ #include "emu.h" #include "emuopts.h" -#include "profiler.h" #include "png.h" #include "debugger.h" #include "debugint/debugint.h" -#include "rendutil.h" #include "ui.h" #include "aviio.h" #include "crsshair.h" @@ -52,102 +50,20 @@ -/*************************************************************************** - DEBUGGING -***************************************************************************/ +//************************************************************************** +// DEBUGGING +//************************************************************************** #define LOG_THROTTLE (0) -#define VERBOSE (0) -#define LOG_PARTIAL_UPDATES(x) do { if (VERBOSE) logerror x; } while (0) - - - -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -#define SUBSECONDS_PER_SPEED_UPDATE (ATTOSECONDS_PER_SECOND / 4) -#define PAUSED_REFRESH_RATE (30) - - - -/*************************************************************************** - DEVICE DEFINITIONS -***************************************************************************/ -const device_type SCREEN = screen_device_config::static_alloc_device_config; +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** -/*************************************************************************** - TYPE DEFINITIONS -***************************************************************************/ - -typedef struct _video_global video_global; -struct _video_global -{ - /* screenless systems */ - emu_timer * screenless_frame_timer; /* timer to signal VBLANK start */ - - /* throttling calculations */ - osd_ticks_t throttle_last_ticks; /* osd_ticks the last call to throttle */ - attotime throttle_realtime; /* real time the last call to throttle */ - attotime throttle_emutime; /* emulated time the last call to throttle */ - UINT32 throttle_history; /* history of frames where we were fast enough */ - - /* dynamic speed computation */ - osd_ticks_t speed_last_realtime; /* real time at the last speed calculation */ - attotime speed_last_emutime; /* emulated time at the last speed calculation */ - double speed_percent; /* most recent speed percentage */ - UINT32 partial_updates_this_frame;/* partial update counter this frame */ - - /* overall speed computation */ - UINT32 overall_real_seconds; /* accumulated real seconds at normal speed */ - osd_ticks_t overall_real_ticks; /* accumulated real ticks at normal speed */ - attotime overall_emutime; /* accumulated emulated time at normal speed */ - UINT32 overall_valid_counter; /* number of consecutive valid time periods */ - - /* configuration */ - UINT8 throttle; /* flag: TRUE if we're currently throttled */ - UINT8 fastforward; /* flag: TRUE if we're currently fast-forwarding */ - UINT32 seconds_to_run; /* number of seconds to run before quitting */ - UINT8 auto_frameskip; /* flag: TRUE if we're automatically frameskipping */ - UINT32 speed; /* overall speed (*100) */ - - /* frameskipping */ - UINT8 empty_skip_count; /* number of empty frames we have skipped */ - UINT8 frameskip_level; /* current frameskip level */ - UINT8 frameskip_counter; /* counter that counts through the frameskip steps */ - INT8 frameskip_adjust; - UINT8 skipping_this_frame; /* flag: TRUE if we are skipping the current frame */ - osd_ticks_t average_oversleep; /* average number of ticks the OSD oversleeps */ - - /* snapshot stuff */ - render_target * snap_target; /* screen shapshot target */ - bitmap_t * snap_bitmap; /* screen snapshot bitmap */ - UINT8 snap_native; /* are we using native per-screen layouts? */ - INT32 snap_width; /* width of snapshots (0 == auto) */ - INT32 snap_height; /* height of snapshots (0 == auto) */ - - /* movie recording */ - mame_file * mngfile; /* handle to the open movie file */ - avi_file * avifile; /* handle to the open movie file */ - attotime movie_frame_period; /* period of a single movie frame */ - attotime movie_next_frame_time; /* time of next frame */ - UINT32 movie_frame; /* current movie frame number */ -}; - - - -/*************************************************************************** - GLOBAL VARIABLES -***************************************************************************/ - -/* global state */ -static video_global global; - -/* frameskipping tables */ -static const UINT8 skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS] = +// frameskipping tables +const UINT8 video_manager::s_skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS] = { { 0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1 }, @@ -165,560 +81,644 @@ static const UINT8 skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS] = -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ - -/* core implementation */ -static void video_exit(running_machine &machine); -static void init_buffered_spriteram(running_machine *machine); - -/* global rendering */ -static int finish_screen_updates(running_machine *machine); -static TIMER_CALLBACK( screenless_update_callback ); +//************************************************************************** +// FUNCTION PROTOTYPES +//************************************************************************** -/* throttling/frameskipping/performance */ -static void update_throttle(running_machine *machine, attotime emutime); -static osd_ticks_t throttle_until_ticks(running_machine *machine, osd_ticks_t target_ticks); -static void update_frameskip(running_machine *machine); -static void recompute_speed(running_machine *machine, attotime emutime); -static void update_refresh_speed(running_machine *machine); +// software rendering +static void rgb888_draw_primitives(const render_primitive_list &primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); -/* screen snapshots */ -static void create_snapshot_bitmap(device_t *screen); -static file_error mame_fopen_next(running_machine *machine, const char *pathoption, const char *extension, mame_file **file); -/* movie recording */ -static void video_mng_record_frame(running_machine *machine); -static void video_avi_record_frame(running_machine *machine); -/* software rendering */ -static void rgb888_draw_primitives(const render_primitive_list &primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +//************************************************************************** +// VIDEO MANAGER +//************************************************************************** +//------------------------------------------------- +// video_manager - constructor +//------------------------------------------------- + +video_manager::video_manager(running_machine &machine) + : m_machine(machine), + m_screenless_frame_timer(NULL), + 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_throttle(options_get_bool(machine.options(), OPTION_THROTTLE)), + m_fastforward(false), + m_seconds_to_run(options_get_int(machine.options(), OPTION_SECONDS_TO_RUN)), + m_auto_frameskip(options_get_bool(machine.options(), OPTION_AUTOFRAMESKIP)), + m_speed(original_speed_setting()), + m_empty_skip_count(0), + m_frameskip_level(options_get_int(machine.options(), OPTION_FRAMESKIP)), + m_frameskip_counter(0), + m_frameskip_adjust(0), + m_skipping_this_frame(false), + m_average_oversleep(0), + m_snap_target(NULL), + m_snap_bitmap(NULL), + m_snap_native(true), + m_snap_width(0), + m_snap_height(0), + m_mngfile(NULL), + m_avifile(NULL), + m_movie_frame_period(attotime_zero), + m_movie_next_frame_time(attotime_zero), + m_movie_frame(0) +{ + // request a callback upon exiting + machine.add_notifier(MACHINE_NOTIFY_EXIT, exit_static); + state_save_register_postload(&machine, state_postload_stub<video_manager, &video_manager::postload>, this); + + // extract initial execution state from global configuration settings + update_refresh_speed(); + + // create spriteram buffers if necessary + if (machine.config->m_video_attributes & VIDEO_BUFFERS_SPRITERAM) + init_buffered_spriteram(); + + // create a render target for snapshots + const char *viewname = options_get_string(machine.options(), OPTION_SNAPVIEW); + m_snap_native = (machine.primary_screen != NULL && (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) + { + 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); + m_snap_target->set_bezels_enabled(false); + 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 + { + m_snap_target = machine.render().target_alloc(NULL, RENDER_CREATE_HIDDEN); + m_snap_target->set_view(m_snap_target->configured_view(viewname, 0, 1)); + m_snap_target->set_screen_overlay_enabled(false); + } -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ + // extract snap resolution if present + if (sscanf(options_get_string(machine.options(), OPTION_SNAPSIZE), "%dx%d", &m_snap_width, &m_snap_height) != 2) + m_snap_width = m_snap_height = 0; -/*------------------------------------------------- - effective_autoframeskip - return the effective - autoframeskip value, accounting for fast - forward --------------------------------------------------*/ + // start recording movie if specified + const char *filename = options_get_string(machine.options(), OPTION_MNGWRITE); + if (filename[0] != 0) + begin_recording(filename, MF_MNG); -INLINE int effective_autoframeskip(running_machine *machine) -{ - /* if we're fast forwarding or paused, autoframeskip is disabled */ - if (global.fastforward || machine->paused()) - return FALSE; + filename = options_get_string(machine.options(), OPTION_AVIWRITE); + if (filename[0] != 0) + begin_recording(filename, MF_AVI); - /* otherwise, it's up to the user */ - return global.auto_frameskip; + // if no screens, create a periodic timer to drive updates + if (machine.primary_screen == NULL) + { + m_screenless_frame_timer = timer_alloc(&machine, &screenless_update_callback, this); + timer_adjust_periodic(m_screenless_frame_timer, screen_device::DEFAULT_FRAME_PERIOD, 0, screen_device::DEFAULT_FRAME_PERIOD); + } } -/*------------------------------------------------- - effective_frameskip - return the effective - frameskip value, accounting for fast - forward --------------------------------------------------*/ +//------------------------------------------------- +// set_frameskip - set the current actual +// frameskip (-1 means autoframeskip) +//------------------------------------------------- -INLINE int effective_frameskip(void) +void video_manager::set_frameskip(int frameskip) { - /* if we're fast forwarding, use the maximum frameskip */ - if (global.fastforward) - return FRAMESKIP_LEVELS - 1; + // -1 means autoframeskip + if (frameskip == -1) + { + m_auto_frameskip = true; + m_frameskip_level = 0; + } - /* otherwise, it's up to the user */ - return global.frameskip_level; + // any other level is a direct control + else if (frameskip >= 0 && frameskip <= MAX_FRAMESKIP) + { + m_auto_frameskip = false; + m_frameskip_level = frameskip; + } } -/*------------------------------------------------- - effective_throttle - return the effective - throttle value, accounting for fast - forward and user interface --------------------------------------------------*/ +//------------------------------------------------- +// frame_update - handle frameskipping and UI, +// plus updating the screen during normal +// operations +//------------------------------------------------- -INLINE int effective_throttle(running_machine *machine) +void video_manager::frame_update(bool debug) { - /* if we're paused, or if the UI is active, we always throttle */ - if (machine->paused() || ui_is_menu_active()) - return TRUE; - - /* if we're fast forwarding, we don't throttle */ - if (global.fastforward) - return FALSE; + // only render sound and video if we're in the running phase + int phase = m_machine.phase(); + bool skipped_it = m_skipping_this_frame; + if (phase == MACHINE_PHASE_RUNNING && (!m_machine.paused() || options_get_bool(m_machine.options(), OPTION_UPDATEINPAUSE))) + { + bool anything_changed = finish_screen_updates(); - /* otherwise, it's up to the user */ - return global.throttle; -} + // if none of the screens changed and we haven't skipped too many frames in a row, + // mark this frame as skipped to prevent throttling; this helps for games that + // don't update their screen at the monitor refresh rate + if (!anything_changed && !m_auto_frameskip && m_frameskip_level == 0 && m_empty_skip_count++ < 3) + skipped_it = true; + else + m_empty_skip_count = 0; + } + // draw the user interface + ui_update_and_render(&m_machine, &m_machine.render().ui_container()); -/*------------------------------------------------- - original_speed_setting - return the original - speed setting --------------------------------------------------*/ + // update the internal render debugger + debugint_update_during_game(&m_machine); -INLINE int original_speed_setting(void) -{ - return options_get_float(mame_options(), OPTION_SPEED) * 100.0 + 0.5; -} + // if we're throttling, synchronize before rendering + attotime current_time = timer_get_time(&m_machine); + if (!debug && !skipped_it && effective_throttle()) + update_throttle(current_time); + // ask the OSD to update + g_profiler.start(PROFILER_BLIT); + m_machine.osd().update(!debug && skipped_it); + g_profiler.stop(); + // perform tasks for this frame + if (!debug) + m_machine.call_notifiers(MACHINE_NOTIFY_FRAME); -/*************************************************************************** - CORE IMPLEMENTATION -***************************************************************************/ + // update frameskipping + if (!debug) + update_frameskip(); -/*------------------------------------------------- - video_init - start up the video system --------------------------------------------------*/ + // update speed computations + if (!debug && !skipped_it) + recompute_speed(current_time); -void video_init(running_machine *machine) -{ - const char *filename; - const char *viewname; - - /* validate */ - assert(machine != NULL); - assert(machine->config != NULL); - - /* request a callback upon exiting */ - machine->add_notifier(MACHINE_NOTIFY_EXIT, video_exit); - - /* reset our global state */ - memset(&global, 0, sizeof(global)); - global.speed_percent = 1.0; - - /* extract initial execution state from global configuration settings */ - global.speed = original_speed_setting(); - update_refresh_speed(machine); - global.throttle = options_get_bool(machine->options(), OPTION_THROTTLE); - global.auto_frameskip = options_get_bool(machine->options(), OPTION_AUTOFRAMESKIP); - global.frameskip_level = options_get_int(machine->options(), OPTION_FRAMESKIP); - global.seconds_to_run = options_get_int(machine->options(), OPTION_SECONDS_TO_RUN); - - /* create spriteram buffers if necessary */ - if (machine->config->m_video_attributes & VIDEO_BUFFERS_SPRITERAM) - init_buffered_spriteram(machine); - - /* create a render target for snapshots */ - viewname = options_get_string(machine->options(), OPTION_SNAPVIEW); - global.snap_native = (machine->primary_screen != NULL && (viewname[0] == 0 || strcmp(viewname, "native") == 0)); - - /* the native target is hard-coded to our internal layout and has all options disabled */ - if (global.snap_native) + // call the end-of-frame callback + if (phase == MACHINE_PHASE_RUNNING) { - global.snap_target = machine->render().target_alloc(layout_snap, RENDER_CREATE_SINGLE_FILE | RENDER_CREATE_HIDDEN); - global.snap_target->set_backdrops_enabled(false); - global.snap_target->set_overlays_enabled(false); - global.snap_target->set_bezels_enabled(false); - global.snap_target->set_screen_overlay_enabled(false); - global.snap_target->set_zoom_to_screen(false); - } + // reset partial updates if we're paused or if the debugger is active + if (m_machine.primary_screen != NULL && (m_machine.paused() || debug || debugger_within_instruction_hook(&m_machine))) + m_machine.primary_screen->scanline0_callback(); - /* other targets select the specified view and turn off effects */ - else - { - global.snap_target = machine->render().target_alloc(NULL, RENDER_CREATE_HIDDEN); - global.snap_target->set_view(video_get_view_for_target(machine, global.snap_target, viewname, 0, 1)); - global.snap_target->set_screen_overlay_enabled(false); + // otherwise, call the video EOF callback + else + { + g_profiler.start(PROFILER_VIDEO); + m_machine.driver_data<driver_device>()->video_eof(); + g_profiler.stop(); + } } +} - /* extract snap resolution if present */ - if (sscanf(options_get_string(machine->options(), OPTION_SNAPSIZE), "%dx%d", &global.snap_width, &global.snap_height) != 2) - global.snap_width = global.snap_height = 0; - - /* start recording movie if specified */ - filename = options_get_string(machine->options(), OPTION_MNGWRITE); - if (filename[0] != 0) - video_mng_begin_recording(machine, filename); - filename = options_get_string(machine->options(), OPTION_AVIWRITE); - if (filename[0] != 0) - video_avi_begin_recording(machine, filename); +//------------------------------------------------- +// speed_text - print the text to be displayed +// into a string buffer +//------------------------------------------------- - /* if no screens, create a periodic timer to drive updates */ - if (machine->primary_screen == NULL) - { - global.screenless_frame_timer = timer_alloc(machine, screenless_update_callback, NULL); - timer_adjust_periodic(global.screenless_frame_timer, screen_device::k_default_frame_period, 0, screen_device::k_default_frame_period); - } -} +astring &video_manager::speed_text(astring &string) +{ + string.reset(); + // if we're paused, just display Paused + bool paused = m_machine.paused(); + if (paused) + string.cat("paused"); -/*------------------------------------------------- - video_exit - close down the video system --------------------------------------------------*/ + // if we're fast forwarding, just display Fast-forward + else if (m_fastforward) + string.cat("fast "); -static void video_exit(running_machine &machine) -{ - int i; + // if we're auto frameskipping, display that plus the level + else if (effective_autoframeskip()) + string.catprintf("auto%2d/%d", effective_frameskip(), MAX_FRAMESKIP); - /* stop recording any movie */ - video_mng_end_recording(&machine); - video_avi_end_recording(&machine); + // otherwise, just display the frameskip plus the level + else + string.catprintf("skip %d/%d", effective_frameskip(), MAX_FRAMESKIP); - /* free all the graphics elements */ - for (i = 0; i < MAX_GFX_ELEMENTS; i++) - gfx_element_free(machine.gfx[i]); + // append the speed for all cases except paused + if (!paused) + string.catprintf("%4d%%", (int)(100 * m_speed_percent + 0.5)); - /* free the snapshot target */ - machine.render().target_free(global.snap_target); - if (global.snap_bitmap != NULL) - global_free(global.snap_bitmap); + // display the number of partial updates as well + int partials = 0; + for (screen_device *screen = m_machine.first_screen(); screen != NULL; screen = screen->next_screen()) + partials += screen->partial_updates(); + if (partials > 1) + string.catprintf("\n%d partial updates", partials); - /* print a final result if we have at least 5 seconds' worth of data */ - if (global.overall_emutime.seconds >= 5) - { - osd_ticks_t tps = osd_ticks_per_second(); - double final_real_time = (double)global.overall_real_seconds + (double)global.overall_real_ticks / (double)tps; - double final_emu_time = attotime_to_double(global.overall_emutime); - mame_printf_info("Average speed: %.2f%% (%d seconds)\n", 100 * final_emu_time / final_real_time, attotime_add_attoseconds(global.overall_emutime, ATTOSECONDS_PER_SECOND / 2).seconds); - } + return string; } -/*------------------------------------------------- - init_buffered_spriteram - initialize the - double-buffered spriteram --------------------------------------------------*/ +//------------------------------------------------- +// save_snapshot - save a snapshot to the given +// file handle +//------------------------------------------------- -static void init_buffered_spriteram(running_machine *machine) +void video_manager::save_snapshot(screen_device *screen, mame_file &fp) { - assert_always(machine->generic.spriteram_size != 0, "Video buffers spriteram but spriteram size is 0"); + // validate + assert(!m_snap_native || screen != NULL); - /* allocate memory for the back buffer */ - machine->generic.buffered_spriteram.u8 = auto_alloc_array(machine, UINT8, machine->generic.spriteram_size); + // create the bitmap to pass in + create_snapshot_bitmap(screen); - /* register for saving it */ - state_save_register_global_pointer(machine, machine->generic.buffered_spriteram.u8, machine->generic.spriteram_size); + // add two text entries describing the image + astring text1(APPNAME, " ", build_version); + astring text2(m_machine.m_game.manufacturer, " ", m_machine.m_game.description); + png_info pnginfo = { 0 }; + png_add_text(&pnginfo, "Software", text1); + png_add_text(&pnginfo, "System", text2); - /* do the same for the second back buffer, if present */ - if (machine->generic.spriteram2_size) - { - /* allocate memory */ - machine->generic.buffered_spriteram2.u8 = auto_alloc_array(machine, UINT8, machine->generic.spriteram2_size); + // now do the actual work + const rgb_t *palette = (m_machine.palette != NULL) ? palette_entry_list_adjusted(m_machine.palette) : NULL; + png_error error = png_write_bitmap(mame_core_file(&fp), &pnginfo, m_snap_bitmap, m_machine.total_colors(), palette); + if (error != PNGERR_NONE) + mame_printf_error("Error generating PNG for snapshot: png_error = %d\n", error); - /* register for saving it */ - state_save_register_global_pointer(machine, machine->generic.buffered_spriteram2.u8, machine->generic.spriteram2_size); - } + // free any data allocated + png_free(&pnginfo); } +//------------------------------------------------- +// save_active_screen_snapshots - save a +// snapshot of all active screens +//------------------------------------------------- -/*************************************************************************** - SCREEN MANAGEMENT -***************************************************************************/ - -/*------------------------------------------------- - screenless_update_callback - update generator - when there are no screens to drive it --------------------------------------------------*/ - -static TIMER_CALLBACK( screenless_update_callback ) +void video_manager::save_active_screen_snapshots() { - /* force an update */ - video_frame_update(machine, false); + // if we're native, then write one snapshot per visible screen + if (m_snap_native) + { + // write one snapshot per visible screen + for (screen_device *screen = m_machine.first_screen(); screen != NULL; screen = screen->next_screen()) + if (m_machine.render().is_live(*screen)) + { + mame_file *fp; + file_error filerr = mame_fopen_next(SEARCHPATH_SCREENSHOT, "png", fp); + if (filerr == FILERR_NONE) + { + save_snapshot(screen, *fp); + mame_fclose(fp); + } + } + } + + // otherwise, just write a single snapshot + else + { + mame_file *fp; + file_error filerr = mame_fopen_next(SEARCHPATH_SCREENSHOT, "png", fp); + if (filerr == FILERR_NONE) + { + save_snapshot(NULL, *fp); + mame_fclose(fp); + } + } } -/*------------------------------------------------- - video_frame_update - handle frameskipping and - UI, plus updating the screen during normal - operations --------------------------------------------------*/ +//------------------------------------------------- +// begin_recording - begin recording of a movie +//------------------------------------------------- -void video_frame_update(running_machine *machine, int debug) +void video_manager::begin_recording(const char *name, movie_format format) { - attotime current_time = timer_get_time(machine); - int skipped_it = global.skipping_this_frame; - int phase = machine->phase(); + // stop any existign recording + end_recording(); + + // create a snapshot bitmap so we know what the target size is + create_snapshot_bitmap(NULL); - /* validate */ - assert(machine != NULL); - assert(machine->config != NULL); + // reset the state + m_movie_frame = 0; + m_movie_next_frame_time = timer_get_time(&m_machine); - /* only render sound and video if we're in the running phase */ - if (phase == MACHINE_PHASE_RUNNING && (!machine->paused() || options_get_bool(machine->options(), OPTION_UPDATEINPAUSE))) + // start up an AVI recording + if (format == MF_AVI) { - int anything_changed = finish_screen_updates(machine); - - /* if none of the screens changed and we haven't skipped too many frames in a row, - mark this frame as skipped to prevent throttling; this helps for games that - don't update their screen at the monitor refresh rate */ - if (!anything_changed && !global.auto_frameskip && global.frameskip_level == 0 && global.empty_skip_count++ < 3) - skipped_it = TRUE; + // build up information about this new movie + avi_movie_info info; + info.video_format = 0; + info.video_timescale = 1000 * ((m_machine.primary_screen != NULL) ? ATTOSECONDS_TO_HZ(m_machine.primary_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 = m_machine.sample_rate; + info.audio_sampletime = 1; + info.audio_numsamples = 0; + info.audio_channels = 2; + info.audio_samplebits = 16; + info.audio_samplerate = m_machine.sample_rate; + + // create a new temporary movie file + mame_file *tempfile; + file_error filerr; + if (name != NULL) + filerr = mame_fopen(SEARCHPATH_MOVIE, name, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &tempfile); else - global.empty_skip_count = 0; - } + filerr = mame_fopen_next(SEARCHPATH_MOVIE, "avi", tempfile); - /* draw the user interface */ - ui_update_and_render(machine, &machine->render().ui_container()); + // compute the frame time + m_movie_frame_period = attotime_div(ATTOTIME_IN_SEC(1000), info.video_timescale); - /* update the internal render debugger */ - debugint_update_during_game(machine); + // if we succeeded, make a copy of the name and create the real file over top + if (filerr == FILERR_NONE) + { + astring fullname(mame_file_full_name(tempfile)); + mame_fclose(tempfile); - /* if we're throttling, synchronize before rendering */ - if (!debug && !skipped_it && effective_throttle(machine)) - update_throttle(machine, current_time); + // create the file and free the string + avi_error avierr = avi_create(fullname, &info, &m_avifile); + if (avierr != AVIERR_NONE) + mame_printf_error("Error creating AVI: %s\n", avi_error_string(avierr)); + } + } + + // start up a MNG recording + else if (format == MF_MNG) + { + // create a new movie file and start recording + file_error filerr; + if (name != NULL) + filerr = mame_fopen(SEARCHPATH_MOVIE, name, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &m_mngfile); + else + filerr = mame_fopen_next(SEARCHPATH_MOVIE, "mng", m_mngfile); - /* ask the OSD to update */ - g_profiler.start(PROFILER_BLIT); - machine->osd().update(!debug && skipped_it); - g_profiler.stop(); + // start the capture + int rate = (m_machine.primary_screen != NULL) ? ATTOSECONDS_TO_HZ(m_machine.primary_screen->frame_period().attoseconds) : screen_device::DEFAULT_FRAME_RATE; + png_error pngerr = mng_capture_start(mame_core_file(m_mngfile), m_snap_bitmap, rate); + if (pngerr != PNGERR_NONE) + return end_recording(); - /* perform tasks for this frame */ - if (!debug) - machine->call_notifiers(MACHINE_NOTIFY_FRAME); + // compute the frame time + m_movie_frame_period = ATTOTIME_IN_HZ(rate); + } +} - /* update frameskipping */ - if (!debug) - update_frameskip(machine); - /* update speed computations */ - if (!debug && !skipped_it) - recompute_speed(machine, current_time); +//------------------------------------------------- +// end_recording - stop recording of a movie +//------------------------------------------------- - /* call the end-of-frame callback */ - if (phase == MACHINE_PHASE_RUNNING) +void video_manager::end_recording() +{ + // close the file if it exists + if (m_avifile != NULL) { - /* reset partial updates if we're paused or if the debugger is active */ - if (machine->primary_screen != NULL && (machine->paused() || debug || debugger_within_instruction_hook(machine))) - machine->primary_screen->scanline0_callback(); + avi_close(m_avifile); + m_avifile = NULL; + } - /* otherwise, call the video EOF callback */ - else - { - g_profiler.start(PROFILER_VIDEO); - machine->driver_data<driver_device>()->video_eof(); - g_profiler.stop(); - } + // close the file if it exists + if (m_mngfile != NULL) + { + mng_capture_stop(mame_core_file(m_mngfile)); + mame_fclose(m_mngfile); + m_mngfile = NULL; } + + // reset the state + m_movie_frame = 0; } -/*------------------------------------------------- - finish_screen_updates - finish updating all - the screens --------------------------------------------------*/ +//------------------------------------------------- +// add_sound_to_recording - add sound to a movie +// recording +//------------------------------------------------- -static int finish_screen_updates(running_machine *machine) +void video_manager::add_sound_to_recording(const INT16 *sound, int numsamples) { - bool anything_changed = false; - - /* finish updating the screens */ - for (screen_device *screen = screen_first(*machine); screen != NULL; screen = screen_next(screen)) - screen->update_partial(screen->visible_area().max_y); - - /* now add the quads for all the screens */ - for (screen_device *screen = screen_first(*machine); screen != NULL; screen = screen_next(screen)) - if (screen->update_quads()) - anything_changed = true; - - /* update our movie recording and burn-in state */ - if (!machine->paused()) + // only record if we have a file + if (m_avifile != NULL) { - video_mng_record_frame(machine); - video_avi_record_frame(machine); - - /* iterate over screens and update the burnin for the ones that care */ - for (screen_device *screen = screen_first(*machine); screen != NULL; screen = screen_next(screen)) - screen->update_burnin(); - } + g_profiler.start(PROFILER_MOVIE_REC); - /* draw any crosshairs */ - for (screen_device *screen = screen_first(*machine); screen != NULL; screen = screen_next(screen)) - crosshair_render(*screen); + // write the next frame + avi_error avierr = avi_append_sound_samples(m_avifile, 0, sound + 0, numsamples, 1); + if (avierr == AVIERR_NONE) + avierr = avi_append_sound_samples(m_avifile, 1, sound + 1, numsamples, 1); + if (avierr != AVIERR_NONE) + end_recording(); - return anything_changed; + g_profiler.stop(); + } } -/*************************************************************************** - THROTTLING/FRAMESKIPPING/PERFORMANCE -***************************************************************************/ - -/*------------------------------------------------- - video_skip_this_frame - accessor to determine - if this frame is being skipped --------------------------------------------------*/ +//------------------------------------------------- +// init_buffered_spriteram - initialize the +// double-buffered spriteram +//------------------------------------------------- -int video_skip_this_frame(void) +void video_manager::init_buffered_spriteram() { - return global.skipping_this_frame; -} + assert_always(m_machine.generic.spriteram_size != 0, "Video buffers spriteram but spriteram size is 0"); + // allocate memory for the back buffer + m_machine.generic.buffered_spriteram.u8 = auto_alloc_array(&m_machine, UINT8, m_machine.generic.spriteram_size); -/*------------------------------------------------- - video_get_speed_factor - return the speed - factor as an integer * 100 --------------------------------------------------*/ + // register for saving it + state_save_register_global_pointer(&m_machine, m_machine.generic.buffered_spriteram.u8, m_machine.generic.spriteram_size); -int video_get_speed_factor(void) -{ - return global.speed; + // do the same for the second back buffer, if present + if (m_machine.generic.spriteram2_size) + { + // allocate memory + m_machine.generic.buffered_spriteram2.u8 = auto_alloc_array(&m_machine, UINT8, m_machine.generic.spriteram2_size); + + // register for saving it + state_save_register_global_pointer(&m_machine, m_machine.generic.buffered_spriteram2.u8, m_machine.generic.spriteram2_size); + } } -/*------------------------------------------------- - video_set_speed_factor - sets the speed - factor as an integer * 100 --------------------------------------------------*/ +//------------------------------------------------- +// video_exit - close down the video system +//------------------------------------------------- -void video_set_speed_factor(int speed) +void video_manager::exit_static(running_machine &machine) { - global.speed = speed; + machine.video().exit(); } - -/*------------------------------------------------- - video_get_speed_text - print the text to - be displayed in the upper-right corner --------------------------------------------------*/ - -const char *video_get_speed_text(running_machine *machine) +void video_manager::exit() { - bool paused = machine->paused(); - static char buffer[1024]; - char *dest = buffer; + // stop recording any movie + end_recording(); - /* validate */ - assert(machine != NULL); + // free all the graphics elements + for (int i = 0; i < MAX_GFX_ELEMENTS; i++) + gfx_element_free(m_machine.gfx[i]); - /* if we're paused, just display Paused */ - if (paused) - dest += sprintf(dest, "paused"); + // free the snapshot target + m_machine.render().target_free(m_snap_target); + if (m_snap_bitmap != NULL) + global_free(m_snap_bitmap); - /* if we're fast forwarding, just display Fast-forward */ - else if (global.fastforward) - dest += sprintf(dest, "fast "); - - /* if we're auto frameskipping, display that plus the level */ - else if (effective_autoframeskip(machine)) - dest += sprintf(dest, "auto%2d/%d", effective_frameskip(), MAX_FRAMESKIP); - - /* otherwise, just display the frameskip plus the level */ - else - dest += sprintf(dest, "skip %d/%d", effective_frameskip(), MAX_FRAMESKIP); + // print a final result if we have at least 5 seconds' worth of data + if (m_overall_emutime.seconds >= 5) + { + osd_ticks_t tps = osd_ticks_per_second(); + double final_real_time = (double)m_overall_real_seconds + (double)m_overall_real_ticks / (double)tps; + double final_emu_time = attotime_to_double(m_overall_emutime); + mame_printf_info("Average speed: %.2f%% (%d seconds)\n", 100 * final_emu_time / final_real_time, attotime_add_attoseconds(m_overall_emutime, ATTOSECONDS_PER_SECOND / 2).seconds); + } +} - /* append the speed for all cases except paused */ - if (!paused) - dest += sprintf(dest, "%4d%%", (int)(100 * global.speed_percent + 0.5)); - /* display the number of partial updates as well */ - if (global.partial_updates_this_frame > 1) - dest += sprintf(dest, "\n%d partial updates", global.partial_updates_this_frame); +//------------------------------------------------- +// screenless_update_callback - update generator +// when there are no screens to drive it +//------------------------------------------------- - /* return a pointer to the static buffer */ - return buffer; +TIMER_CALLBACK( video_manager::screenless_update_callback ) +{ + // force an update + reinterpret_cast<video_manager *>(ptr)->frame_update(false); } -/*------------------------------------------------- - video_get_speed_percent - return the current - effective speed percentage --------------------------------------------------*/ +//------------------------------------------------- +// postload - callback for resetting things after +// state has been loaded +//------------------------------------------------- -double video_get_speed_percent(running_machine *machine) +void video_manager::postload() { - return global.speed_percent; + m_movie_next_frame_time = timer_get_time(&m_machine); } -/*------------------------------------------------- - video_get_frameskip - return the current - actual frameskip (-1 means autoframeskip) --------------------------------------------------*/ +//------------------------------------------------- +// effective_autoframeskip - return the effective +// autoframeskip value, accounting for fast +// forward +//------------------------------------------------- -int video_get_frameskip(void) +inline int video_manager::effective_autoframeskip() const { - /* if autoframeskip is on, return -1 */ - if (global.auto_frameskip) - return -1; + // if we're fast forwarding or paused, autoframeskip is disabled + if (m_fastforward || m_machine.paused()) + return false; - /* otherwise, return the direct level */ - else - return global.frameskip_level; + // otherwise, it's up to the user + return m_auto_frameskip; } -/*------------------------------------------------- - video_set_frameskip - set the current - actual frameskip (-1 means autoframeskip) --------------------------------------------------*/ +//------------------------------------------------- +// effective_frameskip - return the effective +// frameskip value, accounting for fast +// forward +//------------------------------------------------- -void video_set_frameskip(int frameskip) +inline int video_manager::effective_frameskip() const { - /* -1 means autoframeskip */ - if (frameskip == -1) - { - global.auto_frameskip = TRUE; - global.frameskip_level = 0; - } + // if we're fast forwarding, use the maximum frameskip + if (m_fastforward) + return FRAMESKIP_LEVELS - 1; - /* any other level is a direct control */ - else if (frameskip >= 0 && frameskip <= MAX_FRAMESKIP) - { - global.auto_frameskip = FALSE; - global.frameskip_level = frameskip; - } + // otherwise, it's up to the user + return m_frameskip_level; } -/*------------------------------------------------- - video_get_throttle - return the current - actual throttle --------------------------------------------------*/ +//------------------------------------------------- +// effective_throttle - return the effective +// throttle value, accounting for fast +// forward and user interface +//------------------------------------------------- -int video_get_throttle(void) +inline bool video_manager::effective_throttle() const { - return global.throttle; + // if we're paused, or if the UI is active, we always throttle + if (m_machine.paused() || ui_is_menu_active()) + return true; + + // if we're fast forwarding, we don't throttle + if (m_fastforward) + return false; + + // otherwise, it's up to the user + return m_throttle; } -/*------------------------------------------------- - video_set_throttle - set the current - actual throttle --------------------------------------------------*/ +//------------------------------------------------- +// original_speed_setting - return the original +// speed setting +//------------------------------------------------- -void video_set_throttle(int throttle) +inline int video_manager::original_speed_setting() const { - global.throttle = throttle; + return options_get_float(mame_options(), OPTION_SPEED) * 100.0 + 0.5; } -/*------------------------------------------------- - video_get_fastforward - return the current - fastforward value --------------------------------------------------*/ +//------------------------------------------------- +// finish_screen_updates - finish updating all +// the screens +//------------------------------------------------- -int video_get_fastforward(void) +bool video_manager::finish_screen_updates() { - return global.fastforward; -} + // finish updating the screens + for (screen_device *screen = m_machine.first_screen(); screen != NULL; screen = screen->next_screen()) + screen->update_partial(screen->visible_area().max_y); + // now add the quads for all the screens + bool anything_changed = false; + for (screen_device *screen = m_machine.first_screen(); screen != NULL; screen = screen->next_screen()) + if (screen->update_quads()) + anything_changed = true; -/*------------------------------------------------- - video_set_fastforward - set the current - fastforward value --------------------------------------------------*/ + // update our movie recording and burn-in state + if (!m_machine.paused()) + { + record_frame(); -void video_set_fastforward(int _fastforward) -{ - global.fastforward = _fastforward; + // iterate over screens and update the burnin for the ones that care + for (screen_device *screen = m_machine.first_screen(); screen != NULL; screen = screen->next_screen()) + screen->update_burnin(); + } + + // draw any crosshairs + for (screen_device *screen = m_machine.first_screen(); screen != NULL; screen = screen->next_screen()) + crosshair_render(*screen); + + return anything_changed; } -/*------------------------------------------------- - update_throttle - throttle to the game's - natural speed --------------------------------------------------*/ -static void update_throttle(running_machine *machine, attotime emutime) +//------------------------------------------------- +// update_throttle - throttle to the game's +// natural speed +//------------------------------------------------- + +void video_manager::update_throttle(attotime emutime) { /* @@ -764,163 +764,153 @@ static void update_throttle(running_machine *machine, attotime emutime) 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 4,5,5,6,5,6,6,7, 5,6,6,7,6,7,7,8 }; - attoseconds_t real_delta_attoseconds; - attoseconds_t emu_delta_attoseconds; - attoseconds_t real_is_ahead_attoseconds; - attoseconds_t attoseconds_per_tick; - osd_ticks_t ticks_per_second; - osd_ticks_t target_ticks; - osd_ticks_t diff_ticks; - - /* apply speed factor to emu time */ - if (global.speed != 0 && global.speed != 100) + + // outer scope so we can break out in case of a resync + while (1) { - /* multiply emutime by 100, then divide by the global speed factor */ - emutime = attotime_div(attotime_mul(emutime, 100), global.speed); - } + // apply speed factor to emu time + if (m_speed != 0 && m_speed != 100) + { + // multiply emutime by 100, then divide by the global speed factor + emutime = attotime_div(attotime_mul(emutime, 100), m_speed); + } - /* compute conversion factors up front */ - ticks_per_second = osd_ticks_per_second(); - attoseconds_per_tick = ATTOSECONDS_PER_SECOND / ticks_per_second; + // compute conversion factors up front + osd_ticks_t ticks_per_second = osd_ticks_per_second(); + attoseconds_t attoseconds_per_tick = ATTOSECONDS_PER_SECOND / ticks_per_second; - /* if we're paused, emutime will not advance; instead, we subtract a fixed - amount of time (1/60th of a second) from the emulated time that was passed in, - and explicitly reset our tracked real and emulated timers to that value ... - this means we pretend that the last update was exactly 1/60th of a second - ago, and was in sync in both real and emulated time */ - if (machine->paused()) - { - global.throttle_emutime = attotime_sub_attoseconds(emutime, ATTOSECONDS_PER_SECOND / PAUSED_REFRESH_RATE); - global.throttle_realtime = global.throttle_emutime; - } + // if we're paused, emutime will not advance; instead, we subtract a fixed + // amount of time (1/60th of a second) from the emulated time that was passed in, + // and explicitly reset our tracked real and emulated timers to that value ... + // this means we pretend that the last update was exactly 1/60th of a second + // ago, and was in sync in both real and emulated time + if (m_machine.paused()) + { + m_throttle_emutime = attotime_sub_attoseconds(emutime, ATTOSECONDS_PER_SECOND / PAUSED_REFRESH_RATE); + m_throttle_realtime = m_throttle_emutime; + } - /* attempt to detect anomalies in the emulated time by subtracting the previously - reported value from our current value; this should be a small value somewhere - between 0 and 1/10th of a second ... anything outside of this range is obviously - wrong and requires a resync */ - emu_delta_attoseconds = attotime_to_attoseconds(attotime_sub(emutime, global.throttle_emutime)); - if (emu_delta_attoseconds < 0 || emu_delta_attoseconds > ATTOSECONDS_PER_SECOND / 10) - { - if (LOG_THROTTLE) - logerror("Resync due to weird emutime delta: %s\n", attotime_string(attotime_make(0, emu_delta_attoseconds), 18)); - goto resync; - } + // attempt to detect anomalies in the emulated time by subtracting the previously + // reported value from our current value; this should be a small value somewhere + // between 0 and 1/10th of a second ... anything outside of this range is obviously + // wrong and requires a resync + attoseconds_t emu_delta_attoseconds = attotime_to_attoseconds(attotime_sub(emutime, m_throttle_emutime)); + if (emu_delta_attoseconds < 0 || emu_delta_attoseconds > ATTOSECONDS_PER_SECOND / 10) + { + if (LOG_THROTTLE) + logerror("Resync due to weird emutime delta: %s\n", attotime_string(attotime_make(0, emu_delta_attoseconds), 18)); + break; + } - /* now determine the current real time in OSD-specified ticks; we have to be careful - here because counters can wrap, so we only use the difference between the last - read value and the current value in our computations */ - diff_ticks = osd_ticks() - global.throttle_last_ticks; - global.throttle_last_ticks += diff_ticks; + // now determine the current real time in OSD-specified ticks; we have to be careful + // here because counters can wrap, so we only use the difference between the last + // read value and the current value in our computations + osd_ticks_t diff_ticks = osd_ticks() - m_throttle_last_ticks; + m_throttle_last_ticks += diff_ticks; - /* if it has been more than a full second of real time since the last call to this - function, we just need to resynchronize */ - if (diff_ticks >= ticks_per_second) - { - if (LOG_THROTTLE) - logerror("Resync due to real time advancing by more than 1 second\n"); - goto resync; - } + // if it has been more than a full second of real time since the last call to this + // function, we just need to resynchronize + if (diff_ticks >= ticks_per_second) + { + if (LOG_THROTTLE) + logerror("Resync due to real time advancing by more than 1 second\n"); + break; + } - /* convert this value into attoseconds for easier comparison */ - real_delta_attoseconds = diff_ticks * attoseconds_per_tick; + // convert this value into attoseconds for easier comparison + attoseconds_t real_delta_attoseconds = diff_ticks * attoseconds_per_tick; - /* now update our real and emulated timers with the current values */ - global.throttle_emutime = emutime; - global.throttle_realtime = attotime_add_attoseconds(global.throttle_realtime, real_delta_attoseconds); + // now update our real and emulated timers with the current values + m_throttle_emutime = emutime; + m_throttle_realtime = attotime_add_attoseconds(m_throttle_realtime, real_delta_attoseconds); - /* keep a history of whether or not emulated time beat real time over the last few - updates; this can be used for future heuristics */ - global.throttle_history = (global.throttle_history << 1) | (emu_delta_attoseconds > real_delta_attoseconds); + // keep a history of whether or not emulated time beat real time over the last few + // updates; this can be used for future heuristics + m_throttle_history = (m_throttle_history << 1) | (emu_delta_attoseconds > real_delta_attoseconds); - /* determine how far ahead real time is versus emulated time; note that we use the - accumulated times for this instead of the deltas for the current update because - we want to track time over a longer duration than a single update */ - real_is_ahead_attoseconds = attotime_to_attoseconds(attotime_sub(global.throttle_emutime, global.throttle_realtime)); + // determine how far ahead real time is versus emulated time; note that we use the + // accumulated times for this instead of the deltas for the current update because + // we want to track time over a longer duration than a single update + attoseconds_t real_is_ahead_attoseconds = attotime_to_attoseconds(attotime_sub(m_throttle_emutime, m_throttle_realtime)); - /* if we're more than 1/10th of a second out, or if we are behind at all and emulation - is taking longer than the real frame, we just need to resync */ - if (real_is_ahead_attoseconds < -ATTOSECONDS_PER_SECOND / 10 || - (real_is_ahead_attoseconds < 0 && popcount[global.throttle_history & 0xff] < 6)) - { - if (LOG_THROTTLE) - logerror("Resync due to being behind: %s (history=%08X)\n", attotime_string(attotime_make(0, -real_is_ahead_attoseconds), 18), global.throttle_history); - goto resync; - } + // if we're more than 1/10th of a second out, or if we are behind at all and emulation + // is taking longer than the real frame, we just need to resync + if (real_is_ahead_attoseconds < -ATTOSECONDS_PER_SECOND / 10 || + (real_is_ahead_attoseconds < 0 && popcount[m_throttle_history & 0xff] < 6)) + { + if (LOG_THROTTLE) + logerror("Resync due to being behind: %s (history=%08X)\n", attotime_string(attotime_make(0, -real_is_ahead_attoseconds), 18), m_throttle_history); + break; + } - /* if we're behind, it's time to just get out */ - if (real_is_ahead_attoseconds < 0) - return; + // if we're behind, it's time to just get out + if (real_is_ahead_attoseconds < 0) + return; - /* compute the target real time, in ticks, where we want to be */ - target_ticks = global.throttle_last_ticks + real_is_ahead_attoseconds / attoseconds_per_tick; + // compute the target real time, in ticks, where we want to be + osd_ticks_t target_ticks = m_throttle_last_ticks + real_is_ahead_attoseconds / attoseconds_per_tick; - /* throttle until we read the target, and update real time to match the final time */ - diff_ticks = throttle_until_ticks(machine, target_ticks) - global.throttle_last_ticks; - global.throttle_last_ticks += diff_ticks; - global.throttle_realtime = attotime_add_attoseconds(global.throttle_realtime, diff_ticks * attoseconds_per_tick); - return; + // throttle until we read the target, and update real time to match the final time + diff_ticks = throttle_until_ticks(target_ticks) - m_throttle_last_ticks; + m_throttle_last_ticks += diff_ticks; + m_throttle_realtime = attotime_add_attoseconds(m_throttle_realtime, diff_ticks * attoseconds_per_tick); + return; + } -resync: - /* reset realtime and emutime to the same value */ - global.throttle_realtime = global.throttle_emutime = emutime; + // reset realtime and emutime to the same value + m_throttle_realtime = m_throttle_emutime = emutime; } -/*------------------------------------------------- - throttle_until_ticks - spin until the - specified target time, calling the OSD code - to sleep if possible --------------------------------------------------*/ +//------------------------------------------------- +// throttle_until_ticks - spin until the +// specified target time, calling the OSD code +// to sleep if possible +//------------------------------------------------- -static osd_ticks_t throttle_until_ticks(running_machine *machine, osd_ticks_t target_ticks) +osd_ticks_t video_manager::throttle_until_ticks(osd_ticks_t target_ticks) { - osd_ticks_t minimum_sleep = osd_ticks_per_second() / 1000; - osd_ticks_t current_ticks = osd_ticks(); - osd_ticks_t new_ticks; - int allowed_to_sleep = FALSE; - - /* we're allowed to sleep via the OSD code only if we're configured to do so - and we're not frameskipping due to autoframeskip, or if we're paused */ - if (options_get_bool(machine->options(), OPTION_SLEEP) && (!effective_autoframeskip(machine) || effective_frameskip() == 0)) - allowed_to_sleep = TRUE; - if (machine->paused()) - allowed_to_sleep = TRUE; + // we're allowed to sleep via the OSD code only if we're configured to do so + // and we're not frameskipping due to autoframeskip, or if we're paused + bool allowed_to_sleep = false; + if (options_get_bool(m_machine.options(), OPTION_SLEEP) && (!effective_autoframeskip() || effective_frameskip() == 0)) + allowed_to_sleep = true; + if (m_machine.paused()) + allowed_to_sleep = true; - /* loop until we reach our target */ + // loop until we reach our target g_profiler.start(PROFILER_IDLE); + osd_ticks_t minimum_sleep = osd_ticks_per_second() / 1000; + osd_ticks_t current_ticks = osd_ticks(); while (current_ticks < target_ticks) { - osd_ticks_t delta; - int slept = FALSE; - - /* compute how much time to sleep for, taking into account the average oversleep */ - delta = (target_ticks - current_ticks) * 1000 / (1000 + global.average_oversleep); + // compute how much time to sleep for, taking into account the average oversleep + osd_ticks_t delta = (target_ticks - current_ticks) * 1000 / (1000 + m_average_oversleep); - /* see if we can sleep */ + // see if we can sleep + bool slept = false; if (allowed_to_sleep && delta >= minimum_sleep) { osd_sleep(delta); - slept = TRUE; + slept = true; } - /* read the new value */ - new_ticks = osd_ticks(); + // read the new value + osd_ticks_t new_ticks = osd_ticks(); - /* keep some metrics on the sleeping patterns of the OSD layer */ + // keep some metrics on the sleeping patterns of the OSD layer if (slept) { + // if we overslept, keep an average of the amount osd_ticks_t actual_ticks = new_ticks - current_ticks; - - /* if we overslept, keep an average of the amount */ if (actual_ticks > delta) { + // take 90% of the previous average plus 10% of the new value osd_ticks_t oversleep_milliticks = 1000 * (actual_ticks - delta) / delta; - - /* take 90% of the previous average plus 10% of the new value */ - global.average_oversleep = (global.average_oversleep * 99 + oversleep_milliticks) / 100; + m_average_oversleep = (m_average_oversleep * 99 + oversleep_milliticks) / 100; if (LOG_THROTTLE) - logerror("Slept for %d ticks, got %d ticks, avgover = %d\n", (int)delta, (int)actual_ticks, (int)global.average_oversleep); + logerror("Slept for %d ticks, got %d ticks, avgover = %d\n", (int)delta, (int)actual_ticks, (int)m_average_oversleep); } } current_ticks = new_ticks; @@ -931,735 +921,325 @@ static osd_ticks_t throttle_until_ticks(running_machine *machine, osd_ticks_t ta } -/*------------------------------------------------- - update_frameskip - update frameskipping - counters and periodically update autoframeskip --------------------------------------------------*/ +//------------------------------------------------- +// update_frameskip - update frameskipping +// counters and periodically update autoframeskip +//------------------------------------------------- -static void update_frameskip(running_machine *machine) +void video_manager::update_frameskip() { - /* if we're throttling and autoframeskip is on, adjust */ - if (effective_throttle(machine) && effective_autoframeskip(machine) && global.frameskip_counter == 0) + // if we're throttling and autoframeskip is on, adjust + if (effective_throttle() && effective_autoframeskip() && m_frameskip_counter == 0) { - double speed = global.speed * 0.01; - - /* if we're too fast, attempt to increase the frameskip */ - if (global.speed_percent >= 0.995 * speed) + // if we're too fast, attempt to increase the frameskip + double speed = m_speed * 0.01; + if (m_speed_percent >= 0.995 * speed) { - /* but only after 3 consecutive frames where we are too fast */ - if (++global.frameskip_adjust >= 3) + // but only after 3 consecutive frames where we are too fast + if (++m_frameskip_adjust >= 3) { - global.frameskip_adjust = 0; - if (global.frameskip_level > 0) - global.frameskip_level--; + m_frameskip_adjust = 0; + if (m_frameskip_level > 0) + m_frameskip_level--; } } - /* if we're too slow, attempt to increase the frameskip */ + // if we're too slow, attempt to increase the frameskip else { - /* if below 80% speed, be more aggressive */ - if (global.speed_percent < 0.80 * speed) - global.frameskip_adjust -= (0.90 * speed - global.speed_percent) / 0.05; + // if below 80% speed, be more aggressive + if (m_speed_percent < 0.80 * speed) + m_frameskip_adjust -= (0.90 * speed - m_speed_percent) / 0.05; - /* if we're close, only force it up to frameskip 8 */ - else if (global.frameskip_level < 8) - global.frameskip_adjust--; + // if we're close, only force it up to frameskip 8 + else if (m_frameskip_level < 8) + m_frameskip_adjust--; - /* perform the adjustment */ - while (global.frameskip_adjust <= -2) + // perform the adjustment + while (m_frameskip_adjust <= -2) { - global.frameskip_adjust += 2; - if (global.frameskip_level < MAX_FRAMESKIP) - global.frameskip_level++; + m_frameskip_adjust += 2; + if (m_frameskip_level < MAX_FRAMESKIP) + m_frameskip_level++; } } } - /* increment the frameskip counter and determine if we will skip the next frame */ - global.frameskip_counter = (global.frameskip_counter + 1) % FRAMESKIP_LEVELS; - global.skipping_this_frame = skiptable[effective_frameskip()][global.frameskip_counter]; + // increment the frameskip counter and determine if we will skip the next frame + m_frameskip_counter = (m_frameskip_counter + 1) % FRAMESKIP_LEVELS; + m_skipping_this_frame = s_skiptable[effective_frameskip()][m_frameskip_counter]; } -/*------------------------------------------------- - update_refresh_speed - update the global.speed - based on the maximum refresh rate supported --------------------------------------------------*/ +//------------------------------------------------- +// update_refresh_speed - update the m_speed +// based on the maximum refresh rate supported +//------------------------------------------------- -static void update_refresh_speed(running_machine *machine) +void video_manager::update_refresh_speed() { - /* only do this if the refreshspeed option is used */ - if (options_get_bool(machine->options(), OPTION_REFRESHSPEED)) + // only do this if the refreshspeed option is used + if (options_get_bool(m_machine.options(), OPTION_REFRESHSPEED)) { - float minrefresh = machine->render().max_update_rate(); + float minrefresh = m_machine.render().max_update_rate(); if (minrefresh != 0) { + // find the screen with the shortest frame period (max refresh rate) + // note that we first check the token since this can get called before all screens are created attoseconds_t min_frame_period = ATTOSECONDS_PER_SECOND; - UINT32 original_speed = original_speed_setting(); - UINT32 target_speed; - - /* find the screen with the shortest frame period (max refresh rate) */ - /* note that we first check the token since this can get called before all screens are created */ - for (screen_device *screen = screen_first(*machine); screen != NULL; screen = screen_next(screen)) + for (screen_device *screen = m_machine.first_screen(); screen != NULL; screen = screen->next_screen()) { attoseconds_t period = screen->frame_period().attoseconds; if (period != 0) min_frame_period = MIN(min_frame_period, period); } - /* compute a target speed as an integral percentage */ - /* note that we lop 0.25Hz off of the minrefresh when doing the computation to allow for - the fact that most refresh rates are not accurate to 10 digits... */ - target_speed = floor((minrefresh - 0.25f) * 100.0 / ATTOSECONDS_TO_HZ(min_frame_period)); + // compute a target speed as an integral percentage + // note that we lop 0.25Hz off of the minrefresh when doing the computation to allow for + // the fact that most refresh rates are not accurate to 10 digits... + UINT32 target_speed = floor((minrefresh - 0.25f) * 100.0 / ATTOSECONDS_TO_HZ(min_frame_period)); + UINT32 original_speed = original_speed_setting(); target_speed = MIN(target_speed, original_speed); - /* if we changed, log that verbosely */ - if (target_speed != global.speed) + // if we changed, log that verbosely + if (target_speed != m_speed) { mame_printf_verbose("Adjusting target speed to %d%% (hw=%.2fHz, game=%.2fHz, adjusted=%.2fHz)\n", target_speed, minrefresh, ATTOSECONDS_TO_HZ(min_frame_period), ATTOSECONDS_TO_HZ(min_frame_period * 100 / target_speed)); - global.speed = target_speed; + m_speed = target_speed; } } } } -/*------------------------------------------------- - recompute_speed - recompute the current - overall speed; we assume this is called only - if we did not skip a frame --------------------------------------------------*/ +//------------------------------------------------- +// recompute_speed - recompute the current +// overall speed; we assume this is called only +// if we did not skip a frame +//------------------------------------------------- -static void recompute_speed(running_machine *machine, attotime emutime) +void video_manager::recompute_speed(attotime emutime) { - attoseconds_t delta_emutime; - - /* if we don't have a starting time yet, or if we're paused, reset our starting point */ - if (global.speed_last_realtime == 0 || machine->paused()) + // if we don't have a starting time yet, or if we're paused, reset our starting point + if (m_speed_last_realtime == 0 || m_machine.paused()) { - global.speed_last_realtime = osd_ticks(); - global.speed_last_emutime = emutime; + m_speed_last_realtime = osd_ticks(); + m_speed_last_emutime = emutime; } - /* if it has been more than the update interval, update the time */ - delta_emutime = attotime_to_attoseconds(attotime_sub(emutime, global.speed_last_emutime)); + // if it has been more than the update interval, update the time + attoseconds_t delta_emutime = attotime_to_attoseconds(attotime_sub(emutime, m_speed_last_emutime)); if (delta_emutime > SUBSECONDS_PER_SPEED_UPDATE) { + // convert from ticks to attoseconds osd_ticks_t realtime = osd_ticks(); - osd_ticks_t delta_realtime = realtime - global.speed_last_realtime; + osd_ticks_t delta_realtime = realtime - m_speed_last_realtime; osd_ticks_t tps = osd_ticks_per_second(); + m_speed_percent = (double)delta_emutime * (double)tps / ((double)delta_realtime * (double)ATTOSECONDS_PER_SECOND); - /* convert from ticks to attoseconds */ - global.speed_percent = (double)delta_emutime * (double)tps / ((double)delta_realtime * (double)ATTOSECONDS_PER_SECOND); - - /* remember the last times */ - global.speed_last_realtime = realtime; - global.speed_last_emutime = emutime; + // remember the last times + m_speed_last_realtime = realtime; + m_speed_last_emutime = emutime; - /* if we're throttled, this time period counts for overall speed; otherwise, we reset the counter */ - if (!global.fastforward) - global.overall_valid_counter++; + // if we're throttled, this time period counts for overall speed; otherwise, we reset the counter + if (!m_fastforward) + m_overall_valid_counter++; else - global.overall_valid_counter = 0; + m_overall_valid_counter = 0; - /* if we've had at least 4 consecutive valid periods, accumulate stats */ - if (global.overall_valid_counter >= 4) + // if we've had at least 4 consecutive valid periods, accumulate stats + if (m_overall_valid_counter >= 4) { - global.overall_real_ticks += delta_realtime; - while (global.overall_real_ticks >= tps) + m_overall_real_ticks += delta_realtime; + while (m_overall_real_ticks >= tps) { - global.overall_real_ticks -= tps; - global.overall_real_seconds++; + m_overall_real_ticks -= tps; + m_overall_real_seconds++; } - global.overall_emutime = attotime_add_attoseconds(global.overall_emutime, delta_emutime); + m_overall_emutime = attotime_add_attoseconds(m_overall_emutime, delta_emutime); } } - /* if we're past the "time-to-execute" requested, signal an exit */ - if (global.seconds_to_run != 0 && emutime.seconds >= global.seconds_to_run) + // if we're past the "time-to-execute" requested, signal an exit + if (m_seconds_to_run != 0 && emutime.seconds >= m_seconds_to_run) { - if (machine->primary_screen != NULL) + if (m_machine.primary_screen != NULL) { - astring fname(machine->basename(), PATH_SEPARATOR "final.png"); - file_error filerr; - mame_file *file; + astring fname(m_machine.basename(), PATH_SEPARATOR "final.png"); - /* create a final screenshot */ - filerr = mame_fopen(SEARCHPATH_SCREENSHOT, fname, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &file); + // create a final screenshot + mame_file *file; + file_error filerr = mame_fopen(SEARCHPATH_SCREENSHOT, fname, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &file); if (filerr == FILERR_NONE) { - screen_save_snapshot(machine, machine->primary_screen, file); + save_snapshot(m_machine.primary_screen, *file); mame_fclose(file); } } - /* schedule our demise */ - machine->schedule_exit(); - } -} - - - -/*************************************************************************** - SCREEN SNAPSHOTS -***************************************************************************/ - -/*------------------------------------------------- - screen_save_snapshot - save a snapshot - to the given file handle --------------------------------------------------*/ - -void screen_save_snapshot(running_machine *machine, device_t *screen, mame_file *fp) -{ - png_info pnginfo = { 0 }; - const rgb_t *palette; - png_error error; - char text[256]; - - /* validate */ - assert(!global.snap_native || screen != NULL); - assert(fp != NULL); - - /* create the bitmap to pass in */ - create_snapshot_bitmap(screen); - - /* add two text entries describing the image */ - sprintf(text, APPNAME " %s", build_version); - png_add_text(&pnginfo, "Software", text); - sprintf(text, "%s %s", machine->gamedrv->manufacturer, machine->gamedrv->description); - png_add_text(&pnginfo, "System", text); - - /* now do the actual work */ - palette = (machine->palette != NULL) ? palette_entry_list_adjusted(machine->palette) : NULL; - error = png_write_bitmap(mame_core_file(fp), &pnginfo, global.snap_bitmap, machine->total_colors(), palette); - - /* free any data allocated */ - png_free(&pnginfo); -} - - -/*------------------------------------------------- - video_save_active_screen_snapshots - save a - snapshot of all active screens --------------------------------------------------*/ - -void video_save_active_screen_snapshots(running_machine *machine) -{ - mame_file *fp; - - /* validate */ - assert(machine != NULL); - assert(machine->config != NULL); - - /* if we're native, then write one snapshot per visible screen */ - if (global.snap_native) - { - /* write one snapshot per visible screen */ - for (screen_device *screen = screen_first(*machine); screen != NULL; screen = screen_next(screen)) - if (machine->render().is_live(*screen)) - { - file_error filerr = mame_fopen_next(machine, SEARCHPATH_SCREENSHOT, "png", &fp); - if (filerr == FILERR_NONE) - { - screen_save_snapshot(machine, screen, fp); - mame_fclose(fp); - } - } - } - - /* otherwise, just write a single snapshot */ - else - { - file_error filerr = mame_fopen_next(machine, SEARCHPATH_SCREENSHOT, "png", &fp); - if (filerr == FILERR_NONE) - { - screen_save_snapshot(machine, NULL, fp); - mame_fclose(fp); - } + // schedule our demise + m_machine.schedule_exit(); } } -/*------------------------------------------------- - creare_snapshot_bitmap - creates a - bitmap containing the screenshot for the - given screen --------------------------------------------------*/ +//------------------------------------------------- +// create_snapshot_bitmap - creates a +// bitmap containing the screenshot for the +// given screen +//------------------------------------------------- -static void create_snapshot_bitmap(device_t *screen) +void video_manager::create_snapshot_bitmap(device_t *screen) { - INT32 width, height; - int view_index; - - /* select the appropriate view in our dummy target */ - if (global.snap_native && screen != NULL) + // select the appropriate view in our dummy target + if (m_snap_native && screen != NULL) { - view_index = screen->machine->m_devicelist.index(SCREEN, screen->tag()); + int view_index = m_machine.m_devicelist.index(SCREEN, screen->tag()); assert(view_index != -1); - global.snap_target->set_view(view_index); + m_snap_target->set_view(view_index); } - /* get the minimum width/height and set it on the target */ - width = global.snap_width; - height = global.snap_height; + // get the minimum width/height and set it on the target + INT32 width = m_snap_width; + INT32 height = m_snap_height; if (width == 0 || height == 0) - global.snap_target->compute_minimum_size(width, height); - global.snap_target->set_bounds(width, height); + m_snap_target->compute_minimum_size(width, height); + m_snap_target->set_bounds(width, height); - /* if we don't have a bitmap, or if it's not the right size, allocate a new one */ - if (global.snap_bitmap == NULL || width != global.snap_bitmap->width || height != global.snap_bitmap->height) + // if we don't have a bitmap, or if it's not the right size, allocate a new one + if (m_snap_bitmap == NULL || width != m_snap_bitmap->width || height != m_snap_bitmap->height) { - if (global.snap_bitmap != NULL) - global_free(global.snap_bitmap); - global.snap_bitmap = global_alloc(bitmap_t(width, height, BITMAP_FORMAT_RGB32)); - assert(global.snap_bitmap != NULL); + if (m_snap_bitmap != NULL) + auto_free(&m_machine, m_snap_bitmap); + m_snap_bitmap = auto_alloc(&m_machine, bitmap_t(width, height, BITMAP_FORMAT_RGB32)); } - /* render the screen there */ - render_primitive_list &primlist = global.snap_target->get_primitives(); + // render the screen there + render_primitive_list &primlist = m_snap_target->get_primitives(); primlist.acquire_lock(); - rgb888_draw_primitives(primlist, global.snap_bitmap->base, width, height, global.snap_bitmap->rowpixels); + rgb888_draw_primitives(primlist, m_snap_bitmap->base, width, height, m_snap_bitmap->rowpixels); primlist.release_lock(); } -/*------------------------------------------------- - mame_fopen_next - open the next non-existing - file of type filetype according to our - numbering scheme --------------------------------------------------*/ +//------------------------------------------------- +// mame_fopen_next - open the next non-existing +// file of type filetype according to our +// numbering scheme +//------------------------------------------------- -static file_error mame_fopen_next(running_machine *machine, const char *pathoption, const char *extension, mame_file **file) +file_error video_manager::mame_fopen_next(const char *pathoption, const char *extension, mame_file *&file) { - const char *snapname = options_get_string(machine->options(), OPTION_SNAPNAME); - file_error filerr; - astring snapstr; - astring fname; - int index; - - /* handle defaults */ + // handle defaults + const char *snapname = options_get_string(m_machine.options(), OPTION_SNAPNAME); if (snapname == NULL || snapname[0] == 0) snapname = "%g/%i"; - snapstr.cpy(snapname); + astring snapstr(snapname); - /* strip any extension in the provided name and add our own */ - index = snapstr.rchr(0, '.'); + // strip any extension in the provided name and add our own + int index = snapstr.rchr(0, '.'); if (index != -1) snapstr.substr(0, index); snapstr.cat(".").cat(extension); - /* substitute path and gamename up front */ + // substitute path and gamename up front snapstr.replace(0, "/", PATH_SEPARATOR); - snapstr.replace(0, "%g", machine->basename()); + snapstr.replace(0, "%g", m_machine.basename()); - /* determine if the template has an index; if not, we always use the same name */ + // determine if the template has an index; if not, we always use the same name + astring fname; if (snapstr.find(0, "%i") == -1) fname.cpy(snapstr); - /* otherwise, we scan for the next available filename */ + // otherwise, we scan for the next available filename else { - int seq; - - /* try until we succeed */ - for (seq = 0; ; seq++) + // try until we succeed + astring seqtext; + for (int seq = 0; ; seq++) { - char seqtext[10]; - - /* make text for the sequence number */ - sprintf(seqtext, "%04d", seq); + // build up the filename + fname.cpy(snapstr).replace(0, "%i", seqtext.format("%04d", seq).cstr()); - /* build up the filename */ - fname.cpy(snapstr).replace(0, "%i", seqtext); - - /* try to open the file; stop when we fail */ - filerr = mame_fopen(pathoption, fname, OPEN_FLAG_READ, file); + // try to open the file; stop when we fail + file_error filerr = mame_fopen(pathoption, fname, OPEN_FLAG_READ, &file); if (filerr != FILERR_NONE) break; - mame_fclose(*file); + mame_fclose(file); } } - /* create the final file */ - return mame_fopen(pathoption, fname, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, file); + // create the final file + return mame_fopen(pathoption, fname, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &file); } -/*************************************************************************** - MNG MOVIE RECORDING -***************************************************************************/ - -/*------------------------------------------------- - video_mng_is_movie_active - return true if a - MNG movie is currently being recorded --------------------------------------------------*/ - -int video_mng_is_movie_active(running_machine *machine) -{ - return (global.mngfile != NULL); -} - - -/*------------------------------------------------- - video_mng_begin_recording - begin recording - of a MNG movie --------------------------------------------------*/ +//------------------------------------------------- +// record_frame - record a frame of a movie +//------------------------------------------------- -void video_mng_begin_recording(running_machine *machine, const char *name) +void video_manager::record_frame() { - file_error filerr; - png_error pngerr; - int rate; - - /* close any existing movie file */ - if (global.mngfile != NULL) - video_mng_end_recording(machine); - - /* create a snapshot bitmap so we know what the target size is */ - create_snapshot_bitmap(NULL); - - /* create a new movie file and start recording */ - if (name != NULL) - filerr = mame_fopen(SEARCHPATH_MOVIE, name, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &global.mngfile); - else - filerr = mame_fopen_next(machine, SEARCHPATH_MOVIE, "mng", &global.mngfile); - - /* start the capture */ - rate = (machine->primary_screen != NULL) ? ATTOSECONDS_TO_HZ(machine->primary_screen->frame_period().attoseconds) : screen_device::k_default_frame_rate; - pngerr = mng_capture_start(mame_core_file(global.mngfile), global.snap_bitmap, rate); - if (pngerr != PNGERR_NONE) - { - video_mng_end_recording(machine); + // ignore if nothing to do + if (m_mngfile == NULL && m_avifile == NULL) return; - } - - /* compute the frame time */ - global.movie_next_frame_time = timer_get_time(machine); - global.movie_frame_period = ATTOTIME_IN_HZ(rate); - global.movie_frame = 0; -} - -/*------------------------------------------------- - video_mng_end_recording - stop recording of - a MNG movie --------------------------------------------------*/ - -void video_mng_end_recording(running_machine *machine) -{ - /* close the file if it exists */ - if (global.mngfile != NULL) - { - mng_capture_stop(mame_core_file(global.mngfile)); - mame_fclose(global.mngfile); - global.mngfile = NULL; - global.movie_frame = 0; - } -} - - -/*------------------------------------------------- - video_mng_record_frame - record a frame of a - movie --------------------------------------------------*/ - -static void video_mng_record_frame(running_machine *machine) -{ - /* only record if we have a file */ - if (global.mngfile != NULL) - { - attotime curtime = timer_get_time(machine); - png_info pnginfo = { 0 }; - png_error error; - - g_profiler.start(PROFILER_MOVIE_REC); - - /* create the bitmap */ - create_snapshot_bitmap(NULL); - - /* loop until we hit the right time */ - while (attotime_compare(global.movie_next_frame_time, curtime) <= 0) - { - const rgb_t *palette; - - /* set up the text fields in the movie info */ - if (global.movie_frame == 0) - { - char text[256]; - - sprintf(text, APPNAME " %s", build_version); - png_add_text(&pnginfo, "Software", text); - sprintf(text, "%s %s", machine->gamedrv->manufacturer, machine->gamedrv->description); - png_add_text(&pnginfo, "System", text); - } - - /* write the next frame */ - palette = (machine->palette != NULL) ? palette_entry_list_adjusted(machine->palette) : NULL; - error = mng_capture_frame(mame_core_file(global.mngfile), &pnginfo, global.snap_bitmap, machine->total_colors(), palette); - png_free(&pnginfo); - if (error != PNGERR_NONE) - { - video_mng_end_recording(machine); - break; - } - - /* advance time */ - global.movie_next_frame_time = attotime_add(global.movie_next_frame_time, global.movie_frame_period); - global.movie_frame++; - } - - g_profiler.stop(); - } -} - - - -/*************************************************************************** - AVI MOVIE RECORDING -***************************************************************************/ + // start the profiler and get the current time + g_profiler.start(PROFILER_MOVIE_REC); + attotime curtime = timer_get_time(&m_machine); -/*------------------------------------------------- - video_avi_begin_recording - begin recording - of an AVI movie --------------------------------------------------*/ - -void video_avi_begin_recording(running_machine *machine, const char *name) -{ - avi_movie_info info; - mame_file *tempfile; - file_error filerr; - avi_error avierr; - - /* close any existing movie file */ - if (global.avifile != NULL) - video_avi_end_recording(machine); - - /* create a snapshot bitmap so we know what the target size is */ + // create the bitmap create_snapshot_bitmap(NULL); - /* build up information about this new movie */ - info.video_format = 0; - info.video_timescale = 1000 * ((machine->primary_screen != NULL) ? ATTOSECONDS_TO_HZ(machine->primary_screen->frame_period().attoseconds) : screen_device::k_default_frame_rate); - info.video_sampletime = 1000; - info.video_numsamples = 0; - info.video_width = global.snap_bitmap->width; - info.video_height = global.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 */ - if (name != NULL) - filerr = mame_fopen(SEARCHPATH_MOVIE, name, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &tempfile); - else - filerr = mame_fopen_next(machine, SEARCHPATH_MOVIE, "avi", &tempfile); - - /* reset our tracking */ - global.movie_frame = 0; - global.movie_next_frame_time = timer_get_time(machine); - global.movie_frame_period = attotime_div(ATTOTIME_IN_SEC(1000), info.video_timescale); - - /* if we succeeded, make a copy of the name and create the real file over top */ - if (filerr == FILERR_NONE) - { - astring fullname(mame_file_full_name(tempfile)); - mame_fclose(tempfile); - - /* create the file and free the string */ - avierr = avi_create(fullname, &info, &global.avifile); - } -} - - -/*------------------------------------------------- - video_avi_end_recording - stop recording of - a avi movie --------------------------------------------------*/ - -void video_avi_end_recording(running_machine *machine) -{ - /* close the file if it exists */ - if (global.avifile != NULL) + // loop until we hit the right time + while (attotime_compare(m_movie_next_frame_time, curtime) <= 0) { - avi_close(global.avifile); - global.avifile = NULL; - global.movie_frame = 0; - } -} - - -/*------------------------------------------------- - video_avi_record_frame - record a frame of a - movie --------------------------------------------------*/ - -static void video_avi_record_frame(running_machine *machine) -{ - /* only record if we have a file */ - if (global.avifile != NULL) - { - attotime curtime = timer_get_time(machine); - avi_error avierr; - - g_profiler.start(PROFILER_MOVIE_REC); - - /* create the bitmap */ - create_snapshot_bitmap(NULL); - - /* loop until we hit the right time */ - while (attotime_compare(global.movie_next_frame_time, curtime) <= 0) + // handle an AVI recording + if (m_avifile != NULL) { - /* write the next frame */ - avierr = avi_append_video_frame_rgb32(global.avifile, global.snap_bitmap); + // write the next frame + avi_error avierr = avi_append_video_frame_rgb32(m_avifile, m_snap_bitmap); if (avierr != AVIERR_NONE) { - video_avi_end_recording(machine); - break; + g_profiler.stop(); + return end_recording(); } - - /* advance time */ - global.movie_next_frame_time = attotime_add(global.movie_next_frame_time, global.movie_frame_period); - global.movie_frame++; } - g_profiler.stop(); - } -} - - -/*------------------------------------------------- - video_avi_add_sound - add sound to an AVI - recording --------------------------------------------------*/ - -void video_avi_add_sound(running_machine *machine, const INT16 *sound, int numsamples) -{ - /* only record if we have a file */ - if (global.avifile != NULL) - { - avi_error avierr; - - g_profiler.start(PROFILER_MOVIE_REC); - - /* write the next frame */ - avierr = avi_append_sound_samples(global.avifile, 0, sound + 0, numsamples, 1); - if (avierr == AVIERR_NONE) - avierr = avi_append_sound_samples(global.avifile, 1, sound + 1, numsamples, 1); - if (avierr != AVIERR_NONE) - video_avi_end_recording(machine); - - g_profiler.stop(); - } -} - - - -/*************************************************************************** - BURN-IN GENERATION -***************************************************************************/ - - -/*************************************************************************** - CONFIGURATION HELPERS -***************************************************************************/ - -/*------------------------------------------------- - video_get_view_for_target - select a view - for a given target --------------------------------------------------*/ - -int video_get_view_for_target(running_machine *machine, render_target *target, const char *viewname, int targetindex, int numtargets) -{ - int viewindex = -1; - - /* auto view just selects the nth view */ - if (strcmp(viewname, "auto") != 0) - { - /* scan for a matching view name */ - for (viewindex = 0; ; viewindex++) + // handle a MNG recording + if (m_mngfile != NULL) { - const char *name = target->view_name(viewindex); - - /* stop scanning when we hit NULL */ - if (name == NULL) + // set up the text fields in the movie info + png_info pnginfo = { 0 }; + if (m_movie_frame == 0) { - viewindex = -1; - break; + astring text1(APPNAME, " ", build_version); + astring text2(m_machine.m_game.manufacturer, " ", m_machine.m_game.description); + png_add_text(&pnginfo, "Software", text1); + png_add_text(&pnginfo, "System", text2); } - if (mame_strnicmp(name, viewname, strlen(viewname)) == 0) - break; - } - } - /* if we don't have a match, default to the nth view */ - int scrcount = screen_count(*machine->config); - if (viewindex == -1 && scrcount > 0) - { - /* if we have enough targets to be one per screen, assign in order */ - if (numtargets >= scrcount) - { - int index = target->index() % scrcount; - screen_device *screen; - for (screen = screen_first(*machine); screen != NULL; screen = screen_next(screen)) - if (index-- == 0) - break; - - /* find the first view with this screen and this screen only */ - for (viewindex = 0; ; viewindex++) + // write the next frame + const rgb_t *palette = (m_machine.palette != NULL) ? palette_entry_list_adjusted(m_machine.palette) : NULL; + png_error error = mng_capture_frame(mame_core_file(m_mngfile), &pnginfo, m_snap_bitmap, m_machine.total_colors(), palette); + png_free(&pnginfo); + if (error != PNGERR_NONE) { - const render_screen_list &viewscreens = target->view_screens(viewindex); - if (viewscreens.count() == 1 && viewscreens.contains(*screen)) - break; - if (viewscreens.count() == 0) - { - viewindex = -1; - break; - } + g_profiler.stop(); + return end_recording(); } } - /* otherwise, find the first view that has all the screens */ - if (viewindex == -1) - { - for (viewindex = 0; ; viewindex++) - { - const render_screen_list &viewscreens = target->view_screens(viewindex); - if (viewscreens.count() == 0) - break; - if (viewscreens.count() >= scrcount) - { - screen_device *screen; - for (screen = screen_first(*machine); screen != NULL; screen = screen_next(screen)) - if (!viewscreens.contains(*screen)) - break; - if (screen == NULL) - break; - } - } - } + // advance time + m_movie_next_frame_time = attotime_add(m_movie_next_frame_time, m_movie_frame_period); + m_movie_frame++; } - - /* make sure it's a valid view */ - if (target->view_name(viewindex) == NULL) - viewindex = 0; - - return viewindex; + g_profiler.stop(); } -/*************************************************************************** - DEBUGGING HELPERS -***************************************************************************/ - /*------------------------------------------------- video_assert_out_of_range_pixels - assert if any pixels in the given bitmap contain an @@ -1672,11 +1252,11 @@ void video_assert_out_of_range_pixels(running_machine *machine, bitmap_t *bitmap int maxindex = palette_get_max_index(machine->palette); int x, y; - /* this only applies to indexed16 bitmaps */ + // this only applies to indexed16 bitmaps if (bitmap->format != BITMAP_FORMAT_INDEXED16) return; - /* iterate over rows */ + // iterate over rows for (y = 0; y < bitmap->height; y++) { UINT16 *rowbase = BITMAP_ADDR16(bitmap, y, 0); @@ -1689,1023 +1269,6 @@ void video_assert_out_of_range_pixels(running_machine *machine, bitmap_t *bitmap //************************************************************************** -// VIDEO SCREEN DEVICE CONFIGURATION -//************************************************************************** - -const attotime screen_device::k_default_frame_period = STATIC_ATTOTIME_IN_HZ(k_default_frame_rate); - - -//------------------------------------------------- -// screen_device_config - constructor -//------------------------------------------------- - -screen_device_config::screen_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock) - : device_config(mconfig, static_alloc_device_config, "Video Screen", tag, owner, clock), - m_type(SCREEN_TYPE_RASTER), - m_width(0), - m_height(0), - m_oldstyle_vblank_supplied(false), - m_refresh(0), - m_vblank(0), - m_format(BITMAP_FORMAT_INVALID), - m_xoffset(0.0f), - m_yoffset(0.0f), - m_xscale(1.0f), - m_yscale(1.0f) -{ -} - - -//------------------------------------------------- -// static_alloc_device_config - allocate a new -// configuration object -//------------------------------------------------- - -device_config *screen_device_config::static_alloc_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock) -{ - return global_alloc(screen_device_config(mconfig, tag, owner, clock)); -} - - -//------------------------------------------------- -// alloc_device - allocate a new device object -//------------------------------------------------- - -device_t *screen_device_config::alloc_device(running_machine &machine) const -{ - return auto_alloc(&machine, screen_device(machine, *this)); -} - - -//------------------------------------------------- -// static_set_format - configuration helper -// to set the bitmap format -//------------------------------------------------- - -void screen_device_config::static_set_format(device_config *device, bitmap_format format) -{ - screen_device_config *screen = downcast<screen_device_config *>(device); - screen->m_format = format; -} - - -//------------------------------------------------- -// static_set_type - configuration helper -// to set the screen type -//------------------------------------------------- - -void screen_device_config::static_set_type(device_config *device, screen_type_enum type) -{ - screen_device_config *screen = downcast<screen_device_config *>(device); - screen->m_type = type; -} - - -//------------------------------------------------- -// static_set_raw - configuration helper -// to set the raw screen parameters -//------------------------------------------------- - -void screen_device_config::static_set_raw(device_config *device, UINT32 pixclock, UINT16 htotal, UINT16 hbend, UINT16 hbstart, UINT16 vtotal, UINT16 vbend, UINT16 vbstart) -{ - screen_device_config *screen = downcast<screen_device_config *>(device); - screen->m_refresh = HZ_TO_ATTOSECONDS(pixclock) * htotal * vtotal; - screen->m_vblank = screen->m_refresh / vtotal * (vtotal - (vbstart - vbend)); - screen->m_width = htotal; - screen->m_height = vtotal; - screen->m_visarea.min_x = hbend; - screen->m_visarea.max_x = hbstart - 1; - screen->m_visarea.min_y = vbend; - screen->m_visarea.max_y = vbstart - 1; -} - - -//------------------------------------------------- -// static_set_refresh - configuration helper -// to set the refresh rate -//------------------------------------------------- - -void screen_device_config::static_set_refresh(device_config *device, attoseconds_t rate) -{ - screen_device_config *screen = downcast<screen_device_config *>(device); - screen->m_refresh = rate; -} - - -//------------------------------------------------- -// static_set_vblank_time - configuration helper -// to set the VBLANK duration -//------------------------------------------------- - -void screen_device_config::static_set_vblank_time(device_config *device, attoseconds_t time) -{ - screen_device_config *screen = downcast<screen_device_config *>(device); - screen->m_vblank = time; - screen->m_oldstyle_vblank_supplied = true; -} - - -//------------------------------------------------- -// static_set_size - configuration helper to set -// the width/height of the screen -//------------------------------------------------- - -void screen_device_config::static_set_size(device_config *device, UINT16 width, UINT16 height) -{ - screen_device_config *screen = downcast<screen_device_config *>(device); - screen->m_width = width; - screen->m_height = height; -} - - -//------------------------------------------------- -// static_set_visarea - configuration helper to -// set the visible area of the screen -//------------------------------------------------- - -void screen_device_config::static_set_visarea(device_config *device, INT16 minx, INT16 maxx, INT16 miny, INT16 maxy) -{ - screen_device_config *screen = downcast<screen_device_config *>(device); - screen->m_visarea.min_x = minx; - screen->m_visarea.max_x = maxx; - screen->m_visarea.min_y = miny; - screen->m_visarea.max_y = maxy; -} - - -//------------------------------------------------- -// static_set_default_position - configuration -// helper to set the default position and scale -// factors for the screen -//------------------------------------------------- - -void screen_device_config::static_set_default_position(device_config *device, double xscale, double xoffs, double yscale, double yoffs) -{ - screen_device_config *screen = downcast<screen_device_config *>(device); - screen->m_xscale = xscale; - screen->m_xoffset = xoffs; - screen->m_yscale = yscale; - screen->m_yoffset = yoffs; -} - - -//------------------------------------------------- -// device_validity_check - verify device -// configuration -//------------------------------------------------- - -bool screen_device_config::device_validity_check(const game_driver &driver) const -{ - bool error = false; - - // sanity check dimensions - if (m_width <= 0 || m_height <= 0) - { - mame_printf_error("%s: %s screen '%s' has invalid display dimensions\n", driver.source_file, driver.name, tag()); - error = true; - } - - // sanity check display area - if (m_type != SCREEN_TYPE_VECTOR) - { - if ((m_visarea.max_x < m_visarea.min_x) || - (m_visarea.max_y < m_visarea.min_y) || - (m_visarea.max_x >= m_width) || - (m_visarea.max_y >= m_height)) - { - mame_printf_error("%s: %s screen '%s' has an invalid display area\n", driver.source_file, driver.name, tag()); - error = true; - } - - // sanity check screen formats - if (m_format != BITMAP_FORMAT_INDEXED16 && - m_format != BITMAP_FORMAT_RGB15 && - m_format != BITMAP_FORMAT_RGB32) - { - mame_printf_error("%s: %s screen '%s' has unsupported format\n", driver.source_file, driver.name, tag()); - error = true; - } - } - - // check for zero frame rate - if (m_refresh == 0) - { - mame_printf_error("%s: %s screen '%s' has a zero refresh rate\n", driver.source_file, driver.name, tag()); - error = true; - } - return error; -} - - - -//************************************************************************** -// LIVE VIDEO SCREEN DEVICE -//************************************************************************** - -//------------------------------------------------- -// screen_device - constructor -//------------------------------------------------- - -screen_device::screen_device(running_machine &_machine, const screen_device_config &config) - : device_t(_machine, config), - m_config(config), - m_container(NULL), - m_width(m_config.m_width), - m_height(m_config.m_height), - m_visarea(m_config.m_visarea), - m_burnin(NULL), - m_curbitmap(0), - m_curtexture(0), - m_texture_format(0), - m_changed(true), - m_last_partial_scan(0), - m_screen_overlay_bitmap(NULL), - m_frame_period(m_config.m_refresh), - m_scantime(1), - m_pixeltime(1), - m_vblank_period(0), - m_vblank_start_time(attotime_zero), - m_vblank_end_time(attotime_zero), - m_vblank_begin_timer(NULL), - m_vblank_end_timer(NULL), - m_scanline0_timer(NULL), - m_scanline_timer(NULL), - m_frame_number(0), - m_callback_list(NULL) -{ - memset(m_texture, 0, sizeof(m_texture)); - memset(m_bitmap, 0, sizeof(m_bitmap)); -} - - -//------------------------------------------------- -// ~screen_device - destructor -//------------------------------------------------- - -screen_device::~screen_device() -{ - m_machine.render().texture_free(m_texture[0]); - m_machine.render().texture_free(m_texture[1]); - if (m_burnin != NULL) - finalize_burnin(); - global_free(m_screen_overlay_bitmap); -} - - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void screen_device::device_start() -{ - // configure the default cliparea - render_container::user_settings settings; - m_container->get_user_settings(settings); - settings.m_xoffset = m_config.m_xoffset; - settings.m_yoffset = m_config.m_yoffset; - settings.m_xscale = m_config.m_xscale; - settings.m_yscale = m_config.m_yscale; - m_container->set_user_settings(settings); - - // allocate the VBLANK timers - m_vblank_begin_timer = timer_alloc(machine, static_vblank_begin_callback, (void *)this); - m_vblank_end_timer = timer_alloc(machine, static_vblank_end_callback, (void *)this); - - // allocate a timer to reset partial updates - m_scanline0_timer = timer_alloc(machine, static_scanline0_callback, (void *)this); - - // allocate a timer to generate per-scanline updates - if ((machine->config->m_video_attributes & VIDEO_UPDATE_SCANLINE) != 0) - m_scanline_timer = timer_alloc(machine, static_scanline_update_callback, (void *)this); - - // configure the screen with the default parameters - configure(m_config.m_width, m_config.m_height, m_config.m_visarea, m_config.m_refresh); - - // reset VBLANK timing */ - m_vblank_start_time = attotime_zero; - m_vblank_end_time = attotime_make(0, m_vblank_period); - - // start the timer to generate per-scanline updates - if ((machine->config->m_video_attributes & VIDEO_UPDATE_SCANLINE) != 0) - timer_adjust_oneshot(m_scanline_timer, time_until_pos(0), 0); - - // create burn-in bitmap - if (options_get_int(machine->options(), OPTION_BURNIN) > 0) - { - int width, height; - if (sscanf(options_get_string(machine->options(), OPTION_SNAPSIZE), "%dx%d", &width, &height) != 2 || width == 0 || height == 0) - width = height = 300; - m_burnin = auto_alloc(machine, bitmap_t(width, height, BITMAP_FORMAT_INDEXED64)); - if (m_burnin == NULL) - fatalerror("Error allocating burn-in bitmap for screen at (%dx%d)\n", width, height); - bitmap_fill(m_burnin, NULL, 0); - } - - // load the effect overlay - const char *overname = options_get_string(machine->options(), OPTION_EFFECT); - if (overname != NULL && strcmp(overname, "none") != 0) - load_effect_overlay(overname); - - state_save_register_device_item(this, 0, m_width); - state_save_register_device_item(this, 0, m_height); - state_save_register_device_item(this, 0, m_visarea.min_x); - state_save_register_device_item(this, 0, m_visarea.min_y); - state_save_register_device_item(this, 0, m_visarea.max_x); - state_save_register_device_item(this, 0, m_visarea.max_y); - state_save_register_device_item(this, 0, m_last_partial_scan); - state_save_register_device_item(this, 0, m_frame_period); - state_save_register_device_item(this, 0, m_scantime); - state_save_register_device_item(this, 0, m_pixeltime); - state_save_register_device_item(this, 0, m_vblank_period); - state_save_register_device_item(this, 0, m_vblank_start_time.seconds); - state_save_register_device_item(this, 0, m_vblank_start_time.attoseconds); - state_save_register_device_item(this, 0, m_vblank_end_time.seconds); - state_save_register_device_item(this, 0, m_vblank_end_time.attoseconds); - state_save_register_device_item(this, 0, m_frame_number); -} - - -//------------------------------------------------- -// device_post_load - device-specific update -// after a save state is loaded -//------------------------------------------------- - -void screen_device::device_post_load() -{ - realloc_screen_bitmaps(); - global.movie_next_frame_time = timer_get_time(machine); -} - - -//------------------------------------------------- -// configure - configure screen parameters -//------------------------------------------------- - -void screen_device::configure(int width, int height, const rectangle &visarea, attoseconds_t frame_period) -{ - // validate arguments - assert(width > 0); - assert(height > 0); - assert(visarea.min_x >= 0); - assert(visarea.min_y >= 0); - assert(m_config.m_type == SCREEN_TYPE_VECTOR || visarea.min_x < width); - assert(m_config.m_type == SCREEN_TYPE_VECTOR || visarea.min_y < height); - assert(frame_period > 0); - - // fill in the new parameters - m_width = width; - m_height = height; - m_visarea = visarea; - - // reallocate bitmap if necessary - realloc_screen_bitmaps(); - - // compute timing parameters - m_frame_period = frame_period; - m_scantime = frame_period / height; - m_pixeltime = frame_period / (height * width); - - // if there has been no VBLANK time specified in the MACHINE_DRIVER, compute it now - // from the visible area, otherwise just used the supplied value - if (m_config.m_vblank == 0 && !m_config.m_oldstyle_vblank_supplied) - m_vblank_period = m_scantime * (height - (visarea.max_y + 1 - visarea.min_y)); - else - m_vblank_period = m_config.m_vblank; - - // if we are on scanline 0 already, reset the update timer immediately - // otherwise, defer until the next scanline 0 - if (vpos() == 0) - timer_adjust_oneshot(m_scanline0_timer, attotime_zero, 0); - else - timer_adjust_oneshot(m_scanline0_timer, time_until_pos(0), 0); - - // start the VBLANK timer - timer_adjust_oneshot(m_vblank_begin_timer, time_until_vblank_start(), 0); - - // adjust speed if necessary - update_refresh_speed(machine); -} - - -//------------------------------------------------- -// reset_origin - reset the timing such that the -// given (x,y) occurs at the current time -//------------------------------------------------- - -void screen_device::reset_origin(int beamy, int beamx) -{ - // compute the effective VBLANK start/end times - attotime curtime = timer_get_time(machine); - m_vblank_end_time = attotime_sub_attoseconds(curtime, beamy * m_scantime + beamx * m_pixeltime); - m_vblank_start_time = attotime_sub_attoseconds(m_vblank_end_time, m_vblank_period); - - // if we are resetting relative to (0,0) == VBLANK end, call the - // scanline 0 timer by hand now; otherwise, adjust it for the future - if (beamy == 0 && beamx == 0) - scanline0_callback(); - else - timer_adjust_oneshot(m_scanline0_timer, time_until_pos(0), 0); - - // if we are resetting relative to (visarea.max_y + 1, 0) == VBLANK start, - // call the VBLANK start timer now; otherwise, adjust it for the future - if (beamy == m_visarea.max_y + 1 && beamx == 0) - vblank_begin_callback(); - else - timer_adjust_oneshot(m_vblank_begin_timer, time_until_vblank_start(), 0); -} - - -//------------------------------------------------- -// realloc_screen_bitmaps - reallocate bitmaps -// and textures as necessary -//------------------------------------------------- - -void screen_device::realloc_screen_bitmaps() -{ - if (m_config.m_type == SCREEN_TYPE_VECTOR) - return; - - int curwidth = 0, curheight = 0; - - // extract the current width/height from the bitmap - if (m_bitmap[0] != NULL) - { - curwidth = m_bitmap[0]->width; - curheight = m_bitmap[0]->height; - } - - // if we're too small to contain this width/height, reallocate our bitmaps and textures - if (m_width > curwidth || m_height > curheight) - { - // free what we have currently - m_machine.render().texture_free(m_texture[0]); - m_machine.render().texture_free(m_texture[1]); - auto_free(machine, m_bitmap[0]); - auto_free(machine, m_bitmap[1]); - - // compute new width/height - curwidth = MAX(m_width, curwidth); - curheight = MAX(m_height, curheight); - - // choose the texture format - convert the screen format to a texture format - palette_t *palette = NULL; - switch (m_config.m_format) - { - case BITMAP_FORMAT_INDEXED16: m_texture_format = TEXFORMAT_PALETTE16; palette = machine->palette; break; - case BITMAP_FORMAT_RGB15: m_texture_format = TEXFORMAT_RGB15; palette = NULL; break; - case BITMAP_FORMAT_RGB32: m_texture_format = TEXFORMAT_RGB32; palette = NULL; break; - default: fatalerror("Invalid bitmap format!"); break; - } - - // allocate bitmaps - m_bitmap[0] = auto_alloc(machine, bitmap_t(curwidth, curheight, m_config.m_format)); - bitmap_set_palette(m_bitmap[0], machine->palette); - m_bitmap[1] = auto_alloc(machine, bitmap_t(curwidth, curheight, m_config.m_format)); - bitmap_set_palette(m_bitmap[1], machine->palette); - - // allocate textures - m_texture[0] = m_machine.render().texture_alloc(); - m_texture[0]->set_bitmap(m_bitmap[0], &m_visarea, m_texture_format, palette); - m_texture[1] = m_machine.render().texture_alloc(); - m_texture[1]->set_bitmap(m_bitmap[1], &m_visarea, m_texture_format, palette); - } -} - - -//------------------------------------------------- -// set_visible_area - just set the visible area -//------------------------------------------------- - -void screen_device::set_visible_area(int min_x, int max_x, int min_y, int max_y) -{ - // validate arguments - assert(min_x >= 0); - assert(min_y >= 0); - assert(min_x < max_x); - assert(min_y < max_y); - - rectangle visarea; - visarea.min_x = min_x; - visarea.max_x = max_x; - visarea.min_y = min_y; - visarea.max_y = max_y; - - configure(m_width, m_height, visarea, m_frame_period); -} - - -//------------------------------------------------- -// update_partial - perform a partial update from -// the last scanline up to and including the -// specified scanline -//-----------------------------------------------*/ - -bool screen_device::update_partial(int scanline) -{ - // validate arguments - assert(scanline >= 0); - - LOG_PARTIAL_UPDATES(("Partial: update_partial(%s, %d): ", tag(), scanline)); - - // these two checks only apply if we're allowed to skip frames - if (!(machine->config->m_video_attributes & VIDEO_ALWAYS_UPDATE)) - { - // if skipping this frame, bail - if (global.skipping_this_frame) - { - LOG_PARTIAL_UPDATES(("skipped due to frameskipping\n")); - return FALSE; - } - - // skip if this screen is not visible anywhere - if (!m_machine.render().is_live(*this)) - { - LOG_PARTIAL_UPDATES(("skipped because screen not live\n")); - return FALSE; - } - } - - // skip if less than the lowest so far - if (scanline < m_last_partial_scan) - { - LOG_PARTIAL_UPDATES(("skipped because less than previous\n")); - return FALSE; - } - - // set the start/end scanlines - rectangle clip = m_visarea; - if (m_last_partial_scan > clip.min_y) - clip.min_y = m_last_partial_scan; - if (scanline < clip.max_y) - clip.max_y = scanline; - - // render if necessary - bool result = false; - if (clip.min_y <= clip.max_y) - { - UINT32 flags = UPDATE_HAS_NOT_CHANGED; - - g_profiler.start(PROFILER_VIDEO); - LOG_PARTIAL_UPDATES(("updating %d-%d\n", clip.min_y, clip.max_y)); - - flags = machine->driver_data<driver_device>()->video_update(*this, *m_bitmap[m_curbitmap], clip); - global.partial_updates_this_frame++; - g_profiler.stop(); - - // if we modified the bitmap, we have to commit - m_changed |= ~flags & UPDATE_HAS_NOT_CHANGED; - result = true; - } - - // remember where we left off - m_last_partial_scan = scanline + 1; - return result; -} - - -//------------------------------------------------- -// update_now - perform an update from the last -// beam position up to the current beam position -//------------------------------------------------- - -void screen_device::update_now() -{ - int current_vpos = vpos(); - int current_hpos = hpos(); - - // since we can currently update only at the scanline - // level, we are trying to do the right thing by - // updating including the current scanline, only if the - // beam is past the halfway point horizontally. - // If the beam is in the first half of the scanline, - // we only update up to the previous scanline. - // This minimizes the number of pixels that might be drawn - // incorrectly until we support a pixel level granularity - if (current_hpos < (m_width / 2) && current_vpos > 0) - current_vpos = current_vpos - 1; - - update_partial(current_vpos); -} - - -//------------------------------------------------- -// vpos - returns the current vertical position -// of the beam -//------------------------------------------------- - -int screen_device::vpos() const -{ - attoseconds_t delta = attotime_to_attoseconds(attotime_sub(timer_get_time(machine), m_vblank_start_time)); - int vpos; - - // round to the nearest pixel - delta += m_pixeltime / 2; - - // compute the v position relative to the start of VBLANK - vpos = delta / m_scantime; - - // adjust for the fact that VBLANK starts at the bottom of the visible area - return (m_visarea.max_y + 1 + vpos) % m_height; -} - - -//------------------------------------------------- -// hpos - returns the current horizontal position -// of the beam -//------------------------------------------------- - -int screen_device::hpos() const -{ - attoseconds_t delta = attotime_to_attoseconds(attotime_sub(timer_get_time(machine), m_vblank_start_time)); - - // round to the nearest pixel - delta += m_pixeltime / 2; - - // compute the v position relative to the start of VBLANK - int vpos = delta / m_scantime; - - // subtract that from the total time - delta -= vpos * m_scantime; - - // return the pixel offset from the start of this scanline - return delta / m_pixeltime; -} - - -//------------------------------------------------- -// time_until_pos - returns the amount of time -// remaining until the beam is at the given -// hpos,vpos -//------------------------------------------------- - -attotime screen_device::time_until_pos(int vpos, int hpos) const -{ - // validate arguments - assert(vpos >= 0); - assert(hpos >= 0); - - // since we measure time relative to VBLANK, compute the scanline offset from VBLANK - vpos += m_height - (m_visarea.max_y + 1); - vpos %= m_height; - - // compute the delta for the given X,Y position - attoseconds_t targetdelta = (attoseconds_t)vpos * m_scantime + (attoseconds_t)hpos * m_pixeltime; - - // if we're past that time (within 1/2 of a pixel), head to the next frame - attoseconds_t curdelta = attotime_to_attoseconds(attotime_sub(timer_get_time(machine), m_vblank_start_time)); - if (targetdelta <= curdelta + m_pixeltime / 2) - targetdelta += m_frame_period; - while (targetdelta <= curdelta) - targetdelta += m_frame_period; - - // return the difference - return attotime_make(0, targetdelta - curdelta); -} - - -//------------------------------------------------- -// time_until_vblank_end - returns the amount of -// time remaining until the end of the current -// VBLANK (if in progress) or the end of the next -// VBLANK -//------------------------------------------------- - -attotime screen_device::time_until_vblank_end() const -{ - // if we are in the VBLANK region, compute the time until the end of the current VBLANK period - attotime target_time = m_vblank_end_time; - if (!vblank()) - target_time = attotime_add_attoseconds(target_time, m_frame_period); - return attotime_sub(target_time, timer_get_time(machine)); -} - - -//------------------------------------------------- -// register_vblank_callback - registers a VBLANK -// callback -//------------------------------------------------- - -void screen_device::register_vblank_callback(vblank_state_changed_func vblank_callback, void *param) -{ - // validate arguments - assert(vblank_callback != NULL); - - // check if we already have this callback registered - callback_item **itemptr; - for (itemptr = &m_callback_list; *itemptr != NULL; itemptr = &(*itemptr)->m_next) - if ((*itemptr)->m_callback == vblank_callback) - break; - - // if not found, register - if (*itemptr == NULL) - { - *itemptr = auto_alloc(machine, callback_item); - (*itemptr)->m_next = NULL; - (*itemptr)->m_callback = vblank_callback; - (*itemptr)->m_param = param; - } -} - - -//------------------------------------------------- -// vblank_begin_callback - call any external -// callbacks to signal the VBLANK period has begun -//------------------------------------------------- - -void screen_device::vblank_begin_callback() -{ - // reset the starting VBLANK time - m_vblank_start_time = timer_get_time(machine); - m_vblank_end_time = attotime_add_attoseconds(m_vblank_start_time, m_vblank_period); - - // call the screen specific callbacks - for (callback_item *item = m_callback_list; item != NULL; item = item->m_next) - (*item->m_callback)(*this, item->m_param, true); - - // if this is the primary screen and we need to update now - if (this == machine->primary_screen && !(machine->config->m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK)) - video_frame_update(machine, FALSE); - - // reset the VBLANK start timer for the next frame - timer_adjust_oneshot(m_vblank_begin_timer, time_until_vblank_start(), 0); - - // if no VBLANK period, call the VBLANK end callback immedietely, otherwise reset the timer - if (m_vblank_period == 0) - vblank_end_callback(); - else - timer_adjust_oneshot(m_vblank_end_timer, time_until_vblank_end(), 0); -} - - -//------------------------------------------------- -// vblank_end_callback - call any external -// callbacks to signal the VBLANK period has ended -//------------------------------------------------- - -void screen_device::vblank_end_callback() -{ - // call the screen specific callbacks - for (callback_item *item = m_callback_list; item != NULL; item = item->m_next) - (*item->m_callback)(*this, item->m_param, false); - - // if this is the primary screen and we need to update now - if (this == machine->primary_screen && (machine->config->m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK)) - video_frame_update(machine, FALSE); - - // increment the frame number counter - m_frame_number++; -} - - -//------------------------------------------------- -// scanline0_callback - reset partial updates -// for a screen -//------------------------------------------------- - -void screen_device::scanline0_callback() -{ - // reset partial updates - m_last_partial_scan = 0; - global.partial_updates_this_frame = 0; - - timer_adjust_oneshot(m_scanline0_timer, time_until_pos(0), 0); -} - - -//------------------------------------------------- -// scanline_update_callback - perform partial -// updates on each scanline -//------------------------------------------------- - -void screen_device::scanline_update_callback(int scanline) -{ - // force a partial update to the current scanline - update_partial(scanline); - - // compute the next visible scanline - scanline++; - if (scanline > m_visarea.max_y) - scanline = m_visarea.min_y; - timer_adjust_oneshot(m_scanline_timer, time_until_pos(scanline), scanline); -} - - -//------------------------------------------------- -// update_quads - set up the quads for this -// screen -//------------------------------------------------- - -bool screen_device::update_quads() -{ - // only update if live - if (m_machine.render().is_live(*this)) - { - // only update if empty and not a vector game; otherwise assume the driver did it directly - if (m_config.m_type != SCREEN_TYPE_VECTOR && (machine->config->m_video_attributes & VIDEO_SELF_RENDER) == 0) - { - // if we're not skipping the frame and if the screen actually changed, then update the texture - if (!global.skipping_this_frame && m_changed) - { - rectangle fixedvis = m_visarea; - fixedvis.max_x++; - fixedvis.max_y++; - - palette_t *palette = (m_texture_format == TEXFORMAT_PALETTE16) ? machine->palette : NULL; - m_texture[m_curbitmap]->set_bitmap(m_bitmap[m_curbitmap], &fixedvis, m_texture_format, palette); - - m_curtexture = m_curbitmap; - m_curbitmap = 1 - m_curbitmap; - } - - // create an empty container with a single quad - m_container->empty(); - m_container->add_quad(0.0f, 0.0f, 1.0f, 1.0f, MAKE_ARGB(0xff,0xff,0xff,0xff), m_texture[m_curtexture], PRIMFLAG_BLENDMODE(BLENDMODE_NONE) | PRIMFLAG_SCREENTEX(1)); - } - } - - // reset the screen changed flags - bool result = m_changed; - m_changed = false; - return result; -} - - -//------------------------------------------------- -// update_burnin - update the burnin bitmap -//------------------------------------------------- - -void screen_device::update_burnin() -{ -#undef rand - if (m_burnin == NULL) - return; - - bitmap_t *srcbitmap = m_bitmap[m_curtexture]; - if (srcbitmap == NULL) - return; - - int srcwidth = srcbitmap->width; - int srcheight = srcbitmap->height; - int dstwidth = m_burnin->width; - int dstheight = m_burnin->height; - int xstep = (srcwidth << 16) / dstwidth; - int ystep = (srcheight << 16) / dstheight; - int xstart = ((UINT32)rand() % 32767) * xstep / 32767; - int ystart = ((UINT32)rand() % 32767) * ystep / 32767; - int srcx, srcy; - int x, y; - - // iterate over rows in the destination - for (y = 0, srcy = ystart; y < dstheight; y++, srcy += ystep) - { - UINT64 *dst = BITMAP_ADDR64(m_burnin, y, 0); - - // handle the 16-bit palettized case - if (srcbitmap->format == BITMAP_FORMAT_INDEXED16) - { - const UINT16 *src = BITMAP_ADDR16(srcbitmap, srcy >> 16, 0); - const rgb_t *palette = palette_entry_list_adjusted(machine->palette); - for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep) - { - rgb_t pixel = palette[src[srcx >> 16]]; - dst[x] += RGB_GREEN(pixel) + RGB_RED(pixel) + RGB_BLUE(pixel); - } - } - - // handle the 15-bit RGB case - else if (srcbitmap->format == BITMAP_FORMAT_RGB15) - { - const UINT16 *src = BITMAP_ADDR16(srcbitmap, srcy >> 16, 0); - for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep) - { - rgb15_t pixel = src[srcx >> 16]; - dst[x] += ((pixel >> 10) & 0x1f) + ((pixel >> 5) & 0x1f) + ((pixel >> 0) & 0x1f); - } - } - - // handle the 32-bit RGB case - else if (srcbitmap->format == BITMAP_FORMAT_RGB32) - { - const UINT32 *src = BITMAP_ADDR32(srcbitmap, srcy >> 16, 0); - for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep) - { - rgb_t pixel = src[srcx >> 16]; - dst[x] += RGB_GREEN(pixel) + RGB_RED(pixel) + RGB_BLUE(pixel); - } - } - } -} - - -//------------------------------------------------- -// finalize_burnin - finalize the burnin bitmap -//------------------------------------------------- - -void screen_device::finalize_burnin() -{ - if (m_burnin == NULL) - return; - - // compute the scaled visible region - rectangle scaledvis; - scaledvis.min_x = m_visarea.min_x * m_burnin->width / m_width; - scaledvis.max_x = m_visarea.max_x * m_burnin->width / m_width; - scaledvis.min_y = m_visarea.min_y * m_burnin->height / m_height; - scaledvis.max_y = m_visarea.max_y * m_burnin->height / m_height; - - // wrap a bitmap around the subregion we care about - bitmap_t *finalmap = auto_alloc(machine, bitmap_t(scaledvis.max_x + 1 - scaledvis.min_x, - scaledvis.max_y + 1 - scaledvis.min_y, - BITMAP_FORMAT_ARGB32)); - - int srcwidth = m_burnin->width; - int srcheight = m_burnin->height; - int dstwidth = finalmap->width; - int dstheight = finalmap->height; - int xstep = (srcwidth << 16) / dstwidth; - int ystep = (srcheight << 16) / dstheight; - - // find the maximum value - UINT64 minval = ~(UINT64)0; - UINT64 maxval = 0; - for (int y = 0; y < srcheight; y++) - { - UINT64 *src = BITMAP_ADDR64(m_burnin, y, 0); - for (int x = 0; x < srcwidth; x++) - { - minval = MIN(minval, src[x]); - maxval = MAX(maxval, src[x]); - } - } - - if (minval == maxval) - return; - - // now normalize and convert to RGB - for (int y = 0, srcy = 0; y < dstheight; y++, srcy += ystep) - { - UINT64 *src = BITMAP_ADDR64(m_burnin, srcy >> 16, 0); - UINT32 *dst = BITMAP_ADDR32(finalmap, y, 0); - for (int x = 0, srcx = 0; x < dstwidth; x++, srcx += xstep) - { - int brightness = (UINT64)(maxval - src[srcx >> 16]) * 255 / (maxval - minval); - dst[x] = MAKE_ARGB(0xff, brightness, brightness, brightness); - } - } - - // write the final PNG - - // compute the name and create the file - astring fname; - fname.printf("%s" PATH_SEPARATOR "burnin-%s.png", machine->basename(), tag()); - mame_file *file; - file_error filerr = mame_fopen(SEARCHPATH_SCREENSHOT, fname, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &file); - if (filerr == FILERR_NONE) - { - png_info pnginfo = { 0 }; - png_error pngerr; - char text[256]; - - // add two text entries describing the image - sprintf(text, APPNAME " %s", build_version); - png_add_text(&pnginfo, "Software", text); - sprintf(text, "%s %s", machine->gamedrv->manufacturer, machine->gamedrv->description); - png_add_text(&pnginfo, "System", text); - - // now do the actual work - pngerr = png_write_bitmap(mame_core_file(file), &pnginfo, finalmap, 0, NULL); - - // free any data allocated - png_free(&pnginfo); - mame_fclose(file); - } -} - - -//------------------------------------------------- -// finalize_burnin - finalize the burnin bitmap -//------------------------------------------------- - -void screen_device::load_effect_overlay(const char *filename) -{ - // ensure that there is a .png extension - astring fullname(filename); - int extension = fullname.rchr(0, '.'); - if (extension != -1) - fullname.del(extension, -1); - fullname.cat(".png"); - - // load the file - m_screen_overlay_bitmap = render_load_png(OPTION_ARTPATH, NULL, fullname, NULL, NULL); - if (m_screen_overlay_bitmap != NULL) - m_container->set_overlay(m_screen_overlay_bitmap); - else - mame_printf_warning("Unable to load effect PNG file '%s'\n", fullname.cstr()); -} - - - -//************************************************************************** // SOFTWARE RENDERING //************************************************************************** diff --git a/src/emu/video.h b/src/emu/video.h index c49a6dd5ead..f295b0f0c99 100644 --- a/src/emu/video.h +++ b/src/emu/video.h @@ -55,15 +55,6 @@ const int FRAMESKIP_LEVELS = 12; const int MAX_FRAMESKIP = FRAMESKIP_LEVELS - 2; -// screen types -enum screen_type_enum -{ - SCREEN_TYPE_INVALID = 0, - SCREEN_TYPE_RASTER, - SCREEN_TYPE_VECTOR, - SCREEN_TYPE_LCD -}; - //************************************************************************** @@ -72,385 +63,149 @@ enum screen_type_enum // forward references class render_target; -class render_texture; class screen_device; +typedef struct _avi_file avi_file; -// callback that is called to notify of a change in the VBLANK state -typedef void (*vblank_state_changed_func)(screen_device &device, void *param, bool vblank_state); - -// ======================> screen_device_config +// ======================> video_manager -class screen_device_config : public device_config +class video_manager { friend class screen_device; - - // construction/destruction - screen_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock); - + public: - // allocators - static device_config *static_alloc_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock); - virtual device_t *alloc_device(running_machine &machine) const; - - // configuration readers - screen_type_enum screen_type() const { return m_type; } - int width() const { return m_width; } - int height() const { return m_height; } - const rectangle &visible_area() const { return m_visarea; } - bool oldstyle_vblank_supplied() const { return m_oldstyle_vblank_supplied; } - attoseconds_t refresh() const { return m_refresh; } - attoseconds_t vblank() const { return m_vblank; } - bitmap_format format() const { return m_format; } - float xoffset() const { return m_xoffset; } - float yoffset() const { return m_yoffset; } - float xscale() const { return m_xscale; } - float yscale() const { return m_yscale; } - - // inline configuration helpers - static void static_set_format(device_config *device, bitmap_format format); - static void static_set_type(device_config *device, screen_type_enum type); - static void static_set_raw(device_config *device, UINT32 pixclock, UINT16 htotal, UINT16 hbend, UINT16 hbstart, UINT16 vtotal, UINT16 vbend, UINT16 vbstart); - static void static_set_refresh(device_config *device, attoseconds_t rate); - static void static_set_vblank_time(device_config *device, attoseconds_t time); - static void static_set_size(device_config *device, UINT16 width, UINT16 height); - static void static_set_visarea(device_config *device, INT16 minx, INT16 maxx, INT16 miny, INT16 maxy); - static void static_set_default_position(device_config *device, double xscale, double xoffs, double yscale, double yoffs); - -private: - // device_config overrides - virtual bool device_validity_check(const game_driver &driver) const; - - // inline configuration data - screen_type_enum m_type; // type of screen - int m_width, m_height; // default total width/height (HTOTAL, VTOTAL) - rectangle m_visarea; // default visible area (HBLANK end/start, VBLANK end/start) - bool m_oldstyle_vblank_supplied; // MDRV_SCREEN_VBLANK_TIME macro used - attoseconds_t m_refresh; // default refresh period - attoseconds_t m_vblank; // duration of a VBLANK - bitmap_format m_format; // bitmap format - float m_xoffset, m_yoffset; // default X/Y offsets - float m_xscale, m_yscale; // default X/Y scale factor -}; - - - -// ======================> screen_device - -class screen_device : public device_t -{ - friend class screen_device_config; - friend class render_manager; - friend resource_pool_object<screen_device>::~resource_pool_object(); + // movie format options + enum movie_format + { + MF_MNG, + MF_AVI + }; // construction/destruction - screen_device(running_machine &_machine, const screen_device_config &config); - virtual ~screen_device(); - -public: - // information getters - const screen_device_config &config() const { return m_config; } - screen_type_enum screen_type() const { return m_config.m_type; } - int width() const { return m_width; } - int height() const { return m_height; } - const rectangle &visible_area() const { return m_visarea; } - bitmap_format format() const { return m_config.m_format; } - render_container &container() const { assert(m_container != NULL); return *m_container; } - - // dynamic configuration - void configure(int width, int height, const rectangle &visarea, attoseconds_t frame_period); - void reset_origin(int beamy = 0, int beamx = 0); - void set_visible_area(int min_x, int max_x, int min_y, int max_y); - - // beam positioning and state - int vpos() const; - int hpos() const; - bool vblank() const { return attotime_compare(timer_get_time(machine), m_vblank_end_time) < 0; } - bool hblank() const { int curpos = hpos(); return (curpos < m_visarea.min_x || curpos > m_visarea.max_x); } - - // timing - attotime time_until_pos(int vpos, int hpos = 0) const; - attotime time_until_vblank_start() const { return time_until_pos(m_visarea.max_y + 1); } - attotime time_until_vblank_end() const; - attotime time_until_update() const { return (machine->config->m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK) ? time_until_vblank_end() : time_until_vblank_start(); } - attotime scan_period() const { return attotime_make(0, m_scantime); } - attotime frame_period() const { return (this == NULL || !started()) ? k_default_frame_period : attotime_make(0, m_frame_period); }; - UINT64 frame_number() const { return m_frame_number; } - - // updating - bool update_partial(int scanline); - void update_now(); - - // additional helpers - void save_snapshot(mame_file *fp); - void register_vblank_callback(vblank_state_changed_func vblank_callback, void *param); - bitmap_t *alloc_compatible_bitmap(int width = 0, int height = 0) { return auto_bitmap_alloc(machine, (width == 0) ? m_width : width, (height == 0) ? m_height : height, m_config.m_format); } - - // internal to the video system - bool update_quads(); - void update_burnin(); - - // globally accessible constants - static const int k_default_frame_rate = 60; - static const attotime k_default_frame_period; + video_manager(running_machine &machine); + + // getters + bool skip_this_frame() const { return m_skipping_this_frame; } + int speed_factor() const { return m_speed; } + int frameskip() const { return m_auto_frameskip ? -1 : m_frameskip_level; } + bool throttled() const { return m_throttle; } + bool fastforward() const { return m_fastforward; } + bool is_recording() const { return (m_mngfile != NULL || m_avifile != NULL); } + + // setters + void set_speed_factor(int speed) { m_speed = speed; } + void set_frameskip(int frameskip); + void set_throttled(bool throttled = true) { m_throttle = throttled; } + void set_fastforward(bool ffwd = true) { m_fastforward = ffwd; } + + // render a frame + void frame_update(bool debug = false); + + // current speed helpers + astring &speed_text(astring &string); + double speed_percent() const { return m_speed_percent; } + + // snapshots + void save_snapshot(screen_device *screen, mame_file &file); + void save_active_screen_snapshots(); + + // movies + void begin_recording(const char *name, movie_format format = MF_AVI); + void end_recording(); + void add_sound_to_recording(const INT16 *sound, int numsamples); private: - // device-level overrides - virtual void device_start(); - virtual void device_post_load(); - // internal helpers - void set_container(render_container &container) { m_container = &container; } - void realloc_screen_bitmaps(); - - static TIMER_CALLBACK( static_vblank_begin_callback ) { reinterpret_cast<screen_device *>(ptr)->vblank_begin_callback(); } - void vblank_begin_callback(); - - static TIMER_CALLBACK( static_vblank_end_callback ) { reinterpret_cast<screen_device *>(ptr)->vblank_end_callback(); } - void vblank_end_callback(); - - static TIMER_CALLBACK( static_scanline0_callback ) { reinterpret_cast<screen_device *>(ptr)->scanline0_callback(); } -public: // temporary - void scanline0_callback(); -private: - - static TIMER_CALLBACK( static_scanline_update_callback ) { reinterpret_cast<screen_device *>(ptr)->scanline_update_callback(param); } - void scanline_update_callback(int scanline); - - void finalize_burnin(); - void load_effect_overlay(const char *filename); + void init_buffered_spriteram(); + static void exit_static(running_machine &machine); + void exit(); + static TIMER_CALLBACK( screenless_update_callback ); + void postload(); + + // effective value helpers + int effective_autoframeskip() const; + int effective_frameskip() const; + bool effective_throttle() const; + + // speed and throttling helpers + int original_speed_setting() const; + bool finish_screen_updates(); + void update_throttle(attotime emutime); + osd_ticks_t throttle_until_ticks(osd_ticks_t target_ticks); + void update_frameskip(); + void update_refresh_speed(); + void recompute_speed(attotime emutime); + + // snapshot/movie helpers + void create_snapshot_bitmap(device_t *screen); + file_error mame_fopen_next(const char *pathoption, const char *extension, mame_file *&file); + void record_frame(); // internal state - const screen_device_config &m_config; - render_container * m_container; // pointer to our container - - // dimensions - int m_width; // current width (HTOTAL) - int m_height; // current height (VTOTAL) - rectangle m_visarea; // current visible area (HBLANK end/start, VBLANK end/start) - - // textures and bitmaps - render_texture * m_texture[2]; // 2x textures for the screen bitmap - bitmap_t * m_bitmap[2]; // 2x bitmaps for rendering - bitmap_t * m_burnin; // burn-in bitmap - UINT8 m_curbitmap; // current bitmap index - UINT8 m_curtexture; // current texture index - INT32 m_texture_format; // texture format of bitmap for this screen - bool m_changed; // has this bitmap changed? - INT32 m_last_partial_scan; // scanline of last partial update - bitmap_t * m_screen_overlay_bitmap;// screen overlay bitmap - - // screen timing - attoseconds_t m_frame_period; // attoseconds per frame - attoseconds_t m_scantime; // attoseconds per scanline - attoseconds_t m_pixeltime; // attoseconds per pixel - attoseconds_t m_vblank_period; // attoseconds per VBLANK period - attotime m_vblank_start_time; // time of last VBLANK start - attotime m_vblank_end_time; // time of last VBLANK end - emu_timer * m_vblank_begin_timer; // timer to signal VBLANK start - emu_timer * m_vblank_end_timer; // timer to signal VBLANK end - emu_timer * m_scanline0_timer; // scanline 0 timer - emu_timer * m_scanline_timer; // scanline timer - UINT64 m_frame_number; // the current frame number - - struct callback_item - { - callback_item * m_next; - vblank_state_changed_func m_callback; - void * m_param; - }; - callback_item * m_callback_list; // list of VBLANK callbacks + running_machine & m_machine; // reference to our machine + + // screenless systems + emu_timer * m_screenless_frame_timer; // timer to signal VBLANK start + + // throttling calculations + osd_ticks_t m_throttle_last_ticks; // osd_ticks the last call to throttle + attotime m_throttle_realtime; // real time the last call to throttle + attotime m_throttle_emutime; // emulated time the last call to throttle + UINT32 m_throttle_history; // history of frames where we were fast enough + + // dynamic speed computation + osd_ticks_t m_speed_last_realtime; // real time at the last speed calculation + attotime m_speed_last_emutime; // emulated time at the last speed calculation + double m_speed_percent; // most recent speed percentage + + // overall speed computation + UINT32 m_overall_real_seconds; // accumulated real seconds at normal speed + osd_ticks_t m_overall_real_ticks; // accumulated real ticks at normal speed + attotime m_overall_emutime; // accumulated emulated time at normal speed + UINT32 m_overall_valid_counter; // number of consecutive valid time periods + + // configuration + bool m_throttle; // flag: TRUE if we're currently throttled + bool m_fastforward; // flag: TRUE if we're currently fast-forwarding + UINT32 m_seconds_to_run; // number of seconds to run before quitting + bool m_auto_frameskip; // flag: TRUE if we're automatically frameskipping + UINT32 m_speed; // overall speed (*100) + + // frameskipping + UINT8 m_empty_skip_count; // number of empty frames we have skipped + UINT8 m_frameskip_level; // current frameskip level + UINT8 m_frameskip_counter; // counter that counts through the frameskip steps + INT8 m_frameskip_adjust; + bool m_skipping_this_frame; // flag: TRUE if we are skipping the current frame + osd_ticks_t m_average_oversleep; // average number of ticks the OSD oversleeps + + // snapshot stuff + render_target * m_snap_target; // screen shapshot target + bitmap_t * m_snap_bitmap; // screen snapshot bitmap + bool m_snap_native; // are we using native per-screen layouts? + INT32 m_snap_width; // width of snapshots (0 == auto) + INT32 m_snap_height; // height of snapshots (0 == auto) + + // movie recording + mame_file * m_mngfile; // handle to the open movie file + avi_file * m_avifile; // handle to the open movie file + attotime m_movie_frame_period; // period of a single movie frame + attotime m_movie_next_frame_time; // time of next frame + UINT32 m_movie_frame; // current movie frame number + + static const UINT8 s_skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS]; + + static const attoseconds_t SUBSECONDS_PER_SPEED_UPDATE = ATTOSECONDS_PER_SECOND / 4; + static const int PAUSED_REFRESH_RATE = 30; }; -// device type definition -extern const device_type SCREEN; - - - -//************************************************************************** -// SCREEN DEVICE CONFIGURATION MACROS -//************************************************************************** - -#define MDRV_SCREEN_ADD(_tag, _type) \ - MDRV_DEVICE_ADD(_tag, SCREEN, 0) \ - MDRV_SCREEN_TYPE(_type) \ - -#define MDRV_SCREEN_MODIFY(_tag) \ - MDRV_DEVICE_MODIFY(_tag) - -#define MDRV_SCREEN_FORMAT(_format) \ - screen_device_config::static_set_format(device, _format); \ - -#define MDRV_SCREEN_TYPE(_type) \ - screen_device_config::static_set_type(device, SCREEN_TYPE_##_type); \ - -#define MDRV_SCREEN_RAW_PARAMS(_pixclock, _htotal, _hbend, _hbstart, _vtotal, _vbend, _vbstart) \ - screen_device_config::static_set_raw(device, _pixclock, _htotal, _hbend, _hbstart, _vtotal, _vbend, _vbstart); - -#define MDRV_SCREEN_REFRESH_RATE(_rate) \ - screen_device_config::static_set_refresh(device, HZ_TO_ATTOSECONDS(_rate)); \ - -#define MDRV_SCREEN_VBLANK_TIME(_time) \ - screen_device_config::static_set_vblank_time(device, _time); \ - -#define MDRV_SCREEN_SIZE(_width, _height) \ - screen_device_config::static_set_size(device, _width, _height); \ - -#define MDRV_SCREEN_VISIBLE_AREA(_minx, _maxx, _miny, _maxy) \ - screen_device_config::static_set_visarea(device, _minx, _maxx, _miny, _maxy); \ - -#define MDRV_SCREEN_DEFAULT_POSITION(_xscale, _xoffs, _yscale, _yoffs) \ - screen_device_config::static_set_default_position(device, _xscale, _xoffs, _yscale, _yoffs); \ - - - -//************************************************************************** -// FUNCTION PROTOTYPES -//************************************************************************** - -/* ----- core implementation ----- */ - -/* core initialization */ -void video_init(running_machine *machine); - -/* ----- global rendering ----- */ +// ----- debugging helpers ----- -/* update the screen, handling frame skipping and rendering */ -void video_frame_update(running_machine *machine, int debug); - - -/* ----- throttling/frameskipping/performance ----- */ - -/* are we skipping the current frame? */ -int video_skip_this_frame(void); - -/* get/set the speed factor as an integer * 100 */ -int video_get_speed_factor(void); -void video_set_speed_factor(int speed); - -/* return text to display about the current speed */ -const char *video_get_speed_text(running_machine *machine); - -/* return the current effective speed percentage */ -double video_get_speed_percent(running_machine *machine); - -/* get/set the current frameskip (-1 means auto) */ -int video_get_frameskip(void); -void video_set_frameskip(int frameskip); - -/* get/set the current throttle */ -int video_get_throttle(void); -void video_set_throttle(int throttle); - -/* get/set the current fastforward state */ -int video_get_fastforward(void); -void video_set_fastforward(int fastforward); - - -/* ----- snapshots ----- */ - -/* save a snapshot of a given screen */ -void screen_save_snapshot(running_machine *machine, device_t *screen, mame_file *fp); - -/* save a snapshot of all the active screens */ -void video_save_active_screen_snapshots(running_machine *machine); - - -/* ----- movie recording ----- */ - -int video_mng_is_movie_active(running_machine *machine); -void video_mng_begin_recording(running_machine *machine, const char *name); -void video_mng_end_recording(running_machine *machine); - -void video_avi_begin_recording(running_machine *machine, const char *name); -void video_avi_end_recording(running_machine *machine); -void video_avi_add_sound(running_machine *machine, const INT16 *sound, int numsamples); - - -/* ----- configuration helpers ----- */ - -/* select a view for a given target */ -int video_get_view_for_target(running_machine *machine, render_target *target, const char *viewname, int targetindex, int numtargets); - - -/* ----- debugging helpers ----- */ - -/* assert if any pixels in the given bitmap contain an invalid palette index */ +// assert if any pixels in the given bitmap contain an invalid palette index void video_assert_out_of_range_pixels(running_machine *machine, bitmap_t *bitmap); - -//************************************************************************** -// INLINE HELPERS -//************************************************************************** - -//------------------------------------------------- -// screen_count - return the number of -// video screen devices in a machine_config -//------------------------------------------------- - -inline int screen_count(const machine_config &config) -{ - return config.m_devicelist.count(SCREEN); -} - - -//------------------------------------------------- -// screen_first - return the first -// video screen device config in a machine_config -//------------------------------------------------- - -inline const screen_device_config *screen_first(const machine_config &config) -{ - return downcast<screen_device_config *>(config.m_devicelist.first(SCREEN)); -} - - -//------------------------------------------------- -// screen_next - return the next -// video screen device config in a machine_config -//------------------------------------------------- - -inline const screen_device_config *screen_next(const screen_device_config *previous) -{ - return downcast<screen_device_config *>(previous->typenext()); -} - - -//------------------------------------------------- -// screen_count - return the number of -// video screen devices in a machine -//------------------------------------------------- - -inline int screen_count(running_machine &machine) -{ - return machine.m_devicelist.count(SCREEN); -} - - -//------------------------------------------------- -// screen_first - return the first -// video screen device in a machine -//------------------------------------------------- - -inline screen_device *screen_first(running_machine &machine) -{ - return downcast<screen_device *>(machine.m_devicelist.first(SCREEN)); -} - - -//------------------------------------------------- -// screen_next - return the next -// video screen device in a machine -//------------------------------------------------- - -inline screen_device *screen_next(screen_device *previous) -{ - return downcast<screen_device *>(previous->typenext()); -} - - #endif /* __VIDEO_H__ */ diff --git a/src/emu/video/tms9928a.c b/src/emu/video/tms9928a.c index 5793134f42e..399474e72d2 100644 --- a/src/emu/video/tms9928a.c +++ b/src/emu/video/tms9928a.c @@ -435,7 +435,7 @@ int TMS9928A_interrupt(running_machine *machine) { int b; /* when skipping frames, calculate sprite collision */ - if (video_skip_this_frame() ) { + if (machine->video().skip_this_frame()) { if (TMS_SPRITES_ENABLED) { draw_sprites (machine->primary_screen, NULL, NULL); } diff --git a/src/mame/machine/atarigen.c b/src/mame/machine/atarigen.c index 9382d51bb9e..f8edcd7ff2b 100644 --- a/src/mame/machine/atarigen.c +++ b/src/mame/machine/atarigen.c @@ -115,8 +115,8 @@ void atarigen_init(running_machine *machine) int i; /* allocate timers for all screens */ - assert(screen_count(*machine) <= ARRAY_LENGTH(state->screen_timer)); - for (i = 0, screen = screen_first(*machine); screen != NULL; i++, screen = screen_next(screen)) + assert(machine->m_devicelist.count(SCREEN) <= ARRAY_LENGTH(state->screen_timer)); + for (i = 0, screen = machine->first_screen(); screen != NULL; i++, screen = screen->next_screen()) { state->screen_timer[i].screen = screen; state->screen_timer[i].scanline_interrupt_timer = timer_alloc(machine, scanline_interrupt_callback, (void *)screen); diff --git a/src/mame/video/cave.c b/src/mame/video/cave.c index eb063de5b02..32dbf56c301 100644 --- a/src/mame/video/cave.c +++ b/src/mame/video/cave.c @@ -1743,7 +1743,7 @@ void cave_get_sprite_info( running_machine *machine ) cave_state *state = machine->driver_data<cave_state>(); if (state->kludge == 3) /* mazinger metmqstr */ { - if (video_skip_this_frame() == 0) + if (machine->video().skip_this_frame() == 0) { state->spriteram_bank = state->spriteram_bank_delay; (*state->get_sprite_info)(machine); @@ -1752,7 +1752,7 @@ void cave_get_sprite_info( running_machine *machine ) } else { - if (video_skip_this_frame() == 0) + if (machine->video().skip_this_frame() == 0) { state->spriteram_bank = state->videoregs[4] & 1; (*state->get_sprite_info)(machine); diff --git a/src/mame/video/cyberbal.c b/src/mame/video/cyberbal.c index 739b7f461db..b2d530eef45 100644 --- a/src/mame/video/cyberbal.c +++ b/src/mame/video/cyberbal.c @@ -268,7 +268,7 @@ void cyberbal_scanline_update(screen_device &screen, int scanline) screen_device *update_screen; /* loop over screens */ - for (i = 0, update_screen = screen_first(*screen.machine); update_screen != NULL; i++, update_screen = screen_next(update_screen)) + for (i = 0, update_screen = screen.machine->first_screen(); update_screen != NULL; i++, update_screen = update_screen->next_screen()) { UINT16 *vram = i ? state->alpha2 : state->alpha; UINT16 *base = &vram[((scanline - 8) / 8) * 64 + 47]; diff --git a/src/mame/video/gaelco3d.c b/src/mame/video/gaelco3d.c index 373b6bb64b8..a12ab6fb839 100644 --- a/src/mame/video/gaelco3d.c +++ b/src/mame/video/gaelco3d.c @@ -404,7 +404,7 @@ WRITE32_HANDLER( gaelco3d_render_w ) fatalerror("Out of polygon buffer space!"); /* if we've accumulated a completed poly set of data, queue it */ - if (!video_skip_this_frame()) + if (!space->machine->video().skip_this_frame()) { if (polydata_count >= 18 && (polydata_count % 2) == 1 && IS_POLYEND(polydata_buffer[polydata_count - 2])) { diff --git a/src/mame/video/taito_f3.c b/src/mame/video/taito_f3.c index fe8835cad7d..d09bb85822a 100644 --- a/src/mame/video/taito_f3.c +++ b/src/mame/video/taito_f3.c @@ -581,7 +581,7 @@ VIDEO_EOF( f3 ) { if (sprite_lag==2) { - if (video_skip_this_frame() == 0) + if (machine->video().skip_this_frame() == 0) { get_sprite_info(machine, spriteram32_buffered); } @@ -589,7 +589,7 @@ VIDEO_EOF( f3 ) } else if (sprite_lag==1) { - if (video_skip_this_frame() == 0) + if (machine->video().skip_this_frame() == 0) { get_sprite_info(machine, machine->generic.spriteram.u32); } diff --git a/src/osd/sdl/drawogl.c b/src/osd/sdl/drawogl.c index 74f8861bde1..5889f940800 100644 --- a/src/osd/sdl/drawogl.c +++ b/src/osd/sdl/drawogl.c @@ -1207,7 +1207,7 @@ static int drawogl_window_draw(sdl_window_info *window, UINT32 dc, int update) // figure out if we're vector scrnum = is_vector = 0; - for (screen = screen_first(*window->machine->config); screen != NULL; screen = screen_next(screen)) + for (screen = window->machine->config->first_screen(); screen != NULL; screen = screen->next_screen()) { if (scrnum == window->index) { @@ -2944,7 +2944,7 @@ static void texture_shader_update(sdl_window_info *window, texture_info *texture scrnum = 0; container = (render_container *)NULL; - for (screen_device *screen = screen_first(*window->machine); screen != NULL; screen = screen_next(screen)) + for (screen_device *screen = window->machine->first_screen(); screen != NULL; screen = screen->next_screen()) { if (scrnum == window->start_viewscreen) { diff --git a/src/osd/sdl/sound.c b/src/osd/sdl/sound.c index b4bf8b38368..f4a04e53266 100644 --- a/src/osd/sdl/sound.c +++ b/src/osd/sdl/sound.c @@ -130,13 +130,13 @@ static void sdl_cleanup_audio(running_machine &machine) //============================================================ // lock_buffer //============================================================ -static int lock_buffer(long offset, long size, void **buffer1, long *length1, void **buffer2, long *length2) +static int lock_buffer(running_machine &machine, long offset, long size, void **buffer1, long *length1, void **buffer2, long *length2) { volatile long pstart, pend, lstart, lend; if (!buf_locked) { - if (video_get_throttle()) + if (machine.video().throttled()) { pstart = stream_playpos; pend = (pstart + sdl_xfer_samples); @@ -213,14 +213,14 @@ static void att_memcpy(void *dest, const INT16 *data, int bytes_to_copy) // copy_sample_data //============================================================ -static void copy_sample_data(const INT16 *data, int bytes_to_copy) +static void copy_sample_data(running_machine &machine, const INT16 *data, int bytes_to_copy) { void *buffer1, *buffer2 = (void *)NULL; long length1, length2; int cur_bytes; // attempt to lock the stream buffer - if (lock_buffer(stream_buffer_in, bytes_to_copy, &buffer1, &length1, &buffer2, &length2) < 0) + if (lock_buffer(machine, stream_buffer_in, bytes_to_copy, &buffer1, &length1, &buffer2, &length2) < 0) { buffer_underflows++; return; @@ -333,7 +333,7 @@ void sdl_osd_interface::update_audio_stream(const INT16 *buffer, int samples_thi // now we know where to copy; let's do it stream_buffer_in = stream_in; - copy_sample_data(buffer, bytes_this_frame); + copy_sample_data(machine(), buffer, bytes_this_frame); } } diff --git a/src/osd/sdl/window.c b/src/osd/sdl/window.c index 44ad73d5030..e4b1defbace 100644 --- a/src/osd/sdl/window.c +++ b/src/osd/sdl/window.c @@ -1017,7 +1017,7 @@ static void set_starting_view(running_machine *machine, int index, sdl_window_in view = defview; // query the video system to help us pick a view - viewindex = video_get_view_for_target(machine, window->target, view, index, video_config.numscreens); + viewindex = window->target->configured_view(view, index, video_config.numscreens); // set the view window->target->set_view(viewindex); diff --git a/src/osd/windows/drawd3d.c b/src/osd/windows/drawd3d.c index 45d6853b5a8..8f3b44b1c32 100644 --- a/src/osd/windows/drawd3d.c +++ b/src/osd/windows/drawd3d.c @@ -1283,7 +1283,7 @@ static void pick_best_mode(win_window_info *window) int modenum; // determine the refresh rate of the primary screen - const screen_device_config *primary_screen = screen_first(*window->machine->config); + const screen_device_config *primary_screen = window->machine->config->first_screen(); if (primary_screen != NULL) target_refresh = ATTOSECONDS_TO_HZ(primary_screen->refresh()); diff --git a/src/osd/windows/drawdd.c b/src/osd/windows/drawdd.c index afdc06fd821..00db5f9f91b 100644 --- a/src/osd/windows/drawdd.c +++ b/src/osd/windows/drawdd.c @@ -457,7 +457,7 @@ static int drawdd_window_draw(win_window_info *window, HDC dc, int update) if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X unlocking blit surface\n", (int)result); // sync to VBLANK - if ((video_config.waitvsync || video_config.syncrefresh) && video_get_throttle() && (!window->fullscreen || dd->back == NULL)) + if ((video_config.waitvsync || video_config.syncrefresh) && window->machine->video().throttled() && (!window->fullscreen || dd->back == NULL)) { result = IDirectDraw7_WaitForVerticalBlank(dd->ddraw, DDWAITVB_BLOCKBEGIN, NULL); if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X waiting for VBLANK\n", (int)result); @@ -1333,7 +1333,7 @@ static void pick_best_mode(win_window_info *window) // determine the refresh rate of the primary screen einfo.target_refresh = 60.0; - const screen_device_config *primary_screen = screen_first(*window->machine->config); + const screen_device_config *primary_screen = window->machine->config->first_screen(); if (primary_screen != NULL) einfo.target_refresh = ATTOSECONDS_TO_HZ(primary_screen->refresh()); printf("Target refresh = %f\n", einfo.target_refresh); diff --git a/src/osd/windows/window.c b/src/osd/windows/window.c index b8c82315822..4572cc8aa7f 100644 --- a/src/osd/windows/window.c +++ b/src/osd/windows/window.c @@ -753,7 +753,7 @@ void winwindow_video_window_update(win_window_info *window) mtlog_add("winwindow_video_window_update: try lock"); // only block if we're throttled - if (video_get_throttle() || timeGetTime() - last_update_time > 250) + if (window->machine->video().throttled() || timeGetTime() - last_update_time > 250) osd_lock_acquire(window->render_lock); else got_lock = osd_lock_try(window->render_lock); @@ -868,7 +868,7 @@ static void set_starting_view(int index, win_window_info *window, const char *vi view = defview; // query the video system to help us pick a view - viewindex = video_get_view_for_target(window->machine, window->target, view, index, video_config.numscreens); + viewindex = window->target->configured_view(view, index, video_config.numscreens); // set the view window->target->set_view(viewindex); |