diff options
author | 2018-07-26 23:11:02 +1000 | |
---|---|---|
committer | 2018-07-26 23:11:02 +1000 | |
commit | 9db24aa2e965d8469d000a7429679fd06c048406 (patch) | |
tree | f8169c24d45cd0a51880a0c08830482af1755af1 /src/emu/render.cpp | |
parent | 884f3a86bb57652c4306a852a6f8560cc746b1ce (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.cpp | 320 |
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; }); } } |