summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/screen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/screen.cpp')
-rw-r--r--src/emu/screen.cpp641
1 files changed, 445 insertions, 196 deletions
diff --git a/src/emu/screen.cpp b/src/emu/screen.cpp
index 103230ba114..0d8e35d6e4c 100644
--- a/src/emu/screen.cpp
+++ b/src/emu/screen.cpp
@@ -2,21 +2,27 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- screen.c
+ screen.cpp
Core MAME screen device.
***************************************************************************/
#include "emu.h"
+#include "screen.h"
+
#include "emuopts.h"
-#include "png.h"
+#include "fileio.h"
+#include "main.h"
+#include "render.h"
#include "rendutil.h"
-#include <nanosvg/src/nanosvg.h>
-#include <nanosvg/src/nanosvgrast.h>
+#include "nanosvg.h"
+#include "png.h"
+
#include <set>
+
//**************************************************************************
// DEBUGGING
//**************************************************************************
@@ -40,7 +46,6 @@ u32 screen_device::m_id_counter = 0;
class screen_device::svg_renderer {
public:
svg_renderer(memory_region *region);
- ~svg_renderer();
int width() const;
int height() const;
@@ -66,10 +71,10 @@ private:
int x0, y0, x1, y1;
};
- NSVGimage *m_image;
- NSVGrasterizer *m_rasterizer;
+ util::nsvg_image_ptr m_image;
+ util::nsvg_rasterizer_ptr m_rasterizer;
std::vector<bool> m_key_state;
- std::vector<std::list<NSVGshape *>> m_keyed_shapes;
+ std::vector<std::vector<NSVGshape *>> m_keyed_shapes;
std::unordered_map<std::string, int> m_key_ids;
int m_key_count;
@@ -91,23 +96,21 @@ private:
screen_device::svg_renderer::svg_renderer(memory_region *region)
{
- char *s = new char[region->bytes()+1];
- memcpy(s, region->base(), region->bytes());
+ const std::unique_ptr<char []> s(new char[region->bytes() + 1]);
+ memcpy(s.get(), region->base(), region->bytes());
s[region->bytes()] = 0;
- m_image = nsvgParse(s, "px", 72);
- delete[] s;
- m_rasterizer = nsvgCreateRasterizer();
+ m_image.reset(nsvgParse(s.get(), "px", 72));
+ m_rasterizer.reset(nsvgCreateRasterizer());
m_key_count = 0;
- for (NSVGshape *shape = m_image->shapes; shape != nullptr; shape = shape->next)
+ for (NSVGshape *shape = m_image->shapes; shape; shape = shape->next)
if(shape->title[0]) {
- auto it = m_key_ids.find(shape->title);
+ const auto it = m_key_ids.find(shape->title);
if(it != m_key_ids.end())
m_keyed_shapes[it->second].push_back(shape);
else {
- int id = m_key_count;
- m_key_count++;
+ const int id = m_key_count++;
m_keyed_shapes.resize(m_key_count);
m_keyed_shapes[id].push_back(shape);
m_key_ids[shape->title] = id;
@@ -122,12 +125,6 @@ screen_device::svg_renderer::svg_renderer(memory_region *region)
osd_printf_verbose("Parsed SVG '%s', aspect ratio %f\n", region->name(), (m_image->height == 0.0f) ? 0 : m_image->width / m_image->height);
}
-screen_device::svg_renderer::~svg_renderer()
-{
- nsvgDeleteRasterizer(m_rasterizer);
- nsvgDelete(m_image);
-}
-
int screen_device::svg_renderer::width() const
{
return int(m_image->width + 0.5);
@@ -149,7 +146,7 @@ void screen_device::svg_renderer::render_state(std::vector<u32> &dest, const std
s->flags &= ~NSVG_FLAGS_VISIBLE;
}
- nsvgRasterize(m_rasterizer, m_image, 0, 0, m_scale, (unsigned char *)&dest[0], m_sx, m_sy, m_sx*4);
+ nsvgRasterize(m_rasterizer.get(), m_image.get(), 0, 0, m_scale, (unsigned char *)&dest[0], m_sx, m_sy, m_sx*4);
// Nanosvg generates non-premultiplied alpha, so remultiply by
// alpha to "blend" against a black background. Plus align the
@@ -542,12 +539,15 @@ screen_device::screen_device(const machine_config &mconfig, const char *tag, dev
, m_yoffset(0.0f)
, m_xscale(1.0f)
, m_yscale(1.0f)
+ , m_screen_update_ind16(*this)
+ , m_screen_update_rgb32(*this)
, m_screen_vblank(*this)
, m_scanline_cb(*this)
, m_palette(*this, finder_base::DUMMY_TAG)
, m_video_attributes(0)
- , m_svg_region(tag)
+ , m_svg_region(*this, DEVICE_SELF)
, m_container(nullptr)
+ , m_max_width(100)
, m_width(100)
, m_height(100)
, m_visarea(0, 99, 0, 99)
@@ -555,6 +555,7 @@ screen_device::screen_device(const machine_config &mconfig, const char *tag, dev
, m_curbitmap(0)
, m_curtexture(0)
, m_changed(true)
+ , m_last_partial_reset(attotime::zero)
, m_last_partial_scan(0)
, m_partial_scan_hpos(0)
, m_color(rgb_t(0xff, 0xff, 0xff, 0xff))
@@ -584,10 +585,81 @@ screen_device::screen_device(const machine_config &mconfig, const char *tag, dev
screen_device::~screen_device()
{
+ destroy_scan_bitmaps();
}
//-------------------------------------------------
+// destroy_scan_bitmaps - destroy per-scanline
+// bitmaps if applicable
+//-------------------------------------------------
+
+void screen_device::destroy_scan_bitmaps()
+{
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
+ {
+ const bool screen16 = !m_screen_update_ind16.isnull();
+ for (int j = 0; j < 2; j++)
+ {
+ for (bitmap_t* bitmap : m_scan_bitmaps[j])
+ {
+ if (screen16)
+ delete (bitmap_ind16*)bitmap;
+ else
+ delete (bitmap_rgb32*)bitmap;
+ }
+ m_scan_bitmaps[j].clear();
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// allocate_scan_bitmaps - allocate per-scanline
+// bitmaps if applicable
+//-------------------------------------------------
+
+void screen_device::allocate_scan_bitmaps()
+{
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
+ {
+ const bool screen16 = !m_screen_update_ind16.isnull();
+ s32 effwidth = std::max(m_max_width, m_visarea.right() + 1);
+ const s32 old_height = (s32)m_scan_widths.size();
+ s32 effheight = std::max(m_height, m_visarea.bottom() + 1);
+ if (old_height < effheight)
+ {
+ for (int i = old_height; i < effheight; i++)
+ {
+ for (int j = 0; j < 2; j++)
+ {
+ if (screen16)
+ m_scan_bitmaps[j].push_back(new bitmap_ind16(effwidth, 1));
+ else
+ m_scan_bitmaps[j].push_back(new bitmap_rgb32(effwidth, 1));
+ }
+ m_scan_widths.push_back(effwidth);
+ }
+ }
+ else
+ {
+ for (int i = old_height - 1; i >= effheight; i--)
+ {
+ for (int j = 0; j < 2; j++)
+ {
+ if (screen16)
+ delete (bitmap_ind16 *)m_scan_bitmaps[j][i];
+ else
+ delete (bitmap_rgb32 *)m_scan_bitmaps[j][i];
+ m_scan_bitmaps[j].erase(m_scan_bitmaps[j].begin() + i);
+ }
+ m_scan_widths.erase(m_scan_widths.begin() + i);
+ }
+ }
+ }
+}
+
+//-------------------------------------------------
// device_validity_check - verify device
// configuration
//-------------------------------------------------
@@ -608,10 +680,15 @@ void screen_device::device_validity_check(validity_checker &valid) const
if (m_screen_update_ind16.isnull() && m_screen_update_rgb32.isnull())
osd_printf_error("Missing SCREEN_UPDATE function\n");
}
+ else
+ {
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
+ osd_printf_error("Non-raster display cannot have a variable width\n");
+ }
- // check for zero frame rate
- if (m_refresh == 0)
- osd_printf_error("Invalid (zero) refresh rate\n");
+ // check for invalid frame rate
+ if (m_refresh == 0 || m_refresh > ATTOSECONDS_PER_SECOND)
+ osd_printf_error("Invalid (under 1Hz) refresh rate\n");
texture_format texformat = !m_screen_update_ind16.isnull() ? TEXFORMAT_PALETTE16 : TEXFORMAT_RGB32;
if (m_palette.finder_tag() != finder_base::DUMMY_TAG)
@@ -694,10 +771,8 @@ std::pair<unsigned, unsigned> screen_device::physical_aspect() const
void screen_device::device_resolve_objects()
{
// bind our handlers
- m_screen_update_ind16.bind_relative_to(*owner());
- m_screen_update_rgb32.bind_relative_to(*owner());
- m_screen_vblank.resolve_safe();
- m_scanline_cb.resolve();
+ m_screen_update_ind16.resolve();
+ m_screen_update_rgb32.resolve();
// assign our format to the palette before it starts
if (m_palette)
@@ -717,11 +792,10 @@ void screen_device::device_start()
if (m_type == SCREEN_TYPE_SVG)
{
- memory_region *reg = owner()->memregion(m_svg_region);
- if (!reg)
- fatalerror("%s: SVG region \"%s\" does not exist\n", tag(), m_svg_region);
- m_svg = std::make_unique<svg_renderer>(reg);
- machine().output().set_notifier(nullptr, svg_renderer::output_notifier, m_svg.get());
+ if (!m_svg_region)
+ fatalerror("%s: SVG region \"%s\" does not exist\n", tag(), m_svg_region.finder_tag());
+ m_svg = std::make_unique<svg_renderer>(m_svg_region);
+ machine().output().set_global_notifier(svg_renderer::output_notifier, m_svg.get());
// don't do this - SVG units are arbitrary and interpreting them as pixels causes bad things to happen
// just render at the size/aspect ratio supplied by the driver
@@ -736,7 +810,8 @@ void screen_device::device_start()
// configure bitmap formats and allocate screen bitmaps
// svg is RGB32 too, and doesn't have any update method
- texture_format texformat = !m_screen_update_ind16.isnull() ? TEXFORMAT_PALETTE16 : TEXFORMAT_RGB32;
+ const bool screen16 = !m_screen_update_ind16.isnull();
+ texture_format texformat = screen16 ? TEXFORMAT_PALETTE16 : TEXFORMAT_RGB32;
for (auto & elem : m_bitmap)
{
@@ -752,8 +827,7 @@ void screen_device::device_start()
m_texture[1]->set_id((u64(m_unique_id) << 57) | 1);
// configure the default cliparea
- render_container::user_settings settings;
- m_container->get_user_settings(settings);
+ render_container::user_settings settings = m_container->get_user_settings();
settings.m_xoffset = m_xoffset;
settings.m_yoffset = m_yoffset;
settings.m_xscale = m_xscale;
@@ -761,15 +835,15 @@ void screen_device::device_start()
m_container->set_user_settings(settings);
// allocate the VBLANK timers
- m_vblank_begin_timer = timer_alloc(TID_VBLANK_START);
- m_vblank_end_timer = timer_alloc(TID_VBLANK_END);
+ m_vblank_begin_timer = timer_alloc(FUNC(screen_device::vblank_begin), this);
+ m_vblank_end_timer = timer_alloc(FUNC(screen_device::vblank_end), this);
// allocate a timer to reset partial updates
- m_scanline0_timer = timer_alloc(TID_SCANLINE0);
+ m_scanline0_timer = timer_alloc(FUNC(screen_device::first_scanline_tick), this);
// allocate a timer to generate per-scanline updates
- if ((m_video_attributes & VIDEO_UPDATE_SCANLINE) != 0 || m_scanline_cb)
- m_scanline_timer = timer_alloc(TID_SCANLINE);
+ if ((m_video_attributes & VIDEO_UPDATE_SCANLINE) != 0 || !m_scanline_cb.isunset())
+ m_scanline_timer = timer_alloc(FUNC(screen_device::scanline_tick), this);
// configure the screen with the default parameters
configure(m_width, m_height, m_visarea, m_refresh);
@@ -779,7 +853,7 @@ void screen_device::device_start()
m_vblank_end_time = attotime(0, m_vblank_period);
// start the timer to generate per-scanline updates
- if ((m_video_attributes & VIDEO_UPDATE_SCANLINE) != 0 || m_scanline_cb)
+ if ((m_video_attributes & VIDEO_UPDATE_SCANLINE) != 0 || !m_scanline_cb.isunset())
m_scanline_timer->adjust(time_until_pos(0));
// create burn-in bitmap
@@ -804,6 +878,7 @@ void screen_device::device_start()
save_item(NAME(m_visarea.min_y));
save_item(NAME(m_visarea.max_x));
save_item(NAME(m_visarea.max_y));
+ save_item(NAME(m_last_partial_reset));
save_item(NAME(m_last_partial_scan));
save_item(NAME(m_frame_period));
save_item(NAME(m_brightness));
@@ -814,9 +889,9 @@ void screen_device::device_start()
save_item(NAME(m_vblank_end_time));
save_item(NAME(m_frame_number));
if (m_oldstyle_vblank_supplied)
- logerror("%s: Deprecated legacy Old Style screen configured (MCFG_SCREEN_VBLANK_TIME), please use MCFG_SCREEN_RAW_PARAMS instead.\n",this->tag());
+ logerror("%s: Deprecated legacy Old Style screen configured (set_vblank_time), please use set_raw instead.\n",this->tag());
- m_is_primary_screen = (this == screen_device_iterator(machine().root_device()).first());
+ m_is_primary_screen = (this == screen_device_enumerator(machine().root_device()).first());
}
@@ -857,46 +932,39 @@ void screen_device::device_post_load()
//-------------------------------------------------
-// device_timer - called whenever a device timer
-// fires
+// timer events
//-------------------------------------------------
-void screen_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+TIMER_CALLBACK_MEMBER(screen_device::first_scanline_tick)
{
- switch (id)
+ // first scanline
+ reset_partial_updates();
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
{
- // signal VBLANK start
- case TID_VBLANK_START:
- vblank_begin();
- break;
-
- // signal VBLANK end
- case TID_VBLANK_END:
- vblank_end();
- break;
-
- // first scanline
- case TID_SCANLINE0:
- reset_partial_updates();
- break;
+ pre_update_scanline(0);
+ }
+}
- // subsequent scanlines when scanline updates are enabled
- case TID_SCANLINE:
- if (m_video_attributes & VIDEO_UPDATE_SCANLINE)
- {
- // force a partial update to the current scanline
- update_partial(param);
- }
- if (m_scanline_cb)
- m_scanline_cb(param);
-
- // compute the next visible scanline
- param++;
- if (param > m_visarea.bottom())
- param = m_visarea.top();
- m_scanline_timer->adjust(time_until_pos(param), param);
- break;
+TIMER_CALLBACK_MEMBER(screen_device::scanline_tick)
+{
+ // subsequent scanlines when scanline updates are enabled
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
+ {
+ pre_update_scanline(param);
+ }
+ if (m_video_attributes & VIDEO_UPDATE_SCANLINE)
+ {
+ // force a partial update to the current scanline
+ update_partial(param);
}
+ if (!m_scanline_cb.isunset())
+ m_scanline_cb(param);
+
+ // compute the next visible scanline
+ param++;
+ if (param > m_visarea.bottom())
+ param = m_visarea.top();
+ m_scanline_timer->adjust(time_until_pos(param), param);
}
@@ -918,11 +986,12 @@ void screen_device::configure(int width, int height, const rectangle &visarea, a
assert(frame_period > 0);
// fill in the new parameters
+ m_max_width = std::max(m_max_width, width);
m_width = width;
m_height = height;
m_visarea = visarea;
- // reallocate bitmap if necessary
+ // reallocate bitmap(s) if necessary
realloc_screen_bitmaps();
// compute timing parameters
@@ -944,7 +1013,7 @@ void screen_device::configure(int width, int height, const rectangle &visarea, a
// call the VBLANK start timer now; otherwise, adjust it for the future
attoseconds_t delta = (machine().time() - m_vblank_start_time).as_attoseconds();
if (delta >= m_frame_period)
- vblank_begin();
+ vblank_begin(0);
else
m_vblank_begin_timer->adjust(time_until_vblank_start());
@@ -972,19 +1041,41 @@ void screen_device::reset_origin(int beamy, int beamx)
m_vblank_end_time = curtime - attotime(0, beamy * m_scantime + beamx * m_pixeltime);
m_vblank_start_time = m_vblank_end_time - attotime(0, m_vblank_period);
+ // if we are resetting relative to (visarea.bottom() + 1, 0) == VBLANK start,
+ // call the VBLANK start timer now; otherwise, adjust it for the future
+ if (beamy == ((m_visarea.bottom() + 1) % m_height) && beamx == 0)
+ vblank_begin(0);
+ else
+ m_vblank_begin_timer->adjust(time_until_vblank_start());
+
// 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)
reset_partial_updates();
else
m_scanline0_timer->adjust(time_until_pos(0));
+}
- // if we are resetting relative to (visarea.bottom() + 1, 0) == VBLANK start,
- // call the VBLANK start timer now; otherwise, adjust it for the future
- if (beamy == ((m_visarea.bottom() + 1) % m_height) && beamx == 0)
- vblank_begin();
- else
- m_vblank_begin_timer->adjust(time_until_vblank_start());
+
+//-------------------------------------------------
+// update_scan_bitmap_size - reallocate the
+// bitmap for a specific scanline
+//-------------------------------------------------
+
+void screen_device::update_scan_bitmap_size(int y)
+{
+ // don't update this line if it exceeds the allocated size, which can happen on initial configuration
+ if (y >= m_scan_widths.size())
+ return;
+
+ // determine effective size to allocate
+ s32 effwidth = std::max(m_max_width, m_visarea.right() + 1);
+
+ if (m_scan_widths[y] == effwidth)
+ return;
+
+ m_scan_bitmaps[m_curbitmap][y]->resize(effwidth, 1);
+ m_scan_widths[y] = effwidth;
}
@@ -1000,10 +1091,11 @@ void screen_device::realloc_screen_bitmaps()
return;
// determine effective size to allocate
- s32 effwidth = std::max(m_width, m_visarea.right() + 1);
+ const bool per_scanline = (m_video_attributes & VIDEO_VARIABLE_WIDTH);
+ s32 effwidth = std::max(per_scanline ? m_max_width : m_width, m_visarea.right() + 1);
s32 effheight = std::max(m_height, m_visarea.bottom() + 1);
- // reize all registered screen bitmaps
+ // resize all registered screen bitmaps
for (auto &item : m_auto_bitmap_list)
item->m_bitmap.resize(effwidth, effheight);
@@ -1015,6 +1107,19 @@ void screen_device::realloc_screen_bitmaps()
}
m_texture[0]->set_bitmap(m_bitmap[0], m_visarea, m_bitmap[0].texformat());
m_texture[1]->set_bitmap(m_bitmap[1], m_visarea, m_bitmap[1].texformat());
+
+ allocate_scan_bitmaps();
+}
+
+
+//-------------------------------------------------
+// pre_update_scanline - check if the bitmap for
+// a specific scanline needs its size updated
+//-------------------------------------------------
+
+void screen_device::pre_update_scanline(int y)
+{
+ update_scan_bitmap_size(y);
}
@@ -1065,11 +1170,17 @@ bool screen_device::update_partial(int scanline)
return false;
}
+ // skip if we already rendered this frame
+ // this can happen if a cpu timeslice that called update_partial is in the previous frame while scanline 0 already started
+ if (m_last_partial_scan == 0 && m_last_partial_reset > machine().time())
+ {
+ LOG_PARTIAL_UPDATES(("skipped because frame was already rendered\n"));
+ return false;
+ }
+
// set the range of scanlines to render
rectangle clip(m_visarea);
- clip.sety(
- (std::max)(clip.top(), m_last_partial_scan),
- (std::min)(clip.bottom(), scanline));
+ clip.sety((std::max)(clip.top(), m_last_partial_scan), (std::min)(clip.bottom(), scanline));
// skip if entirely outside of visible area
if (clip.top() > clip.bottom())
@@ -1080,32 +1191,56 @@ bool screen_device::update_partial(int scanline)
// otherwise, render
LOG_PARTIAL_UPDATES(("updating %d-%d\n", clip.top(), clip.bottom()));
- g_profiler.start(PROFILER_VIDEO);
- u32 flags;
- if (m_type != SCREEN_TYPE_SVG)
+ u32 flags = 0;
{
- screen_bitmap &curbitmap = m_bitmap[m_curbitmap];
- switch (curbitmap.format())
+ auto profile = g_profiler.start(PROFILER_VIDEO);
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
{
- default:
- case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, curbitmap.as_ind16(), clip); break;
- case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, curbitmap.as_rgb32(), clip); break;
+ rectangle scan_clip(clip);
+ for (int y = clip.top(); y <= clip.bottom(); y++)
+ {
+ scan_clip.sety(y, y);
+ pre_update_scanline(y);
+
+ screen_bitmap &curbitmap = m_bitmap[m_curbitmap];
+ switch (curbitmap.format())
+ {
+ default:
+ case BITMAP_FORMAT_IND16: flags |= m_screen_update_ind16(*this, *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][y], scan_clip); break;
+ case BITMAP_FORMAT_RGB32: flags |= m_screen_update_rgb32(*this, *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][y], scan_clip); break;
+ }
+
+ m_partial_updates_this_frame++;
+ }
}
+ else
+ {
+ if (m_type != SCREEN_TYPE_SVG)
+ {
+ screen_bitmap &curbitmap = m_bitmap[m_curbitmap];
+ switch (curbitmap.format())
+ {
+ default:
+ case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, curbitmap.as_ind16(), clip); break;
+ case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, curbitmap.as_rgb32(), clip); break;
+ }
+ }
+ else
+ {
+ flags = m_svg->render(*this, m_bitmap[m_curbitmap].as_rgb32(), clip);
+ }
+ m_partial_updates_this_frame++;
+ }
+ // stop profiling
}
- else
- {
- flags = m_svg->render(*this, m_bitmap[m_curbitmap].as_rgb32(), 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;
// remember where we left off
m_last_partial_scan = scanline + 1;
+ m_partial_scan_hpos = 0;
return true;
}
@@ -1139,6 +1274,28 @@ void screen_device::update_now()
int current_hpos = hpos();
rectangle clip = m_visarea;
+ // skip if we already rendered this line
+ if (current_vpos < m_last_partial_scan)
+ {
+ LOG_PARTIAL_UPDATES(("skipped because line was already rendered\n"));
+ return;
+ }
+
+ // if beam position is the same, there's nothing to update
+ if (current_vpos == m_last_partial_scan && current_hpos == m_partial_scan_hpos)
+ {
+ LOG_PARTIAL_UPDATES(("skipped because beam position is unchanged\n"));
+ return;
+ }
+
+ // skip if we already rendered this frame
+ // this can happen if a cpu timeslice that called update_now is in the previous frame while scanline 0 already started
+ if (m_last_partial_scan == 0 && m_partial_scan_hpos == 0 && m_last_partial_reset > machine().time())
+ {
+ LOG_PARTIAL_UPDATES(("skipped because frame was already rendered\n"));
+ return;
+ }
+
LOG_PARTIAL_UPDATES(("update_now(): Y=%d, X=%d, last partial %d, partial hpos %d (vis %d %d)\n", current_vpos, current_hpos, m_last_partial_scan, m_partial_scan_hpos, m_visarea.right(), m_visarea.bottom()));
// start off by doing a partial update up to the line before us, in case that was necessary
@@ -1148,30 +1305,46 @@ void screen_device::update_now()
if (m_partial_scan_hpos > 0)
{
// now finish the previous partial scanline
- clip.set(
- (std::max)(clip.left(), m_partial_scan_hpos),
- clip.right(),
- (std::max)(clip.top(), m_last_partial_scan),
- (std::min)(clip.bottom(), m_last_partial_scan));
+ clip.set((std::max)(clip.left(), m_partial_scan_hpos),
+ clip.right(),
+ (std::max)(clip.top(), m_last_partial_scan),
+ (std::min)(clip.bottom(), m_last_partial_scan));
// if there's something to draw, do it
if (!clip.empty())
{
- g_profiler.start(PROFILER_VIDEO);
+ auto profile = g_profiler.start(PROFILER_VIDEO);
+ u32 flags = 0;
screen_bitmap &curbitmap = m_bitmap[m_curbitmap];
- switch (curbitmap.format())
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
{
- default:
- case BITMAP_FORMAT_IND16: m_screen_update_ind16(*this, curbitmap.as_ind16(), clip); break;
- case BITMAP_FORMAT_RGB32: m_screen_update_rgb32(*this, curbitmap.as_rgb32(), clip); break;
+ pre_update_scanline(m_last_partial_scan);
+ switch (curbitmap.format())
+ {
+ default:
+ case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][m_last_partial_scan], clip); break;
+ case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][m_last_partial_scan], clip); break;
+ }
+ }
+ else
+ {
+ switch (curbitmap.format())
+ {
+ default:
+ case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, curbitmap.as_ind16(), clip); break;
+ case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, curbitmap.as_rgb32(), clip); break;
+ }
}
m_partial_updates_this_frame++;
- g_profiler.stop();
- m_partial_scan_hpos = 0;
- m_last_partial_scan++;
+
+ // if we modified the bitmap, we have to commit
+ m_changed |= ~flags & UPDATE_HAS_NOT_CHANGED;
}
+
+ m_partial_scan_hpos = 0;
+ m_last_partial_scan++;
}
if (current_vpos > m_last_partial_scan)
{
@@ -1180,47 +1353,54 @@ void screen_device::update_now()
}
// now draw this partial scanline
- clip = m_visarea;
-
- clip.set(
- (std::max)(clip.left(), m_partial_scan_hpos),
- (std::min)(clip.right(), current_hpos),
- (std::max)(clip.top(), current_vpos),
- (std::min)(clip.bottom(), current_vpos));
-
- // and if there's something to draw, do it
- if (!clip.empty())
+ if (current_hpos > 0)
{
- g_profiler.start(PROFILER_VIDEO);
+ clip = m_visarea;
- LOG_PARTIAL_UPDATES(("doing scanline partial draw: Y %d X %d-%d\n", clip.bottom(), clip.left(), clip.right()));
+ clip.set((std::max)(clip.left(), m_partial_scan_hpos),
+ (std::min)(clip.right(), current_hpos - 1),
+ (std::max)(clip.top(), current_vpos),
+ (std::min)(clip.bottom(), current_vpos));
- u32 flags;
- screen_bitmap &curbitmap = m_bitmap[m_curbitmap];
- switch (curbitmap.format())
+ // and if there's something to draw, do it
+ if (!clip.empty())
{
- default:
- case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, curbitmap.as_ind16(), clip); break;
- case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, curbitmap.as_rgb32(), clip); break;
- }
+ auto profile = g_profiler.start(PROFILER_VIDEO);
- m_partial_updates_this_frame++;
- g_profiler.stop();
+ LOG_PARTIAL_UPDATES(("doing scanline partial draw: Y %d X %d-%d\n", clip.bottom(), clip.left(), clip.right()));
- // if we modified the bitmap, we have to commit
- m_changed |= ~flags & UPDATE_HAS_NOT_CHANGED;
+ u32 flags = 0;
+ screen_bitmap &curbitmap = m_bitmap[m_curbitmap];
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
+ {
+ pre_update_scanline(current_vpos);
+ switch (curbitmap.format())
+ {
+ default:
+ case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][current_vpos], clip); break;
+ case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][current_vpos], clip); break;
+ }
+ }
+ else
+ {
+ switch (curbitmap.format())
+ {
+ default:
+ case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, curbitmap.as_ind16(), clip); break;
+ case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, curbitmap.as_rgb32(), clip); break;
+ }
+ }
- // remember where we left off
- m_partial_scan_hpos = current_hpos;
- m_last_partial_scan = current_vpos;
+ m_partial_updates_this_frame++;
- // if we completed the line, mark it so
- if (current_hpos >= m_visarea.right())
- {
- m_partial_scan_hpos = 0;
- m_last_partial_scan = current_vpos + 1;
+ // if we modified the bitmap, we have to commit
+ m_changed |= ~flags & UPDATE_HAS_NOT_CHANGED;
}
}
+
+ // remember where we left off
+ m_partial_scan_hpos = current_hpos;
+ m_last_partial_scan = current_vpos;
}
@@ -1231,6 +1411,7 @@ void screen_device::update_now()
void screen_device::reset_partial_updates()
{
+ m_last_partial_reset = machine().time();
m_last_partial_scan = 0;
m_partial_scan_hpos = 0;
m_partial_updates_this_frame = 0;
@@ -1255,21 +1436,28 @@ u32 screen_device::pixel(s32 x, s32 y)
if (x < 0 || y < 0 || x >= srcwidth || y >= srcheight)
return 0;
+ const bool per_scanline = (m_video_attributes & VIDEO_VARIABLE_WIDTH);
+
switch (curbitmap.format())
{
case BITMAP_FORMAT_IND16:
{
- bitmap_ind16 &srcbitmap = curbitmap.as_ind16();
- const u16 src = srcbitmap.pix(y, x);
+ bitmap_ind16 &srcbitmap = per_scanline ? *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][y] : curbitmap.as_ind16();
+ const u16 src = per_scanline ? srcbitmap.pix(0, x) : srcbitmap.pix(y, x);
const rgb_t *palette = m_palette->palette()->entry_list_adjusted();
return (u32)palette[src];
}
case BITMAP_FORMAT_RGB32:
{
- // iterate over rows in the destination
- bitmap_rgb32 &srcbitmap = curbitmap.as_rgb32();
- return (u32)srcbitmap.pix(y, x);
+ if (per_scanline)
+ {
+ return (u32)(*(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][y]).pix(0, x);
+ }
+ else
+ {
+ return (u32)curbitmap.as_rgb32().pix(y, x);
+ }
}
default:
@@ -1291,15 +1479,17 @@ void screen_device::pixels(u32 *buffer)
const rectangle &visarea = visible_area();
+ const bool per_scanline = (m_video_attributes & VIDEO_VARIABLE_WIDTH);
+
switch (curbitmap.format())
{
case BITMAP_FORMAT_IND16:
{
- bitmap_ind16 &srcbitmap = curbitmap.as_ind16();
const rgb_t *palette = m_palette->palette()->entry_list_adjusted();
for (int y = visarea.min_y; y <= visarea.max_y; y++)
{
- const u16 *src = &srcbitmap.pix(y, visarea.min_x);
+ bitmap_ind16 &srcbitmap = per_scanline ? *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][y] : curbitmap.as_ind16();
+ const u16 *src = &srcbitmap.pix(per_scanline ? 0 : y, visarea.min_x);
for (int x = visarea.min_x; x <= visarea.max_x; x++)
{
*buffer++ = palette[*src++];
@@ -1310,10 +1500,10 @@ void screen_device::pixels(u32 *buffer)
case BITMAP_FORMAT_RGB32:
{
- bitmap_rgb32 &srcbitmap = curbitmap.as_rgb32();
for (int y = visarea.min_y; y <= visarea.max_y; y++)
{
- const u32 *src = &srcbitmap.pix(y, visarea.min_x);
+ bitmap_rgb32 &srcbitmap = per_scanline ? *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][y] : curbitmap.as_rgb32();
+ const u32 *src = &srcbitmap.pix(per_scanline ? 0 : y, visarea.min_x);
for (int x = visarea.min_x; x <= visarea.max_x; x++)
{
*buffer++ = *src++;
@@ -1462,7 +1652,7 @@ void screen_device::register_screen_bitmap(bitmap_t &bitmap)
// signal the VBLANK period has begun
//-------------------------------------------------
-void screen_device::vblank_begin()
+TIMER_CALLBACK_MEMBER(screen_device::vblank_begin)
{
// reset the starting VBLANK time
m_vblank_start_time = machine().time();
@@ -1482,7 +1672,7 @@ void screen_device::vblank_begin()
// if no VBLANK period, call the VBLANK end callback immediately, otherwise reset the timer
if (m_vblank_period == 0)
- vblank_end();
+ vblank_end(0);
else
m_vblank_end_timer->adjust(time_until_vblank_end());
}
@@ -1493,7 +1683,7 @@ void screen_device::vblank_begin()
// signal the VBLANK period has ended
//-------------------------------------------------
-void screen_device::vblank_end()
+TIMER_CALLBACK_MEMBER(screen_device::vblank_end)
{
// call the screen specific callbacks
for (auto &item : m_callback_list)
@@ -1510,6 +1700,58 @@ void screen_device::vblank_end()
//-------------------------------------------------
+// create_composited_bitmap - composite scanline
+// bitmaps into the output bitmap
+//-------------------------------------------------
+
+void screen_device::create_composited_bitmap()
+{
+ screen_bitmap &curbitmap = m_bitmap[m_curtexture];
+ if (!curbitmap.valid())
+ return;
+
+ s32 dstwidth = std::max(m_max_width, m_visarea.right() + 1);
+ int dstheight = curbitmap.height();
+
+ switch (curbitmap.format())
+ {
+ default:
+ case BITMAP_FORMAT_IND16:
+ {
+ for (int y = 0; y < dstheight; y++)
+ {
+ const bitmap_ind16 &srcbitmap = *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][y];
+ u16 *dst = &curbitmap.as_ind16().pix(y);
+ const u16 *src = &srcbitmap.pix(0);
+ const int dx = (m_scan_widths[y] << 15) / dstwidth;
+ for (int x = 0; x < m_scan_widths[y]; x += dx)
+ {
+ *dst++ = src[x >> 15];
+ }
+ }
+ break;
+ }
+
+ case BITMAP_FORMAT_RGB32:
+ {
+ for (int y = 0; y < dstheight; y++)
+ {
+ const bitmap_rgb32 &srcbitmap = *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][y];
+ u32 *dst = &curbitmap.as_rgb32().pix(y);
+ const u32 *src = &srcbitmap.pix(0);
+ const int dx = (m_scan_widths[y] << 15) / dstwidth;
+ for (int x = 0; x < dstwidth << 15; x += dx)
+ {
+ *dst++ = src[x >> 15];
+ }
+ }
+ break;
+ }
+ }
+}
+
+
+//-------------------------------------------------
// update_quads - set up the quads for this
// screen
//-------------------------------------------------
@@ -1525,6 +1767,10 @@ bool screen_device::update_quads()
// if we're not skipping the frame and if the screen actually changed, then update the texture
if (!machine().video().skip_this_frame() && m_changed)
{
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
+ {
+ create_composited_bitmap();
+ }
m_texture[m_curbitmap]->set_bitmap(m_bitmap[m_curbitmap], m_visarea, m_bitmap[m_curbitmap].texformat());
m_curtexture = m_curbitmap;
m_curbitmap = 1 - m_curbitmap;
@@ -1552,7 +1798,8 @@ bool screen_device::update_quads()
void screen_device::update_burnin()
{
-// TODO: other than being unnecessary (we should use our rand function first off), this is a simplification of how analog signals really works!
+// TODO: other than being unnecessary, this is a simplification of how analog signals really works!
+// It's important not to use machine().rand() here, it can cause machine().rand() used in emulation to desync.
#undef rand
if (!m_burnin.valid())
return;
@@ -1569,8 +1816,8 @@ void screen_device::update_burnin()
int ystep = (srcheight << 16) / dstheight;
int xstart = (u32(rand()) % 32767) * xstep / 32767;
int ystart = (u32(rand()) % 32767) * ystep / 32767;
- int srcx, srcy;
- int x, y;
+
+ bool per_scanline = (m_video_attributes & VIDEO_VARIABLE_WIDTH);
switch (curbitmap.format())
{
@@ -1578,13 +1825,13 @@ void screen_device::update_burnin()
case BITMAP_FORMAT_IND16:
{
// iterate over rows in the destination
- bitmap_ind16 &srcbitmap = curbitmap.as_ind16();
- for (y = 0, srcy = ystart; y < dstheight; y++, srcy += ystep)
+ for (int y = 0, srcy = ystart; y < dstheight; y++, srcy += ystep)
{
- u64 *dst = &m_burnin.pix64(y);
- const u16 *src = &srcbitmap.pix16(srcy >> 16);
- const rgb_t *palette = m_palette->palette()->entry_list_adjusted();
- for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep)
+ const bitmap_ind16 &srcbitmap = per_scanline ? *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][y] : curbitmap.as_ind16();
+ u64 *const dst = &m_burnin.pix(y);
+ u16 const *const src = &srcbitmap.pix(per_scanline ? 0 : (srcy >> 16));
+ rgb_t const *const palette = m_palette->palette()->entry_list_adjusted();
+ for (int x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep)
{
rgb_t pixel = palette[src[srcx >> 16]];
dst[x] += pixel.g() + pixel.r() + pixel.b();
@@ -1596,12 +1843,12 @@ void screen_device::update_burnin()
case BITMAP_FORMAT_RGB32:
{
// iterate over rows in the destination
- bitmap_rgb32 &srcbitmap = curbitmap.as_rgb32();
- for (y = 0, srcy = ystart; y < dstheight; y++, srcy += ystep)
+ for (int y = 0, srcy = ystart; y < dstheight; y++, srcy += ystep)
{
- u64 *dst = &m_burnin.pix64(y);
- const u32 *src = &srcbitmap.pix32(srcy >> 16);
- for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep)
+ const bitmap_rgb32 &srcbitmap = per_scanline ? *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][y] : curbitmap.as_rgb32();
+ u64 *const dst = &m_burnin.pix(y);
+ u32 const *const src = &srcbitmap.pix(per_scanline ? 0 : (srcy >> 16));
+ for (int x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep)
{
rgb_t pixel = src[srcx >> 16];
dst[x] += pixel.g() + pixel.r() + pixel.b();
@@ -1629,7 +1876,7 @@ void screen_device::finalize_burnin()
m_visarea.top() * m_burnin.height() / m_height,
m_visarea.bottom() * m_burnin.height() / m_height);
- // wrap a bitmap around the memregion we care about
+ // wrap a bitmap around the subregion we care about
bitmap_argb32 finalmap(scaledvis.width(), scaledvis.height());
int srcwidth = m_burnin.width();
int srcheight = m_burnin.height();
@@ -1643,7 +1890,7 @@ void screen_device::finalize_burnin()
u64 maxval = 0;
for (int y = 0; y < srcheight; y++)
{
- u64 *src = &m_burnin.pix64(y);
+ u64 const *const src = &m_burnin.pix(y);
for (int x = 0; x < srcwidth; x++)
{
minval = std::min(minval, src[x]);
@@ -1657,8 +1904,8 @@ void screen_device::finalize_burnin()
// now normalize and convert to RGB
for (int y = 0, srcy = 0; y < dstheight; y++, srcy += ystep)
{
- u64 *src = &m_burnin.pix64(srcy >> 16);
- u32 *dst = &finalmap.pix32(y);
+ u64 const *const src = &m_burnin.pix(srcy >> 16);
+ u32 *const dst = &finalmap.pix(y);
for (int x = 0, srcx = 0; x < dstwidth; x++, srcx += xstep)
{
int brightness = u64(maxval - src[srcx >> 16]) * 255 / (maxval - minval);
@@ -1670,26 +1917,23 @@ void screen_device::finalize_burnin()
// compute the name and create the file
emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
- osd_file::error filerr = file.open(machine().basename(), PATH_SEPARATOR "burnin-", this->tag()+1, ".png") ;
- if (filerr == osd_file::error::NONE)
+ std::error_condition const filerr = file.open(util::string_format("%s" PATH_SEPARATOR "burnin-%s.png", machine().basename(), tag() + 1));
+ if (!filerr)
{
- png_info pnginfo;
- char text[256];
+ util::png_info pnginfo;
// add two text entries describing the image
- sprintf(text,"%s %s", emulator_info::get_appname(), emulator_info::get_build_version());
- pnginfo.add_text("Software", text);
- sprintf(text, "%s %s", machine().system().manufacturer, machine().system().type.fullname());
- pnginfo.add_text("System", text);
+ pnginfo.add_text("Software", util::string_format("%s %s", emulator_info::get_appname(), emulator_info::get_build_version()));
+ pnginfo.add_text("System", util::string_format("%s %s", machine().system().manufacturer, machine().system().type.fullname()));
// now do the actual work
- png_write_bitmap(file, &pnginfo, finalmap, 0, nullptr);
+ util::png_write_bitmap(file, &pnginfo, finalmap, 0, nullptr);
}
}
//-------------------------------------------------
-// finalize_burnin - finalize the burnin bitmap
+// load_effect_overlay -
//-------------------------------------------------
void screen_device::load_effect_overlay(const char *filename)
@@ -1702,10 +1946,15 @@ void screen_device::load_effect_overlay(const char *filename)
fullname.append(".png");
// load the file
+ m_screen_overlay_bitmap.reset();
emu_file file(machine().options().art_path(), OPEN_FLAG_READ);
- render_load_png(m_screen_overlay_bitmap, file, nullptr, fullname.c_str());
+ if (!file.open(fullname))
+ {
+ render_load_png(m_screen_overlay_bitmap, file);
+ file.close();
+ }
if (m_screen_overlay_bitmap.valid())
m_container->set_overlay(&m_screen_overlay_bitmap);
else
- osd_printf_warning("Unable to load effect PNG file '%s'\n", fullname.c_str());
+ osd_printf_warning("Unable to load effect PNG file '%s'\n", fullname);
}