summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2010-12-02 17:26:38 +0000
committer Aaron Giles <aaron@aarongiles.com>2010-12-02 17:26:38 +0000
commit57ef3a5ed673acfbd93eb4f759b58f5d813c4db5 (patch)
tree4acce003c8c739c1e86e634b8a77b7a393c6ea9c /src
parent3c55c6743652d2f8e82a8f8e78e64e52838f08ba (diff)
Split the screen device into a separate module.
Converted global video routines into a video_manager. Moved video manager initialization earlier in startup.
Diffstat (limited to 'src')
-rw-r--r--src/emu/debug/debugcmd.c4
-rw-r--r--src/emu/debug/debugcpu.c2
-rw-r--r--src/emu/debugger.c2
-rw-r--r--src/emu/diexec.c4
-rw-r--r--src/emu/emu.h1
-rw-r--r--src/emu/emu.mak1
-rw-r--r--src/emu/emupal.c2
-rw-r--r--src/emu/info.c2
-rw-r--r--src/emu/inptport.c2
-rw-r--r--src/emu/machine.c11
-rw-r--r--src/emu/machine.h4
-rw-r--r--src/emu/mame.c2
-rw-r--r--src/emu/mconfig.c11
-rw-r--r--src/emu/mconfig.h5
-rw-r--r--src/emu/render.c76
-rw-r--r--src/emu/render.h3
-rw-r--r--src/emu/rendlay.c2
-rw-r--r--src/emu/screen.c1076
-rw-r--r--src/emu/screen.h305
-rw-r--r--src/emu/sound.c4
-rw-r--r--src/emu/state.h15
-rw-r--r--src/emu/ui.c41
-rw-r--r--src/emu/validity.c2
-rw-r--r--src/emu/video.c3005
-rw-r--r--src/emu/video.h489
-rw-r--r--src/emu/video/tms9928a.c2
-rw-r--r--src/mame/machine/atarigen.c4
-rw-r--r--src/mame/video/cave.c4
-rw-r--r--src/mame/video/cyberbal.c2
-rw-r--r--src/mame/video/gaelco3d.c2
-rw-r--r--src/mame/video/taito_f3.c4
-rw-r--r--src/osd/sdl/drawogl.c4
-rw-r--r--src/osd/sdl/sound.c10
-rw-r--r--src/osd/sdl/window.c2
-rw-r--r--src/osd/windows/drawd3d.c2
-rw-r--r--src/osd/windows/drawdd.c4
-rw-r--r--src/osd/windows/window.c4
37 files changed, 2465 insertions, 2650 deletions
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);