summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--src/devices/video/snes_ppu.cpp10
-rw-r--r--src/emu/screen.cpp298
-rw-r--r--src/emu/screen.h30
-rw-r--r--src/emu/video.h1
-rw-r--r--src/mame/drivers/snes.cpp1
-rw-r--r--src/mame/machine/snes.cpp4
6 files changed, 274 insertions, 70 deletions
diff --git a/src/devices/video/snes_ppu.cpp b/src/devices/video/snes_ppu.cpp
index 52e1492e483..fc6f5fed2ed 100644
--- a/src/devices/video/snes_ppu.cpp
+++ b/src/devices/video/snes_ppu.cpp
@@ -1865,7 +1865,7 @@ void snes_ppu_device::refresh_scanline( bitmap_rgb32 &bitmap, uint16_t curline )
if (m_screen_disabled) /* screen is forced blank */
for (int x = 0; x < SNES_SCR_WIDTH * 2; x++)
- bitmap.pix32(curline, x) = rgb_t::black();
+ bitmap.pix32(0, x) = rgb_t::black();
else
{
/* Update clip window masks if necessary */
@@ -1947,8 +1947,8 @@ void snes_ppu_device::refresh_scanline( bitmap_rgb32 &bitmap, uint16_t curline )
int g = (((c & 0x3e0) >> 5) * fade) >> 4;
int b = (((c & 0x7c00) >> 10) * fade) >> 4;
- bitmap.pix32(curline, x * 2 + 0) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
- bitmap.pix32(curline, x * 2 + 1) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
+ bitmap.pix32(0, x * 2 + 0) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
+ bitmap.pix32(0, x * 2 + 1) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
}
else
{
@@ -1986,7 +1986,7 @@ void snes_ppu_device::refresh_scanline( bitmap_rgb32 &bitmap, uint16_t curline )
int g = (((c & 0x3e0) >> 5) * fade) >> 4;
int b = (((c & 0x7c00) >> 10) * fade) >> 4;
- bitmap.pix32(curline, x * 2 + 0) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
+ bitmap.pix32(0, x * 2 + 0) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
prev_colour = tmp_col0;
/* average the second pixel if required, or draw it directly*/
@@ -1999,7 +1999,7 @@ void snes_ppu_device::refresh_scanline( bitmap_rgb32 &bitmap, uint16_t curline )
g = (((c & 0x3e0) >> 5) * fade) >> 4;
b = (((c & 0x7c00) >> 10) * fade) >> 4;
- bitmap.pix32(curline, x * 2 + 1) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
+ bitmap.pix32(0, x * 2 + 1) = rgb_t(pal5bit(r), pal5bit(g), pal5bit(b));
prev_colour = tmp_col1;
}
}
diff --git a/src/emu/screen.cpp b/src/emu/screen.cpp
index db6f7c91ad4..7a342662c56 100644
--- a/src/emu/screen.cpp
+++ b/src/emu/screen.cpp
@@ -531,7 +531,12 @@ void screen_device::svg_renderer::rebuild_cache()
//-------------------------------------------------
screen_device::screen_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
- : device_t(mconfig, SCREEN, tag, owner, clock)
+ : screen_device(mconfig, SCREEN, tag, owner, clock)
+{
+}
+
+screen_device::screen_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, type, tag, owner, clock)
, m_type(SCREEN_TYPE_RASTER)
, m_orientation(ROT0)
, m_phys_aspect(0U, 0U)
@@ -548,6 +553,7 @@ screen_device::screen_device(const machine_config &mconfig, const char *tag, dev
, m_video_attributes(0)
, 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)
@@ -608,6 +614,11 @@ 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)
@@ -735,7 +746,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)
{
@@ -750,6 +762,25 @@ void screen_device::device_start()
m_texture[1] = machine().render().texture_alloc();
m_texture[1]->set_id((u64(m_unique_id) << 57) | 1);
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
+ {
+ for (int i = 0; i < m_height; i++)
+ {
+ for (int j = 0; j < 2; j++)
+ {
+ if (screen16)
+ {
+ m_scan_bitmaps[j].push_back(new bitmap_ind16(m_width, 1));
+ }
+ else
+ {
+ m_scan_bitmaps[j].push_back(new bitmap_rgb32(m_width, 1));
+ }
+ }
+ m_scan_widths.push_back(m_width);
+ }
+ }
+
// configure the default cliparea
render_container::user_settings settings;
m_container->get_user_settings(settings);
@@ -877,10 +908,18 @@ void screen_device::device_timer(emu_timer &timer, device_timer_id id, int param
// first scanline
case TID_SCANLINE0:
reset_partial_updates();
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
+ {
+ pre_update_scanline(0);
+ }
break;
// subsequent scanlines when scanline updates are enabled
case TID_SCANLINE:
+ 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
@@ -917,12 +956,20 @@ 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
- realloc_screen_bitmaps();
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
+ {
+ update_scan_bitmap_size(vpos());
+ }
+ else
+ {
+ realloc_screen_bitmaps();
+ }
// compute timing parameters
m_frame_period = frame_period;
@@ -988,6 +1035,29 @@ void screen_device::reset_origin(int beamy, int beamx)
//-------------------------------------------------
+// update_scan_bitmap_size - reallocate the
+// bitmap for a specific scanline
+//-------------------------------------------------
+
+void screen_device::update_scan_bitmap_size(int y)
+{
+ // determine effective size to allocate
+ s32 effwidth = std::max(m_max_width, m_visarea.right() + 1);
+
+ if (machine().input().code_pressed(KEYCODE_Q))
+ {
+ printf("Updating scan bitmap %d to %d (%d vs. %d)\n", y, effwidth, 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;
+}
+
+
+//-------------------------------------------------
// realloc_screen_bitmaps - reallocate bitmaps
// and textures as necessary
//-------------------------------------------------
@@ -999,7 +1069,8 @@ 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
@@ -1018,6 +1089,17 @@ void screen_device::realloc_screen_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);
+}
+
+
+//-------------------------------------------------
// set_visible_area - just set the visible area
//-------------------------------------------------
@@ -1066,9 +1148,7 @@ bool screen_device::update_partial(int scanline)
// 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())
@@ -1081,23 +1161,45 @@ bool screen_device::update_partial(int scanline)
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;
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
{
- screen_bitmap &curbitmap = m_bitmap[m_curbitmap];
- switch (curbitmap.format())
+ rectangle scan_clip(clip);
+ for (int y = clip.top(); y <= clip.bottom(); y++)
{
- 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;
+ 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
{
- flags = m_svg->render(*this, m_bitmap[m_curbitmap].as_rgb32(), clip);
+ 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++;
}
- m_partial_updates_this_frame++;
g_profiler.stop();
// if we modified the bitmap, we have to commit
@@ -1147,11 +1249,10 @@ 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())
@@ -1159,11 +1260,24 @@ void screen_device::update_now()
g_profiler.start(PROFILER_VIDEO);
screen_bitmap &curbitmap = m_bitmap[m_curbitmap];
- switch (curbitmap.format())
+ if (m_video_attributes & VIDEO_VARIABLE_WIDTH)
+ {
+ pre_update_scanline(m_last_partial_scan);
+ switch (curbitmap.format())
+ {
+ default:
+ case BITMAP_FORMAT_IND16: m_screen_update_ind16(*this, *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][m_last_partial_scan], clip); break;
+ case BITMAP_FORMAT_RGB32: m_screen_update_rgb32(*this, *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][m_last_partial_scan], clip); break;
+ }
+ }
+ else
{
- 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;
+ switch (curbitmap.format())
+ {
+ 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;
+ }
}
m_partial_updates_this_frame++;
@@ -1181,11 +1295,10 @@ 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));
+ 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())
@@ -1194,13 +1307,26 @@ void screen_device::update_now()
LOG_PARTIAL_UPDATES(("doing scanline partial draw: Y %d X %d-%d\n", clip.bottom(), clip.left(), clip.right()));
- u32 flags;
+ 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: 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;
+ 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;
+ }
}
m_partial_updates_this_frame++;
@@ -1254,21 +1380,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:
@@ -1290,15 +1423,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++];
@@ -1309,10 +1444,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++;
@@ -1509,6 +1644,57 @@ 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++)
+ {
+ bitmap_ind16 &srcbitmap = *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][y];
+ u16 *dst = &curbitmap.as_ind16().pix16(y);
+ const u16 *src = &srcbitmap.pix16(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];
+ }
+ }
+ }
+
+ case BITMAP_FORMAT_RGB32:
+ {
+ for (int y = 0; y < dstheight; y++)
+ {
+ bitmap_rgb32 &srcbitmap = *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][y];
+ u32 *dst = &curbitmap.as_rgb32().pix32(y);
+ const u32 *src = &srcbitmap.pix32(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
//-------------------------------------------------
@@ -1524,6 +1710,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;
@@ -1568,8 +1758,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())
{
@@ -1577,13 +1767,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)
{
+ bitmap_ind16 &srcbitmap = per_scanline ? *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][y] : curbitmap.as_ind16();
u64 *dst = &m_burnin.pix64(y);
- const u16 *src = &srcbitmap.pix16(srcy >> 16);
+ const u16 *src = &srcbitmap.pix16(per_scanline ? 0 : (srcy >> 16));
const rgb_t *palette = m_palette->palette()->entry_list_adjusted();
- for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep)
+ 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();
@@ -1595,12 +1785,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)
{
+ bitmap_rgb32 &srcbitmap = per_scanline ? *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][y] : curbitmap.as_rgb32();
u64 *dst = &m_burnin.pix64(y);
- const u32 *src = &srcbitmap.pix32(srcy >> 16);
- for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep)
+ const u32 *src = &srcbitmap.pix32(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();
diff --git a/src/emu/screen.h b/src/emu/screen.h
index 6754d66f071..cf6201b4acf 100644
--- a/src/emu/screen.h
+++ b/src/emu/screen.h
@@ -63,6 +63,9 @@ constexpr u32 UPDATE_HAS_NOT_CHANGED = 0x0001; // the video has not changed
@def VIDEO_UPDATE_SCANLINE
calls VIDEO_UPDATE for every visible scanline, even for skipped frames
+ @def VIDEO_VARIABLE_WIDTH
+ causes the screen to construct its final bitmap from a composite upscale of individual scanline bitmaps
+
@}
*/
@@ -72,6 +75,7 @@ constexpr u32 VIDEO_UPDATE_AFTER_VBLANK = 0x0004;
constexpr u32 VIDEO_SELF_RENDER = 0x0008;
constexpr u32 VIDEO_ALWAYS_UPDATE = 0x0080;
constexpr u32 VIDEO_UPDATE_SCANLINE = 0x0100;
+constexpr u32 VIDEO_VARIABLE_WIDTH = 0x0200;
//**************************************************************************
@@ -182,7 +186,7 @@ public:
set_type(type);
set_color(color);
}
- ~screen_device();
+ virtual ~screen_device();
// configuration readers
screen_type_enum screen_type() const { return m_type; }
@@ -224,7 +228,7 @@ public:
/// \param [in] vbstart Index of first line in vertical blanking
/// period after visible lines.
/// \return Reference to device for method chaining.
- screen_device &set_raw(u32 pixclock, u16 htotal, u16 hbend, u16 hbstart, u16 vtotal, u16 vbend, u16 vbstart)
+ virtual screen_device &set_raw(u32 pixclock, u16 htotal, u16 hbend, u16 hbstart, u16 vtotal, u16 vbend, u16 vbstart)
{
assert(pixclock != 0);
m_clock = pixclock;
@@ -389,7 +393,7 @@ public:
// beam positioning and state
int vpos() const;
- int hpos() const;
+ virtual int hpos() const;
DECLARE_READ_LINE_MEMBER(vblank) const { return (machine().time() < m_vblank_end_time) ? 1 : 0; }
DECLARE_READ_LINE_MEMBER(hblank) const { int const curpos = hpos(); return (curpos < m_visarea.left() || curpos > m_visarea.right()) ? 1 : 0; }
@@ -403,14 +407,14 @@ public:
u64 frame_number() const { return m_frame_number; }
// pixel-level access
- u32 pixel(s32 x, s32 y);
- void pixels(u32* buffer);
+ virtual u32 pixel(s32 x, s32 y);
+ virtual void pixels(u32* buffer);
// updating
int partial_updates() const { return m_partial_updates_this_frame; }
- bool update_partial(int scanline);
- void update_now();
- void reset_partial_updates();
+ virtual bool update_partial(int scanline);
+ virtual void update_now();
+ virtual void reset_partial_updates();
// additional helpers
void register_vblank_callback(vblank_state_delegate vblank_callback);
@@ -424,7 +428,9 @@ public:
static constexpr int DEFAULT_FRAME_RATE = 60;
static const attotime DEFAULT_FRAME_PERIOD;
-private:
+protected:
+ screen_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);
+
class svg_renderer;
// timer IDs
@@ -453,6 +459,9 @@ private:
void vblank_end();
void finalize_burnin();
void load_effect_overlay(const char *filename);
+ void update_scan_bitmap_size(int y);
+ void pre_update_scanline(int y);
+ void create_composited_bitmap();
// inline configuration data
screen_type_enum m_type; // type of screen
@@ -475,14 +484,17 @@ private:
render_container * m_container; // pointer to our container
std::unique_ptr<svg_renderer> m_svg; // the svg renderer
// dimensions
+ int m_max_width; // maximum width encountered
int m_width; // current width (HTOTAL)
int m_height; // current height (VTOTAL)
rectangle m_visarea; // current visible area (HBLANK end/start, VBLANK end/start)
+ std::vector<int> m_scan_widths; // current width, in samples, of each individual scanline
// textures and bitmaps
texture_format m_texformat; // texture format
render_texture * m_texture[2]; // 2x textures for the screen bitmap
screen_bitmap m_bitmap[2]; // 2x bitmaps for rendering
+ std::vector<bitmap_t *> m_scan_bitmaps[2]; // 2x bitmaps for each individual scanline
bitmap_ind8 m_priority; // priority bitmap
bitmap_ind64 m_burnin; // burn-in bitmap
u8 m_curbitmap; // current bitmap index
diff --git a/src/emu/video.h b/src/emu/video.h
index b8b1c408773..6015eecf146 100644
--- a/src/emu/video.h
+++ b/src/emu/video.h
@@ -38,6 +38,7 @@ constexpr int MAX_FRAMESKIP = FRAMESKIP_LEVELS - 2;
class video_manager
{
friend class screen_device;
+ friend class variable_width_screen_device;
public:
// movie format options
diff --git a/src/mame/drivers/snes.cpp b/src/mame/drivers/snes.cpp
index 4017cab0b6c..9b84e1c4d88 100644
--- a/src/mame/drivers/snes.cpp
+++ b/src/mame/drivers/snes.cpp
@@ -1351,6 +1351,7 @@ void snes_console_state::snes(machine_config &config)
/* video hardware */
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
m_screen->set_raw(DOTCLK_NTSC * 2, SNES_HTOTAL * 2, 0, SNES_SCR_WIDTH * 2, SNES_VTOTAL_NTSC, 0, SNES_SCR_HEIGHT_NTSC);
+ m_screen->set_video_attributes(VIDEO_VARIABLE_WIDTH);
m_screen->set_screen_update(FUNC(snes_state::screen_update));
SNES_PPU(config, m_ppu, MCLK_NTSC);
diff --git a/src/mame/machine/snes.cpp b/src/mame/machine/snes.cpp
index fac15ca7b75..a7c24950abf 100644
--- a/src/mame/machine/snes.cpp
+++ b/src/mame/machine/snes.cpp
@@ -235,7 +235,7 @@ TIMER_CALLBACK_MEMBER(snes_state::snes_hblank_tick)
hdma(cpu0space);
if (m_screen->vpos() > 0)
- m_screen->update_partial((m_ppu->interlace() == 2) ? (m_ppu->current_vert() * m_ppu->interlace()) : m_ppu->current_vert());
+ m_screen->update_partial((m_ppu->interlace() == 2) ? (m_ppu->current_vert() * m_ppu->interlace()) : m_ppu->current_vert() - 1);
}
// signal hblank
@@ -1035,7 +1035,7 @@ void snes_state::snes_init_timers()
// SNES hcounter has a 0-339 range. hblank starts at counter 260.
// clayfighter sets an HIRQ at 260, apparently it wants it to be before hdma kicks off, so we'll delay 2 pixels.
- m_hblank_offset = 128;
+ m_hblank_offset = 274;
m_hblank_timer->adjust(m_screen->time_until_pos(m_ppu->vtotal() - 1, m_hblank_offset));
}