Update rendering.

This commit is contained in:
OM3GAZX committed 2026-07-08 14:43:48 -06:00
1 parent 45d64b0804
commit 27bfc22efd
5 files changed
+406 -243

No files matched your search

+231 -203
View File
@@ -41,6 +41,7 @@
#include "corestr.h"
#include "emuopts.h"
#include "fileio.h"
#include "rendfont.h"
#include "rendlay.h"
#include "rendutil.h"
@@ -56,6 +57,7 @@
#include "util/xmlfile.h"
#include <algorithm>
#include <limits>
@@ -98,6 +100,20 @@ struct render_target::object_transform
};
struct render_target::hit_test
{
hit_test() noexcept
: inbounds(0U, 0U)
, hit(0U)
{
}
std::pair<u64, u64> inbounds;
u64 hit;
};
//**************************************************************************
// GLOBAL VARIABLES
@@ -300,7 +316,8 @@ render_texture::render_texture()
m_curseq(0)
{
m_sbounds.set(0, -1, 0, -1);
memset(m_scaled, 0, sizeof(m_scaled));
for (auto &elem : m_scaled)
elem.seqid = 0;
}
@@ -352,6 +369,7 @@ void render_texture::release()
m_bitmap = nullptr;
m_sbounds.set(0, -1, 0, -1);
m_format = TEXFORMAT_ARGB32;
m_scaler = nullptr;
m_curseq = 0;
}
@@ -430,6 +448,7 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo
texinfo.base = m_bitmap->raw_pixptr(m_sbounds.top(), m_sbounds.left());
texinfo.rowpixels = m_bitmap->rowpixels();
texinfo.width = swidth;
texinfo.width_margin = m_sbounds.left();
texinfo.height = sheight;
// palette will be set later
texinfo.seqid = ++m_curseq;
@@ -690,7 +709,7 @@ const rgb_t *render_container::bcg_lookup_table(int texformat, u32 &out_length,
m_bcglookup.resize(palette->max_index());
recompute_lookups();
}
assert (palette == &m_palclient->palette());
assert(palette == &m_palclient->palette());
out_length = palette->max_index();
return &m_bcglookup[0];
@@ -880,19 +899,21 @@ render_container::user_settings::user_settings()
// render_target - constructor
//-------------------------------------------------
render_target::render_target(render_manager &manager, const internal_layout *layoutfile, u32 flags)
: render_target(manager, layoutfile, flags, CONSTRUCTOR_IMPL)
render_target::render_target(render_manager &manager, render_container *ui, const internal_layout *layoutfile, u32 flags)
: render_target(manager, ui, layoutfile, flags, CONSTRUCTOR_IMPL)
{
}
render_target::render_target(render_manager &manager, util::xml::data_node const &layout, u32 flags)
: render_target(manager, layout, flags, CONSTRUCTOR_IMPL)
render_target::render_target(render_manager &manager, render_container *ui, util::xml::data_node const &layout, u32 flags)
: render_target(manager, ui, layout, flags, CONSTRUCTOR_IMPL)
{
}
template <typename T> render_target::render_target(render_manager &manager, T &&layout, u32 flags, constructor_impl_t)
template <typename T>
render_target::render_target(render_manager &manager, render_container *ui, T &&layout, u32 flags, constructor_impl_t)
: m_next(nullptr)
, m_manager(manager)
, m_ui_container(ui)
, m_curview(0U)
, m_flags(flags)
, m_listindex(0)
@@ -1012,9 +1033,9 @@ void render_target::set_bounds(s32 width, s32 height, float pixel_aspect)
m_width = width;
m_height = height;
m_bounds.x0 = m_bounds.y0 = 0;
m_bounds.x1 = (float)width;
m_bounds.y1 = (float)height;
m_pixel_aspect = pixel_aspect != 0.0? pixel_aspect : 1.0;
m_bounds.x1 = float(width);
m_bounds.y1 = float(height);
m_pixel_aspect = pixel_aspect != 0.0F ? pixel_aspect : 1.0F;
}
@@ -1030,6 +1051,8 @@ void render_target::set_view(unsigned viewindex)
m_curview = viewindex;
current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
current_view().preload();
m_clickable_items.clear();
m_clickable_items.resize(current_view().interactive_items().size());
}
}
@@ -1058,7 +1081,7 @@ void render_target::set_visibility_toggle(unsigned index, bool enable)
m_views[m_curview].second |= u32(1) << index;
else
m_views[m_curview].second &= ~(u32(1) << index);
current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
update_layer_config();
current_view().preload();
}
@@ -1070,28 +1093,29 @@ void render_target::set_visibility_toggle(unsigned index, bool enable)
unsigned render_target::configured_view(const char *viewname, int targetindex, int numtargets)
{
layout_view *view = nullptr;
// if it isn't "auto" or an empty string, try to match it as a view name prefix
if (viewname && *viewname && strcmp(viewname, "auto"))
{
// scan for a matching view name
size_t const viewlen = strlen(viewname);
for (unsigned i = 0; !view && (m_views.size() > i); ++i)
for (unsigned i = 0; m_views.size() > i; ++i)
{
if (!core_strnicmp(m_views[i].first.name().c_str(), viewname, viewlen))
view = &m_views[i].first;
return i;
}
}
// if we don't have a match, default to the nth view
std::vector<std::reference_wrapper<screen_device> > screens;
for (screen_device &screen : screen_device_enumerator(m_manager.machine().root_device()))
screens.push_back(screen);
if (!view && !screens.empty())
if (!screens.empty())
{
// if we have enough targets to be one per screen, assign in order
if (numtargets >= screens.size())
{
// find the first view with this screen and this screen only
layout_view *view = nullptr;
screen_device const &screen = screens[index() % screens.size()];
for (unsigned i = 0; !view && (m_views.size() > i); ++i)
{
@@ -1109,22 +1133,21 @@ unsigned render_target::configured_view(const char *viewname, int targetindex, i
}
}
}
if (view)
return view_index(*view);
}
// otherwise, find the first view that has all the screens
if (!view)
for (unsigned i = 0; m_views.size() > i; ++i)
{
for (unsigned i = 0; !view && (m_views.size() > i); ++i)
{
layout_view &curview = m_views[i].first;
if (std::find_if(screens.begin(), screens.end(), [&curview] (screen_device &screen) { return !curview.has_screen(screen); }) == screens.end())
view = &curview;
}
layout_view &curview = m_views[i].first;
if (std::find_if(screens.begin(), screens.end(), [&curview] (screen_device &screen) { return !curview.has_screen(screen); }) == screens.end())
return i;
}
}
// make sure it's a valid view
return view ? view_index(*view) : 0;
// default to the first view
return 0;
}
@@ -1197,63 +1220,103 @@ void render_target::compute_visible_area(s32 target_width, s32 target_height, fl
// apply orientation if required
if (target_orientation & ORIENTATION_SWAP_XY)
src_aspect = 1.0 / src_aspect;
src_aspect = 1.0f / src_aspect;
// get target aspect
float target_aspect = (float)target_width / (float)target_height * target_pixel_aspect;
// we need the ratio of target to source aspect
float aspect_ratio = m_keepaspect ? (float)target_width / (float)target_height * target_pixel_aspect / src_aspect : 1.0f;
// first compute (a, b) scale factors to fit the screen
float a = (float)target_width / src_width;
float b = (float)target_height / src_height;
// apply automatic axial stretching if required
int scale_mode = m_scale_mode;
if (m_scale_mode == SCALE_FRACTIONAL_AUTO)
if (scale_mode == SCALE_FRACTIONAL_AUTO)
scale_mode = (m_manager.machine().system().flags & ORIENTATION_SWAP_XY) ^ (target_orientation & ORIENTATION_SWAP_XY) ?
SCALE_FRACTIONAL_Y : SCALE_FRACTIONAL_X;
// determine the scaling method for each axis
bool a_is_fract = (scale_mode == SCALE_FRACTIONAL_X || scale_mode == SCALE_FRACTIONAL);
bool b_is_fract = (scale_mode == SCALE_FRACTIONAL_Y || scale_mode == SCALE_FRACTIONAL);
// check if we have user defined scale factors, if so use them instead, but only on integer axes
int a_user = a_is_fract ? 0 : m_int_scale_x;
int b_user = b_is_fract ? 0 : m_int_scale_y;
// we allow overscan either explicitely or if integer scale factors are forced by user
bool int_overscan = m_int_overscan || (m_keepaspect && (a_user != 0 || b_user != 0));
float a_max = std::max(a, (float)a_user);
float b_max = std::max(b, (float)b_user);
// get the usable bounding box considering the type of scaling for each axis
float usable_aspect = (a_is_fract ? a : std::max(1.0f, floorf(a))) * src_width /
((b_is_fract ? b : std::max(1.0f, floorf(b))) * src_height) * target_pixel_aspect;
// depending on the relative shape between target and source, let's define 'a' and 'b' so that:
// * a is the leader axis (first to hit a boundary)
// * b is the follower axis
if (usable_aspect > src_aspect)
{
bool is_rotated = (m_manager.machine().system().flags & ORIENTATION_SWAP_XY) ^ (target_orientation & ORIENTATION_SWAP_XY);
scale_mode = is_rotated ? SCALE_FRACTIONAL_Y : SCALE_FRACTIONAL_X;
std::swap(a, b);
std::swap(a_user, b_user);
std::swap(a_is_fract, b_is_fract);
std::swap(a_max, b_max);
aspect_ratio = 1.0f / aspect_ratio;
}
// first compute scale factors to fit the screen
float xscale = (float)target_width / src_width;
float yscale = (float)target_height / src_height;
// now find an (a, b) pair that best fits our boundaries and scale options
float a_best = 1.0f, b_best = 1.0f;
float diff = 1000;
// apply aspect correction
if (m_keepaspect)
// fill (a0, a1) range
float u = a_user == 0 ? a : (float)a_user;
float a_range[] = {a_is_fract ? u : std::max(1.0f, floorf(u)), a_is_fract ? u : std::max(1.0f, roundf(u))};
for (float aa : a_range)
{
if (target_aspect > src_aspect)
xscale *= src_aspect / target_aspect;
else
yscale *= target_aspect / src_aspect;
}
// apply aspect correction to 'b' axis if needed, considering resulting 'a' borders
float ba = b * (m_keepaspect ? aspect_ratio * (aa / a) : 1.0f);
bool x_fits = render_round_nearest(xscale) * src_width <= target_width;
bool y_fits = render_round_nearest(yscale) * src_height <= target_height;
// fill (b0, b1) range
float v = b_user == 0 ? ba : (float)b_user;
float b_range[] = {b_is_fract ? v : std::max(1.0f, floorf(v)), b_is_fract ? v : std::max(1.0f, roundf(v))};
// compute integer scale factors
float integer_x = std::max(1.0f, float(m_int_overscan || x_fits ? render_round_nearest(xscale) : floor(xscale)));
float integer_y = std::max(1.0f, float(m_int_overscan || y_fits ? render_round_nearest(yscale) : floor(yscale)));
for (float bb : b_range)
{
// we may need to propagate proportions back to 'a' axis
float ab = aa;
if (m_keepaspect && a_user == 0)
{
if (a_is_fract) ab *= (bb / ba);
else if (b_user != 0) ab = std::max(1.0f, roundf(ab * (bb / ba)));
}
// check if we have user defined scale factors, if so use them instead
integer_x = m_int_scale_x > 0 ? m_int_scale_x : integer_x;
integer_y = m_int_scale_y > 0 ? m_int_scale_y : integer_y;
// if overscan isn't allowed, discard values that exceed the usable bounding box, except a minimum of 1.0f
if (!int_overscan && ((ab > a_max && bb > 1.0f) || (bb > b_max && ab > 1.0f)))
continue;
// now apply desired scale mode
if (scale_mode == SCALE_FRACTIONAL_X)
{
if (m_keepaspect) xscale *= integer_y / yscale;
yscale = integer_y;
}
else if (scale_mode == SCALE_FRACTIONAL_Y)
{
if (m_keepaspect) yscale *= integer_x / xscale;
xscale = integer_x;
}
else
{
xscale = integer_x;
yscale = integer_y;
// score the result
float new_diff = fabsf(aspect_ratio * (a / b) - (ab / bb));
if (new_diff <= diff)
{
diff = new_diff;
a_best = ab;
b_best = bb;
}
}
}
a = a_best;
b = b_best;
// restore orientation
if (usable_aspect > src_aspect)
std::swap(a, b);
// set the final width/height
visible_width = render_round_nearest(src_width * xscale);
visible_height = render_round_nearest(src_height * yscale);
visible_width = render_round_nearest(src_width * a);
visible_height = render_round_nearest(src_height * b);
break;
}
}
@@ -1411,8 +1474,8 @@ render_primitive_list &render_target::get_primitives()
}
}
// process the UI if we are the UI target
if (is_ui_target())
// process UI elements if applicable
if (m_ui_container)
{
// compute the transform for the UI
object_transform ui_xform;
@@ -1425,7 +1488,7 @@ render_primitive_list &render_target::get_primitives()
ui_xform.no_center = false;
// add UI elements
add_container_primitives(list, root_xform, ui_xform, m_manager.ui_container(), BLENDMODE_ALPHA);
add_container_primitives(list, root_xform, ui_xform, *m_ui_container, BLENDMODE_ALPHA);
}
// optimize the list before handing it off
@@ -1446,16 +1509,12 @@ bool render_target::map_point_container(s32 target_x, s32 target_y, render_conta
std::pair<float, float> target_f(map_point_internal(target_x, target_y));
// explicitly check for the UI container
if (&container == &m_manager.ui_container())
if (&container == m_ui_container)
{
// this hit test went against the UI container
if ((target_f.first >= 0.0f) && (target_f.first < 1.0f) && (target_f.second >= 0.0f) && (target_f.second < 1.0f))
{
// this point was successfully mapped
container_x = float(target_x) / m_width;
container_y = float(target_y) / m_height;
return true;
}
container_x = float(target_x) / m_width;
container_y = float(target_y) / m_height;
return (target_f.first >= 0.0f) && (target_f.first < 1.0f) && (target_f.second >= 0.0f) && (target_f.second < 1.0f);
}
else
{
@@ -1474,15 +1533,12 @@ bool render_target::map_point_container(s32 target_x, s32 target_y, render_conta
[&container] (layout_view_item &item) { return &item.screen()->container() == &container; }));
if (items.end() != found)
{
// point successfully mapped
layout_view_item &item(*found);
render_bounds const bounds(item.bounds());
if (bounds.includes(target_f.first, target_f.second))
{
// point successfully mapped
container_x = (target_f.first - bounds.x0) / bounds.width();
container_y = (target_f.second - bounds.y0) / bounds.height();
return true;
}
container_x = (target_f.first - bounds.x0) / bounds.width();
container_y = (target_f.second - bounds.y0) / bounds.height();
return bounds.includes(target_f.first, target_f.second);
}
}
@@ -1492,74 +1548,6 @@ bool render_target::map_point_container(s32 target_x, s32 target_y, render_conta
}
//-------------------------------------------------
// map_point_input - attempts to map a point on
// the specified render_target to an input port
// field, if possible
//-------------------------------------------------
bool render_target::map_point_input(s32 target_x, s32 target_y, ioport_port *&input_port, ioport_value &input_mask, float &input_x, float &input_y)
{
std::pair<float, float> target_f(map_point_internal(target_x, target_y));
if (m_orientation & ORIENTATION_FLIP_X)
target_f.first = 1.0f - target_f.first;
if (m_orientation & ORIENTATION_FLIP_Y)
target_f.second = 1.0f - target_f.second;
if (m_orientation & ORIENTATION_SWAP_XY)
std::swap(target_f.first, target_f.second);
auto const &items(current_view().interactive_items());
m_hit_test.resize(items.size() * 2);
std::fill(m_hit_test.begin(), m_hit_test.end(), false);
for (auto const &edge : current_view().interactive_edges_x())
{
if ((edge.position() > target_f.first) || ((edge.position() == target_f.first) && edge.trailing()))
break;
else
m_hit_test[edge.index()] = !edge.trailing();
}
for (auto const &edge : current_view().interactive_edges_y())
{
if ((edge.position() > target_f.second) || ((edge.position() == target_f.second) && edge.trailing()))
break;
else
m_hit_test[items.size() + edge.index()] = !edge.trailing();
}
for (unsigned i = 0; items.size() > i; ++i)
{
if (m_hit_test[i] && m_hit_test[items.size() + i])
{
layout_view_item &item(items[i]);
render_bounds const bounds(item.bounds());
if (bounds.includes(target_f.first, target_f.second))
{
if (item.has_input())
{
// point successfully mapped
std::tie(input_port, input_mask) = item.input_tag_and_mask();
input_x = (target_f.first - bounds.x0) / bounds.width();
input_y = (target_f.second - bounds.y0) / bounds.height();
return true;
}
else
{
break;
}
}
}
}
// default to point not mapped
input_port = nullptr;
input_mask = 0;
input_x = input_y = -1.0f;
return false;
}
//-------------------------------------------------
// invalidate_all - if any of our primitive lists
// contain a reference to the given pointer,
@@ -1589,7 +1577,7 @@ void render_target::resolve_tags()
for (layout_file &file : m_filelist)
file.resolve_tags();
current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
update_layer_config();
current_view().preload();
}
@@ -1602,6 +1590,8 @@ void render_target::resolve_tags()
void render_target::update_layer_config()
{
current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
m_clickable_items.clear();
m_clickable_items.resize(current_view().interactive_items().size());
}
@@ -1664,14 +1654,6 @@ void render_target::load_additional_layout_files(const char *basename, bool have
else
m_external_artwork = true;
// if a default view has been specified, use that as a fallback
bool have_default = false;
if (system.default_layout)
have_default |= load_layout_file(nullptr, *system.default_layout);
m_manager.machine().config().apply_default_layouts(
[this, &have_default] (device_t &dev, internal_layout const &layout)
{ have_default |= load_layout_file(nullptr, layout, &dev); });
// try to load another file based on the parent driver name
int cloneof = driver_list::clone(system);
while (0 <= cloneof)
@@ -1689,6 +1671,14 @@ void render_target::load_additional_layout_files(const char *basename, bool have
cloneof = driver_list::clone(parent);
}
// if a default view has been specified, use that as a fallback
bool have_default = false;
if (system.default_layout)
have_default |= load_layout_file(nullptr, *system.default_layout);
m_manager.machine().config().apply_default_layouts(
[this, &have_default] (device_t &dev, internal_layout const &layout)
{ have_default |= load_layout_file(nullptr, layout, &dev); });
have_artwork |= m_external_artwork;
// Use fallback artwork if defined and no artwork has been found yet
@@ -2076,11 +2066,10 @@ bool render_target::load_layout_file(const char *dirname, const internal_layout
size_t decompressed = 0;
do
{
size_t actual;
std::error_condition const err = inflater->read(
auto const [err, actual] = read(
*inflater,
&tempout[decompressed],
layout_data.decompressed_size - decompressed,
actual);
layout_data.decompressed_size - decompressed);
decompressed += actual;
if (err)
{
@@ -2173,7 +2162,7 @@ bool render_target::load_layout_file(device_t &device, util::xml::data_node cons
{
m_filelist.emplace_back(device, rootnode, searchpath, dirname);
}
catch (emu_fatalerror &err)
catch (emu_fatalerror const &err)
{
osd_printf_warning("%s\n", err.what());
return false;
@@ -2309,11 +2298,11 @@ void render_target::add_container_primitives(render_primitive_list &list, const
// clip the primitive
if (!m_transform_container && PRIMFLAG_GET_VECTOR(curitem.flags()))
{
clipped = render_clip_line(&prim->bounds, &root_cliprect);
clipped = render_clip_line(prim->bounds, root_cliprect);
}
else
{
clipped = render_clip_line(&prim->bounds, &cliprect);
clipped = render_clip_line(prim->bounds, cliprect);
}
break;
@@ -2346,7 +2335,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const
prim->texcoords = oriented_texcoords[finalorient];
// apply clipping
clipped = render_clip_quad(&prim->bounds, &cliprect, &prim->texcoords);
clipped = render_clip_quad(prim->bounds, cliprect, &prim->texcoords);
// apply the final orientation from the quad flags and then build up the final flags
prim->flags |= (curitem.flags() & ~(PRIMFLAG_TEXORIENT_MASK | PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK))
@@ -2406,7 +2395,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const
prim->texcoords = oriented_texcoords[finalorient];
// apply clipping
clipped = render_clip_quad(&prim->bounds, &cliprect, &prim->texcoords);
clipped = render_clip_quad(prim->bounds, cliprect, &prim->texcoords);
// apply the final orientation from the quad flags and then build up the final flags
prim->flags |= (curitem.flags() & ~(PRIMFLAG_TEXORIENT_MASK | PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK))
@@ -2422,7 +2411,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const
| PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA);
// apply clipping
clipped = render_clip_quad(&prim->bounds, &cliprect, nullptr);
clipped = render_clip_quad(prim->bounds, cliprect, nullptr);
}
}
break;
@@ -2547,7 +2536,7 @@ void render_target::add_element_primitives(render_primitive_list &list, const ob
}
// add to the list or free if we're clipped out
bool const clipped = render_clip_quad(&prim->bounds, &cliprect, &prim->texcoords);
bool const clipped = render_clip_quad(prim->bounds, cliprect, &prim->texcoords);
list.append_or_return(*prim, clipped);
}
}
@@ -2577,6 +2566,25 @@ std::pair<float, float> render_target::map_point_internal(s32 target_x, s32 targ
}
//-------------------------------------------------
// map_point_layout - map point from screen
// coordinates to layout coordinates
//-------------------------------------------------
std::pair<float, float> render_target::map_point_layout(s32 target_x, s32 target_y)
{
using std::swap;
std::pair<float, float> result(map_point_internal(target_x, target_y));
if (m_orientation & ORIENTATION_FLIP_X)
result.first = 1.0f - result.first;
if (m_orientation & ORIENTATION_FLIP_Y)
result.second = 1.0f - result.second;
if (m_orientation & ORIENTATION_SWAP_XY)
swap(result.first, result.second);
return result;
}
//-------------------------------------------------
// view_name - return the name of the indexed
// view, or nullptr if it doesn't exist
@@ -2619,20 +2627,22 @@ void render_target::config_load(util::xml::data_node const *targetnode)
if (!targetnode)
return;
// TODO: consider option priority - command line should take precedence over CFG
// not practical at the moment because view selection options are in the OSD layer
// find the view
const char *viewname = targetnode->get_attribute_string("view", nullptr);
if (viewname != nullptr)
for (int viewnum = 0; viewnum < 1000; viewnum++)
if (viewname)
{
for (unsigned viewnum = 0; m_views.size() > viewnum; viewnum++)
{
const char *testname = view_name(viewnum);
if (testname == nullptr)
break;
if (!strcmp(viewname, testname))
if (!strcmp(viewname, view_name(viewnum)))
{
set_view(viewnum);
break;
}
}
}
// modify the artwork config
int const zoom = targetnode->get_attribute_int("zoom", -1);
@@ -2654,12 +2664,11 @@ void render_target::config_load(util::xml::data_node const *targetnode)
set_orientation(orientation_add(rotate, orientation()));
// apply the opposite orientation to the UI
if (is_ui_target())
if (m_ui_container)
{
render_container &ui_container = m_manager.ui_container();
render_container::user_settings settings = ui_container.get_user_settings();
render_container::user_settings settings = m_ui_container->get_user_settings();
settings.m_orientation = orientation_add(orientation_reverse(rotate), settings.m_orientation);
ui_container.set_user_settings(settings);
m_ui_container->set_user_settings(settings);
}
}
@@ -2699,6 +2708,8 @@ void render_target::config_load(util::xml::data_node const *targetnode)
{
current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
current_view().preload();
m_clickable_items.clear();
m_clickable_items.resize(current_view().interactive_items().size());
}
}
}
@@ -3060,7 +3071,6 @@ render_manager::render_manager(running_machine &machine)
, m_ui_target(nullptr)
, m_live_textures(0)
, m_texture_id(0)
, m_ui_container(std::make_unique<render_container>(*this))
{
// register callbacks
machine.configuration().config_register(
@@ -3081,7 +3091,7 @@ render_manager::render_manager(running_machine &machine)
render_manager::~render_manager()
{
// free all the containers since they may own textures
m_ui_container.reset();
m_ui_containers.clear();
m_screen_container_list.clear();
// better not be any outstanding textures when we die
@@ -3137,12 +3147,14 @@ float render_manager::max_update_rate() const
render_target *render_manager::target_alloc(const internal_layout *layoutfile, u32 flags)
{
return &m_targetlist.append(*new render_target(*this, layoutfile, flags));
render_container *const ui = (flags & RENDER_CREATE_HIDDEN) ? nullptr : &m_ui_containers.emplace_back(*this);
return &m_targetlist.append(*new render_target(*this, ui, layoutfile, flags));
}
render_target *render_manager::target_alloc(util::xml::data_node const &layout, u32 flags)
{
return &m_targetlist.append(*new render_target(*this, layout, flags));
render_container *const ui = (flags & RENDER_CREATE_HIDDEN) ? nullptr : &m_ui_containers.emplace_back(*this);
return &m_targetlist.append(*new render_target(*this, ui, layout, flags));
}
@@ -3179,35 +3191,56 @@ render_target *render_manager::target_by_index(int index) const
float render_manager::ui_aspect(render_container *rc)
{
int orient;
// work out if this is a UI container
render_target *target = nullptr;
if (!rc)
{
target = &ui_target();
rc = target->ui_container();
assert(rc);
}
else
{
for (render_target &t : m_targetlist)
{
if (t.ui_container() == rc)
{
target = &t;
break;
}
}
}
float aspect;
if (rc == m_ui_container.get() || rc == nullptr) {
// ui container, aggregated multi-screen target
if (target)
{
// UI container, aggregated multi-screen target
orient = orientation_add(m_ui_target->orientation(), m_ui_container->orientation());
// based on the orientation of the target, compute height/width or width/height
int const orient = orientation_add(target->orientation(), rc->orientation());
if (!(orient & ORIENTATION_SWAP_XY))
aspect = (float)m_ui_target->height() / (float)m_ui_target->width();
aspect = float(target->height()) / float(target->width());
else
aspect = (float)m_ui_target->width() / (float)m_ui_target->height();
aspect = float(target->width()) / float(target->height());
// if we have a valid pixel aspect, apply that and return
if (m_ui_target->pixel_aspect() != 0.0f)
if (target->pixel_aspect() != 0.0f)
{
float pixel_aspect = m_ui_target->pixel_aspect();
float pixel_aspect = target->pixel_aspect();
if (orient & ORIENTATION_SWAP_XY)
pixel_aspect = 1.0f / pixel_aspect;
return aspect /= pixel_aspect;
}
} else {
}
else
{
// single screen container
orient = rc->orientation();
// based on the orientation of the target, compute height/width or width/height
int const orient = rc->orientation();
if (!(orient & ORIENTATION_SWAP_XY))
aspect = (float)rc->screen()->visible_area().height() / (float)rc->screen()->visible_area().width();
else
@@ -3215,12 +3248,7 @@ float render_manager::ui_aspect(render_container *rc)
}
// clamp for extreme proportions
if (aspect < 0.66f)
aspect = 0.66f;
if (aspect > 1.5f)
aspect = 1.5f;
return aspect;
return std::clamp(aspect, 0.66f, 1.5f);
}
+31 -28
View File
@@ -47,18 +47,12 @@
#define MAME_EMU_RENDER_H
#include "rendertypes.h"
#include "screen.h"
#include <array>
#include <cmath>
#include <functional>
#include <map>
#include <list>
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -164,6 +158,7 @@ struct render_texinfo
void * base; // base of the data
u32 rowpixels; // pixels per row
u32 width; // width of the image
u32 width_margin; // left margin of the scaled bounds, if applicable
u32 height; // height of the image
u32 seqid; // sequence ID
u64 unique_id; // unique identifier to pass to osd
@@ -346,7 +341,7 @@ private:
void get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo, render_primitive_list &primlist, u32 flags = 0);
const rgb_t *get_adjusted_palette(render_container &container, u32 &out_length);
static constexpr int MAX_TEXTURE_SCALES = 20;
static constexpr int MAX_TEXTURE_SCALES = 100;
// a scaled_texture contains a single scaled entry for a texture
struct scaled_texture
@@ -498,14 +493,15 @@ class render_target
friend class render_manager;
// construction/destruction
render_target(render_manager &manager, const internal_layout *layoutfile = nullptr, u32 flags = 0);
render_target(render_manager &manager, util::xml::data_node const &layout, u32 flags = 0);
render_target(render_manager &manager, render_container *ui, const internal_layout *layoutfile, u32 flags);
render_target(render_manager &manager, render_container *ui, util::xml::data_node const &layout, u32 flags);
~render_target();
public:
// getters
render_target *next() const { return m_next; }
render_manager &manager() const { return m_manager; }
render_container *ui_container() const { return m_ui_container; }
u32 width() const { return m_width; }
u32 height() const { return m_height; }
float pixel_aspect() const { return m_pixel_aspect; }
@@ -556,7 +552,6 @@ public:
// hit testing
bool map_point_container(s32 target_x, s32 target_y, render_container &container, float &container_x, float &container_y);
bool map_point_input(s32 target_x, s32 target_y, ioport_port *&input_port, ioport_value &input_mask, float &input_x, float &input_y);
// reference tracking
void invalidate_all(void *refptr);
@@ -565,15 +560,24 @@ public:
void resolve_tags();
private:
// constants
static inline constexpr int NUM_PRIMLISTS = 3;
static inline constexpr int MAX_CLEAR_EXTENTS = 1000;
using view_mask_pair = std::pair<layout_view &, u32>;
using view_mask_vector = std::vector<view_mask_pair>;
// private classes declared in render.cpp
struct object_transform;
struct pointer_info;
struct hit_test;
using pointer_info_vector = std::vector<pointer_info>;
using hit_test_vector = std::vector<hit_test>;
// internal helpers
enum constructor_impl_t { CONSTRUCTOR_IMPL };
template <typename T> render_target(render_manager &manager, T&& layout, u32 flags, constructor_impl_t);
template <typename T> render_target(render_manager &manager, render_container *ui, T&& layout, u32 flags, constructor_impl_t);
void update_layer_config();
void load_layout_files(const internal_layout *layoutfile, bool singlefile);
void load_layout_files(util::xml::data_node const &rootnode, bool singlefile);
@@ -584,6 +588,7 @@ private:
void add_container_primitives(render_primitive_list &list, const object_transform &root_xform, const object_transform &xform, render_container &container, int blendmode);
void add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_view_item &item);
std::pair<float, float> map_point_internal(s32 target_x, s32 target_y);
std::pair<float, float> map_point_layout(s32 target_x, s32 target_y);
// config callbacks
void config_load(util::xml::data_node const *targetnode);
@@ -599,13 +604,10 @@ private:
void add_clear_extents(render_primitive_list &list);
void add_clear_and_optimize_primitive_list(render_primitive_list &list);
// constants
static constexpr int NUM_PRIMLISTS = 3;
static constexpr int MAX_CLEAR_EXTENTS = 1000;
// internal state
render_target * m_next; // link to next target
render_manager & m_manager; // reference to our owning manager
render_container *const m_ui_container; // container for drawing UI elements
std::list<layout_file> m_filelist; // list of layout files
view_mask_vector m_views; // views we consider
unsigned m_curview; // current view index
@@ -624,7 +626,8 @@ private:
float m_max_refresh; // maximum refresh rate, 0 or if none
int m_orientation; // orientation
render_layer_config m_layerconfig; // layer configuration
std::vector<bool> m_hit_test; // used when mapping points to inputs
pointer_info_vector m_pointers; // state of pointers over this target
hit_test_vector m_clickable_items; // for tracking clicked elements
layout_view * m_base_view; // the view at the time of first frame
int m_base_orientation; // the orientation at the time of first frame
render_layer_config m_base_layerconfig; // the layer configuration at the time of first frame
@@ -671,7 +674,7 @@ public:
float ui_aspect(render_container *rc = nullptr);
// UI containers
render_container &ui_container() const { assert(m_ui_container != nullptr); return *m_ui_container; }
render_container &ui_container() const { assert(ui_target().ui_container()); return *ui_target().ui_container(); }
// textures
render_texture *texture_alloc(texture_scaler_func scaler = nullptr, void *param = nullptr);
@@ -692,20 +695,20 @@ private:
void config_save(config_type cfg_type, util::xml::data_node *parentnode);
// internal state
running_machine & m_machine; // reference back to the machine
running_machine & m_machine; // reference back to the machine
// array of live targets
simple_list<render_target> m_targetlist; // list of targets
render_target * m_ui_target; // current UI target
simple_list<render_target> m_targetlist; // list of targets
render_target * m_ui_target; // current UI target
// texture lists
u32 m_live_textures; // number of live textures
u64 m_texture_id; // rolling texture ID counter
fixed_allocator<render_texture> m_texture_allocator;// texture allocator
u32 m_live_textures; // number of live textures
u64 m_texture_id; // rolling texture ID counter
fixed_allocator<render_texture> m_texture_allocator; // texture allocator
// containers for the UI and for screens
std::unique_ptr<render_container> m_ui_container; // UI container
std::list<render_container> m_screen_container_list; // list of containers for the screen
// containers for UI elements and for screens
std::list<render_container> m_ui_containers; // containers for drawing UI elements
std::list<render_container> m_screen_container_list; // list of containers for the screen
};
#endif // MAME_EMU_RENDER_H
+10
View File
@@ -20,6 +20,16 @@
//**************************************************************************
// texture formats
enum texture_format
{
TEXFORMAT_UNDEFINED = 0, // require a format to be specified
TEXFORMAT_PALETTE16, // 16bpp palettized, no alpha
TEXFORMAT_RGB32, // 32bpp 8-8-8 RGB
TEXFORMAT_ARGB32, // 32bpp 8-8-8-8 ARGB
TEXFORMAT_YUY16 // 16bpp 8-8 Y/Cb, Y/Cr in sequence
};
// blending modes
enum
{
+11 -8
View File
@@ -15,8 +15,11 @@
#include "rendertypes.h"
#include "utilfwd.h"
#include <algorithm>
#include <cmath>
#include <utility>
/* ----- image formats ----- */
@@ -39,14 +42,14 @@ enum ru_imgformat
/* ----- render utilities ----- */
void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source, const render_color &color, bool force = false);
bool render_clip_line(render_bounds *bounds, const render_bounds *clip);
bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_quad_texuv *texcoords);
void render_line_to_quad(const render_bounds *bounds, float width, float length_extension, render_bounds *bounds0, render_bounds *bounds1);
void render_load_msdib(bitmap_argb32 &bitmap, util::random_read &file);
void render_load_jpeg(bitmap_argb32 &bitmap, util::random_read &file);
bool render_load_png(bitmap_argb32 &bitmap, util::random_read &file, bool load_as_alpha_to_existing = false);
ru_imgformat render_detect_image(util::random_read &file);
void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source, const render_color &color, bool force = false) noexcept;
bool render_clip_line(render_bounds &bounds, const render_bounds &clip);
bool render_clip_quad(render_bounds &bounds, const render_bounds &clip, render_quad_texuv *texcoords);
std::pair<render_bounds, render_bounds> render_line_to_quad(const render_bounds &bounds, float width, float length_extension);
void render_load_msdib(bitmap_argb32 &bitmap, util::random_read &file) noexcept;
void render_load_jpeg(bitmap_argb32 &bitmap, util::random_read &file) noexcept;
bool render_load_png(bitmap_argb32 &bitmap, util::random_read &file, bool load_as_alpha_to_existing = false) noexcept;
ru_imgformat render_detect_image(util::random_read &file) noexcept;
+123 -4
View File
@@ -19,6 +19,8 @@
#include <cstdlib>
#include <memory>
#include <system_error>
#include <tuple>
#include <utility>
// FIXME: make a proper place for OSD forward declarations
@@ -56,7 +58,7 @@ public:
/// \param [out] actual Number of bytes actually read. Will always
/// be less than or equal to the requested length.
/// \return An error condition if reading stopped due to an error.
virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
};
@@ -100,7 +102,7 @@ public:
/// \param [out] actual Number of bytes actually written. Will
/// always be less than or equal to the requested length.
/// \return An error condition if writing stopped due to an error.
virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
};
@@ -184,7 +186,7 @@ public:
/// be less than or equal to the requested length.
/// \return An error condition if seeking failed or reading stopped
/// due to an error.
virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
};
@@ -214,7 +216,7 @@ public:
/// always be less than or equal to the requested length.
/// \return An error condition if seeking failed or writing stopped
/// due to an error.
virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
virtual std::error_condition write_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
};
@@ -229,6 +231,123 @@ public:
using ptr = std::unique_ptr<random_read_write>;
};
/// \brief Read from the current position in the stream
///
/// Reads up to the specified number of bytes from the stream into the
/// supplied buffer, continuing if interrupted by asynchronous signals.
/// May read less than the requested number of bytes if the end of the
/// stream is reached or an error occurs. If the stream supports
/// seeking, reading starts at the current position in the stream, and
/// the current position is incremented by the number of bytes read.
/// The operation may not be atomic if it is interrupted before the
/// requested number of bytes is read.
/// \param [in] stream The stream to read from.
/// \param [out] buffer Destination buffer. Must be large enough to
/// hold the requested number of bytes.
/// \param [in] length Maximum number of bytes to read.
/// \return A pair containing an error condition if reading stopped due
/// to an error, and the actual number of bytes read.
std::pair<std::error_condition, std::size_t> read(read_stream &stream, void *buffer, std::size_t length) noexcept;
/// \brief Allocate memory and read from the current position in the
/// stream
///
/// Allocates the specified number of bytes and then reads up to that
/// number of bytes from the stream into the newly allocated buffer,
/// continuing if interrupted by asynchronous signals. May read less
/// than the requested number of bytes if the end of the stream is
/// reached or an error occurs. If the stream supports seeking,
/// reading starts at the current position in the stream, and the
/// current position is incremented by the number of bytes read. The
/// operation may not be atomic if it is interrupted before the
/// requested number of bytes is read. No data will be read if
/// allocation fails.
/// \param [in] stream The stream to read from.
/// hold the requested number of bytes.
/// \param [in] length Maximum number of bytes to read.
/// \return A tuple containing an error condition if allocation failed
/// or reading stopped due to an error, the allocated buffer, and the
/// actual number of bytes read.
std::tuple<std::error_condition, std::unique_ptr<std::uint8_t []>, std::size_t> read(read_stream &stream, std::size_t length) noexcept;
/// \brief Read from the specified position
///
/// Reads up to the specified number of bytes from the stream into the
/// supplied buffer, continuing if interrupted by asynchronous signals.
/// May read less than the requested number of bytes if the end of the
/// stream is reached or an error occurs. If seeking is supported,
/// reading starts at the specified position and the current position is
/// unaffected. The operation may not be atomic if it is interrupted
/// before the requested number of bytes is read.
/// \param [in] stream The stream to read from.
/// \param [in] offset The position to start reading from, specified as
/// a number of bytes from the beginning of the stream.
/// \param [out] buffer Destination buffer. Must be large enough to
/// hold the requested number of bytes.
/// \param [in] length Maximum number of bytes to read.
/// \return A pair containing an error condition if reading stopped due
/// to an error, and the actual number of bytes read.
std::pair<std::error_condition, std::size_t> read_at(random_read &stream, std::uint64_t offset, void *buffer, std::size_t length) noexcept;
/// \brief Allocate memory and read from the specified position
///
/// Allocates the specified number of bytes and then reads up to that
/// number of bytes from the stream into the newly allocated buffer,
/// continuing if interrupted by asynchronous signals. May read less
/// than the requested number of bytes if the end of the stream is
/// reached or an error occurs. If seeking is supported, reading
/// starts at the specified position and the current position is
/// unaffected. The operation may not be atomic if it is interrupted
/// before the requested number of bytes is read. No data will be read
/// if allocation fails.
/// \param [in] stream The stream to read from.
/// \param [in] offset The position to start reading from, specified as
/// a number of bytes from the beginning of the stream.
/// \param [out] buffer Destination buffer. Must be large enough to
/// hold the requested number of bytes.
/// \param [in] length Maximum number of bytes to read.
/// \return A tuple containing an error condition if allocation failed
/// or reading stopped due to an error, the allocated buffer, and the
/// actual number of bytes read.
std::tuple<std::error_condition, std::unique_ptr<std::uint8_t []>, std::size_t> read_at(random_read &stream, std::uint64_t offset, std::size_t length) noexcept;
/// \brief Write at the current position in the stream
///
/// Writes up to the specified number of bytes from the supplied
/// buffer to the stream, continuing if interrupted by asynchronous
/// signals. May write less than the requested number of bytes if an
/// error occurs. If the stream supports seeking, writing starts at the
/// current position in the stream, and the current position is
/// incremented by the number of bytes written. The operation may not
/// be atomic if it is interrupted before the requested number of bytes
/// is written.
/// \param [in] stream The stream to write to.
/// \param [in] buffer Buffer containing the data to write. Must
/// contain at least the specified number of bytes.
/// \param [in] length Number of bytes to write.
/// \return A pair containing an error condition if writing stopped due
/// to an error, and the actual number of bytes written.
std::pair<std::error_condition, std::size_t> write(write_stream &stream, void const *buffer, std::size_t length) noexcept;
/// \brief Write at specified position
///
/// Writes up to the specified number of bytes from the supplied buffer,
/// continuing if interrupted by asynchronous signals. If seeking is
/// supported, writing starts at the specified position and the current
/// position is unaffected. May write less than the requested number
/// of bytes if an error occurs. The operation may not be atomic if it
/// is interrupted before the requested number of bytes is written.
/// \param [in] stream The stream to write to.
/// \param [in] offset The position to start writing at, specified as a
/// number of bytes from the beginning of the stream.
/// \param [in] buffer Buffer containing the data to write. Must
/// contain at least the specified number of bytes.
/// \param [in] length Number of bytes to write.
/// \return A pair containing an error condition if writing stopped due
/// to an error, and the actual number of bytes written.
std::pair<std::error_condition, std::size_t> write_at(random_write &stream, std::uint64_t offset, void const *buffer, std::size_t length) noexcept;
/// \}