summaryrefslogtreecommitdiffstats
path: root/src/emu/render.cpp
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2018-07-26 23:11:02 +1000
committer Vas Crabb <vas@vastheman.com>2018-07-26 23:11:02 +1000
commit9db24aa2e965d8469d000a7429679fd06c048406 (patch)
treef8169c24d45cd0a51880a0c08830482af1755af1 /src/emu/render.cpp
parent884f3a86bb57652c4306a852a6f8560cc746b1ce (diff)
Better support for screen orientation/geometry:
* Eliminates the need for the horizontal/vertical/LCD/SVG layout files * Screens can now have orientation and physical aspect ratio specified * RASTER/VECTOR defaults to 4:3, LCD/SVG defaults to square pixels at config time * System orientation is applied on top of screen orientation Automatically generated single-screen views and orientation flags in XML output now work correctly for systems with multiple screens in different geometries/orientations, e.g. housemnq, rocnms, stepstag, or netmerc. The "core rotation options" only interact with system orientation. Allowing multi-screen systems to work well with one monitor per emulated screen is a complex topic. System orientation also affects the GFX viewer while screen orientation doesn't. The orientation displayed in the system selection menu is from the system orientation. Let me know if I've broken any systems or use cases. Also, add save state support for std::array/C array nested to any depth.
Diffstat (limited to 'src/emu/render.cpp')
-rw-r--r--src/emu/render.cpp320
1 files changed, 213 insertions, 107 deletions
diff --git a/src/emu/render.cpp b/src/emu/render.cpp
index 9ec8445fa49..a65a47e2cf6 100644
--- a/src/emu/render.cpp
+++ b/src/emu/render.cpp
@@ -46,8 +46,12 @@
#include "drivenum.h"
#include "xmlfile.h"
#include "ui/uimain.h"
+
#include <zlib.h>
+#include <algorithm>
+#include <functional>
+
//**************************************************************************
@@ -562,20 +566,20 @@ const rgb_t *render_texture::get_adjusted_palette(render_container &container)
//-------------------------------------------------
render_container::render_container(render_manager &manager, screen_device *screen)
- : m_next(nullptr),
- m_manager(manager),
- m_screen(screen),
- m_overlaybitmap(nullptr),
- m_overlaytexture(nullptr)
+ : m_next(nullptr)
+ , m_manager(manager)
+ , m_screen(screen)
+ , m_overlaybitmap(nullptr)
+ , m_overlaytexture(nullptr)
{
// make sure it is empty
empty();
// if we have a screen, read and apply the options
- if (m_screen != nullptr)
+ if (m_screen)
{
// set the initial orientation and brightness/contrast/gamma
- m_user.m_orientation = manager.machine().system().flags & machine_flags::MASK_ORIENTATION;
+ m_user.m_orientation = m_screen->orientation();
m_user.m_brightness = manager.machine().options().brightness();
m_user.m_contrast = manager.machine().options().contrast();
m_user.m_gamma = manager.machine().options().gamma();
@@ -884,14 +888,14 @@ void render_container::update_palette()
//-------------------------------------------------
render_container::user_settings::user_settings()
- : m_orientation(0),
- m_brightness(1.0f),
- m_contrast(1.0f),
- m_gamma(1.0f),
- m_xscale(1.0f),
- m_yscale(1.0f),
- m_xoffset(0.0f),
- m_yoffset(0.0f)
+ : m_orientation(0)
+ , m_brightness(1.0f)
+ , m_contrast(1.0f)
+ , m_gamma(1.0f)
+ , m_xscale(1.0f)
+ , m_yscale(1.0f)
+ , m_xoffset(0.0f)
+ , m_yoffset(0.0f)
{
}
@@ -1676,23 +1680,53 @@ void render_target::load_additional_layout_files(const char *basename, bool have
}
- screen_device_iterator iter(m_manager.machine().root_device());
- unsigned const screens = iter.count();
-
- // now do the built-in layouts for single-screen games
- if (screens == 1)
+ // local screen info to avoid repeated code
+ class screen_info
{
- if (system.flags & ORIENTATION_SWAP_XY)
- load_layout_file(nullptr, layout_vertical);
- else
- load_layout_file(nullptr, layout_horizont);
- if (m_filelist.empty())
- throw emu_fatalerror("Couldn't parse default layout??");
- }
+ public:
+ screen_info(screen_device const &screen)
+ : m_device(screen)
+ , m_rotated(screen.orientation() & ORIENTATION_SWAP_XY)
+ , m_physical(screen.physical_aspect())
+ , m_native(screen.visible_area().width(), screen.visible_area().height())
+ {
+ util::reduce_fraction(m_native.first, m_native.second);
+ if (m_rotated)
+ {
+ std::swap(m_physical.first, m_physical.second);
+ std::swap(m_native.first, m_native.second);
+ }
+ }
+
+ screen_device const &device() const { return m_device.get(); }
+ bool rotated() const { return m_rotated; }
+ bool square() const { return m_physical == m_native; }
+ unsigned physical_x() const { return m_physical.first; }
+ unsigned physical_y() const { return m_physical.second; }
+ unsigned native_x() const { return m_native.first; }
+ unsigned native_y() const { return m_native.second; }
+
+ std::pair<float, float> tiled_size() const
+ {
+ if (physical_x() == physical_y())
+ return std::make_pair(1.0F, 1.0F);
+ else if (physical_x() > physical_y())
+ return std::make_pair(1.0F, float(physical_y()) / physical_x());
+ else
+ return std::make_pair(float(physical_x()) / physical_y(), 1.0F);
+ }
+
+ private:
+ std::reference_wrapper<screen_device const> m_device;
+ bool m_rotated;
+ std::pair<unsigned, unsigned> m_physical, m_native;
+ };
+ screen_device_iterator iter(m_manager.machine().root_device());
+ std::vector<screen_info> const screens(std::begin(iter), std::end(iter));
if (!have_default && !have_artwork)
{
- if (screens == 2)
+ if (screens.size() == 2U)
{
load_layout_file(nullptr, layout_dualhsxs);
if (m_filelist.empty())
@@ -1700,7 +1734,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
}
}
- if (screens == 0) // ensure the fallback view for systems with no screens is loaded if necessary
+ if (screens.empty()) // ensure the fallback view for systems with no screens is loaded if necessary
{
if (!view_by_index(0))
{
@@ -1709,7 +1743,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
throw emu_fatalerror("Couldn't parse default layout??");
}
}
- else if (screens >= 2) // generate default layouts for larger numbers of screens
+ else // generate default layouts for larger numbers of screens
{
util::xml::file::ptr const root(util::xml::file::create());
if (!root)
@@ -1719,17 +1753,18 @@ void render_target::load_additional_layout_files(const char *basename, bool have
throw emu_fatalerror("Couldn't create XML node??");
layoutnode->set_attribute_int("version", 2);
- // get standard width/height assuming 4:3 screens
- unsigned const stdwidth((system.flags & ORIENTATION_SWAP_XY) ? 3 : 4);
- unsigned const stdheight((system.flags & ORIENTATION_SWAP_XY) ? 4 : 3);
-
- // generate individual 4:3 views
- for (unsigned i = 0; screens > i; ++i)
+ // generate individual physical aspect views
+ for (unsigned i = 0; screens.size() > i; ++i)
{
util::xml::data_node *const viewnode(layoutnode->add_child("view", nullptr));
if (!viewnode)
throw emu_fatalerror("Couldn't create XML node??");
- viewnode->set_attribute("name", util::xml::normalize_string(util::string_format("Screen %1$u Standard (%2$u:%3$u)", i, stdwidth, stdheight).c_str()));
+ viewnode->set_attribute(
+ "name",
+ util::xml::normalize_string(
+ util::string_format(
+ "Screen %1$u Standard (%2$u:%3$u)",
+ i, screens[i].physical_x(), screens[i].physical_y()).c_str()));
util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
if (!screennode)
throw emu_fatalerror("Couldn't create XML node??");
@@ -1739,40 +1774,85 @@ void render_target::load_additional_layout_files(const char *basename, bool have
throw emu_fatalerror("Couldn't create XML node??");
boundsnode->set_attribute_int("x", 0);
boundsnode->set_attribute_int("y", 0);
- boundsnode->set_attribute_int("width", stdwidth);
- boundsnode->set_attribute_int("height", stdheight);
+ boundsnode->set_attribute_int("width", screens[i].physical_x());
+ boundsnode->set_attribute_int("height", screens[i].physical_y());
}
// generate individual pixel aspect views
- for (unsigned i = 0; screens > i; ++i)
+ for (unsigned i = 0; screens.size() > i; ++i)
+ {
+ if (!screens[i].square())
+ {
+ util::xml::data_node *const viewnode(layoutnode->add_child("view", nullptr));
+ if (!viewnode)
+ throw emu_fatalerror("Couldn't create XML node??");
+ viewnode->set_attribute(
+ "name",
+ util::xml::normalize_string(
+ util::string_format(
+ "Screen %1$u Pixel Aspect (%2$u:%3$u)",
+ i, screens[i].native_x(), screens[i].native_y()).c_str()));
+ util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
+ if (!screennode)
+ throw emu_fatalerror("Couldn't create XML node??");
+ screennode->set_attribute_int("index", i);
+ util::xml::data_node *const boundsnode(screennode->add_child("bounds", nullptr));
+ if (!boundsnode)
+ throw emu_fatalerror("Couldn't create XML node??");
+ boundsnode->set_attribute_int("x", 0);
+ boundsnode->set_attribute_int("y", 0);
+ boundsnode->set_attribute_int("width", screens[i].native_x());
+ boundsnode->set_attribute_int("height", screens[i].native_y());
+ }
+ }
+
+ // generate the fake cocktail view for single-screen systems
+ if (screens.size() == 1U)
{
util::xml::data_node *const viewnode(layoutnode->add_child("view", nullptr));
if (!viewnode)
throw emu_fatalerror("Couldn't create XML node??");
- viewnode->set_attribute("name", util::xml::normalize_string(util::string_format("Screen %1$u Pixel Aspect (~scr%1$unativexaspect~:~scr%1$unativeyaspect~)", i).c_str()));
+ viewnode->set_attribute("name", "Cocktail");
+
+ util::xml::data_node *const mirrornode(viewnode->add_child("screen", nullptr));
+ if (!mirrornode)
+ throw emu_fatalerror("Couldn't create XML node??");
+ mirrornode->set_attribute_int("index", 0);
+ util::xml::data_node *const mirrorbounds(mirrornode->add_child("bounds", nullptr));
+ if (!mirrorbounds)
+ throw emu_fatalerror("Couldn't create XML node??");
+ mirrorbounds->set_attribute_int("x", 0);
+ mirrorbounds->set_attribute_float("y", (-0.01 * (std::min)(screens[0].physical_x(), screens[0].physical_y())) - screens[0].physical_y());
+ mirrorbounds->set_attribute_int("width", screens[0].physical_x());
+ mirrorbounds->set_attribute_int("height", screens[0].physical_y());
+ util::xml::data_node *const flipper(mirrornode->add_child("orientation", nullptr));
+ if (!flipper)
+ throw emu_fatalerror("Couldn't create XML node??");
+ flipper->set_attribute_int("rotate", 180);
+
util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
if (!screennode)
throw emu_fatalerror("Couldn't create XML node??");
- screennode->set_attribute_int("index", i);
- util::xml::data_node *const boundsnode(screennode->add_child("bounds", nullptr));
- if (!boundsnode)
+ screennode->set_attribute_int("index", 0);
+ util::xml::data_node *const screenbounds(screennode->add_child("bounds", nullptr));
+ if (!screenbounds)
throw emu_fatalerror("Couldn't create XML node??");
- boundsnode->set_attribute_int("x", 0);
- boundsnode->set_attribute_int("y", 0);
- boundsnode->set_attribute("width", util::xml::normalize_string(util::string_format("~scr%1$uwidth~", i).c_str()));
- boundsnode->set_attribute("height", util::xml::normalize_string(util::string_format("~scr%1$uheight~", i).c_str()));
+ screenbounds->set_attribute_int("x", 0);
+ screenbounds->set_attribute_int("y", 0);
+ screenbounds->set_attribute_int("width", screens[0].physical_x());
+ screenbounds->set_attribute_int("height", screens[0].physical_y());
}
// generate tiled views if the supplied artwork doesn't provide a view of all screens
- bool need_tiles(screens >= 3);
- if (!need_tiles)
+ bool need_tiles(screens.size() >= 3);
+ if (!need_tiles && (screens.size() >= 2))
{
need_tiles = true;
int viewindex(0);
for (layout_view *view = view_by_index(viewindex); need_tiles && view; view = view_by_index(++viewindex))
{
render_screen_list const &viewscreens(view->screens());
- if (viewscreens.count() >= screens)
+ if (viewscreens.count() >= screens.size())
{
bool screen_missing(false);
for (screen_device &screen : iter)
@@ -1790,93 +1870,119 @@ void render_target::load_additional_layout_files(const char *basename, bool have
}
if (need_tiles)
{
- // helper for generating a view since we do this a lot
+ // helpers for generating a view since we do this a lot
+ std::vector<float> widths(screens.size()), heights(screens.size());
+ std::vector<std::pair<float, float> > sizes(screens.size());
+ std::transform(screens.begin(), screens.end(), sizes.begin(), [] (screen_info const &s) { return s.tiled_size(); });
auto const generate_view =
- [&layoutnode, screens, stdwidth, stdheight] (char const *title, auto &&bounds_callback)
+ [&layoutnode, &screens, &widths, &heights, &sizes] (char const *title, unsigned columns, bool gapless, auto &&mapper)
{
+ // calculate necessary widths/heights of rows/columns restricting screens to unit square
+ assert(0U < columns);
+ assert(screens.size() >= columns);
+ unsigned const rows((screens.size() + columns - 1) / columns);
+ std::fill_n(widths.begin(), columns, 0.0F);
+ std::fill_n(heights.begin(), rows, 0.0F);
+ for (unsigned y = 0U; rows > y; ++y)
+ {
+ for (unsigned x = 0U; columns > x; ++x)
+ {
+ int const i(mapper(x, y));
+ if (0 <= i)
+ {
+ widths[x] = (std::max)(widths[x], sizes[i].first);
+ heights[y] = (std::max)(heights[y], sizes[i].second);
+ }
+ }
+ }
+
+ // spacing is 1% of minor dimension
+ float spacing(0.0F);
+ if (!gapless)
+ {
+ spacing = 0.01F * (std::min)(
+ *std::max_element(widths.begin(), widths.begin() + columns),
+ *std::max_element(heights.begin(), heights.begin() + rows));
+ }
+
+ // actually generate elements
util::xml::data_node *viewnode = layoutnode->add_child("view", nullptr);
if (!viewnode)
throw emu_fatalerror("Couldn't create XML node??");
viewnode->set_attribute("name", util::xml::normalize_string(title));
- for (unsigned i = 0; screens > i; ++i)
+ float ypos(0.0F);
+ for (unsigned y = 0U; rows > y; ypos += heights[y] + spacing, ++y)
{
- util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
- if (!screennode)
- throw emu_fatalerror("Couldn't create XML node??");
- screennode->set_attribute_int("index", i);
- util::xml::data_node *const boundsnode(screennode->add_child("bounds", nullptr));
- if (!boundsnode)
- throw emu_fatalerror("Couldn't create XML node??");
- bounds_callback(*boundsnode, i);
- boundsnode->set_attribute_int("width", stdwidth);
- boundsnode->set_attribute_int("height", stdheight);
+ float xpos(0.0F);
+ for (unsigned x = 0U; columns > x; xpos += widths[x] + spacing, ++x)
+ {
+ int const i(mapper(x, y));
+ if (0 <= i)
+ {
+ util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
+ if (!screennode)
+ throw emu_fatalerror("Couldn't create XML node??");
+ screennode->set_attribute_int("index", i);
+ util::xml::data_node *const boundsnode(screennode->add_child("bounds", nullptr));
+ if (!boundsnode)
+ throw emu_fatalerror("Couldn't create XML node??");
+ boundsnode->set_attribute_float("x", xpos + ((widths[x] - sizes[i].first) / 2));
+ boundsnode->set_attribute_float("y", ypos + ((heights[y] - sizes[i].second) / 2));
+ boundsnode->set_attribute_float("width", sizes[i].first);
+ boundsnode->set_attribute_float("height", sizes[i].second);
+ }
+ }
}
};
// generate linear views
- generate_view(
- "Left-to-Right",
- [stdwidth] (util::xml::data_node &boundsnode, unsigned i)
- {
- boundsnode.set_attribute_float("x", i * (stdwidth + 0.03f));
- boundsnode.set_attribute_int("y", 0);
- });
- generate_view(
- "Left-to-Right (Gapless)",
- [stdwidth] (util::xml::data_node &boundsnode, unsigned i)
- {
- boundsnode.set_attribute_int("x", i * stdwidth);
- boundsnode.set_attribute_int("y", 0);
- });
- generate_view(
- "Top-to-Bottom",
- [stdheight] (util::xml::data_node &boundsnode, unsigned i)
- {
- boundsnode.set_attribute_int("x", 0);
- boundsnode.set_attribute_float("y", i * (stdheight + 0.03f));
- });
- generate_view(
- "Top-to-Bottom (Gapless)",
- [stdheight] (util::xml::data_node &boundsnode, unsigned i)
- {
- boundsnode.set_attribute_int("x", 0);
- boundsnode.set_attribute_int("y", i * stdheight);
- });
+ generate_view("Left-to-Right", screens.size(), false, [] (unsigned x, unsigned y) { return x; });
+ generate_view("Left-to-Right (Gapless)", screens.size(), true, [] (unsigned x, unsigned y) { return x; });
+ generate_view("Top-to-Bottom", 1U, false, [] (unsigned x, unsigned y) { return y; });
+ generate_view("Top-to-Bottom (Gapless)", 1U, true, [] (unsigned x, unsigned y) { return y; });
// generate tiled views
- for (unsigned mindim = 2; ((screens + mindim - 1) / mindim) >= mindim; ++mindim)
+ for (unsigned mindim = 2; ((screens.size() + mindim - 1) / mindim) >= mindim; ++mindim)
{
- unsigned const majdim((screens + mindim - 1) / mindim);
- unsigned const remainder(screens % majdim);
+ unsigned const majdim((screens.size() + mindim - 1) / mindim);
+ unsigned const remainder(screens.size() % majdim);
if (!remainder || (((majdim + 1) / 2) <= remainder))
{
generate_view(
util::string_format("%1$u\xC3\x97%2$u Left-to-Right, Top-to-Bottom", majdim, mindim).c_str(),
- [majdim, stdwidth, stdheight] (util::xml::data_node &boundsnode, unsigned i)
+ majdim,
+ false,
+ [&screens, majdim] (unsigned x, unsigned y)
{
- boundsnode.set_attribute_float("x", (i % majdim) * (stdwidth + 0.03f));
- boundsnode.set_attribute_float("y", (i / majdim) * (stdheight + 0.03f));
+ unsigned const i(x + (y * majdim));
+ return (screens.size() > i) ? int(i) : -1;
});
generate_view(
util::string_format("%1$u\xC3\x97%2$u Left-to-Right, Top-to-Bottom (Gapless)", majdim, mindim).c_str(),
- [majdim, stdwidth, stdheight] (util::xml::data_node &boundsnode, unsigned i)
+ majdim,
+ true,
+ [&screens, majdim] (unsigned x, unsigned y)
{
- boundsnode.set_attribute_int("x", (i % majdim) * stdwidth);
- boundsnode.set_attribute_int("y", (i / majdim) * stdheight);
+ unsigned const i(x + (y * majdim));
+ return (screens.size() > i) ? int(i) : -1;
});
generate_view(
util::string_format("%1$u\xC3\x97%2$u Top-to-Bottom, Left-to-Right", mindim, majdim).c_str(),
- [majdim, stdwidth, stdheight] (util::xml::data_node &boundsnode, unsigned i)
+ mindim,
+ false,
+ [&screens, majdim] (unsigned x, unsigned y)
{
- boundsnode.set_attribute_float("x", (i / majdim) * (stdwidth + 0.03f));
- boundsnode.set_attribute_float("y", (i % majdim) * (stdheight + 0.03f));
+ unsigned const i((x * majdim) + y);
+ return (screens.size() > i) ? int(i) : -1;
});
generate_view(
util::string_format("%1$u\xC3\x97%2$u Top-to-Bottom, Left-to-Right (Gapless)", mindim, majdim).c_str(),
- [majdim, stdwidth, stdheight] (util::xml::data_node &boundsnode, unsigned i)
+ mindim,
+ true,
+ [&screens, majdim] (unsigned x, unsigned y)
{
- boundsnode.set_attribute_int("x", (i / majdim) * stdwidth);
- boundsnode.set_attribute_int("y", (i % majdim) * stdheight);
+ unsigned const i((x * majdim) + y);
+ return (screens.size() > i) ? int(i) : -1;
});
}
}