forked from mamedev/mame
Call flow about Frame Update
gamz edited this page Jan 17, 2021
·
1 revision
1. @src/osd/sdl/sdlmain.cpp
int main(int argc, char** argv) {
...
sdl_options options;
sdl_osd_interface osd(options);
res = emulator_info::**start_frontend**(options, osd, args);
}
2. @src/frontend/mame/mame.cpp
int emulator_info::start_frontend(emu_options &options, osd_interface &osd, std::vector<std::string> &args) {
cli_frontend frontend(options, osd);
return frontend.**execute**(args);
}
3. @src/frontend/mame/clifront.cpp
int cli_frontend::execute(std::vector<std::string> &args) {
mame_machine_manager *manager = mame_machine_manager::instance(m_options, m_osd);
**start_execution**(manager, args);
...
}
void cli_frontend::start_execution(mame_machine_manager *manager, const std::vector<std::string> &args) {
...
manager->start_http_server();
manager->start_luaengine();
m_result = manager->**execute**();
}
4. @src/frontend/mame/mame.cpp
int mame_machine_manager::execute() {
...
running_machine machine(config, *this);
set_machine(&machine);
error = machine.**run**(is_empty);
...
}
5. @src/emu/machine.cpp
int running_machine::run(bool quiet) {
...
// then finish setting up our local machine
**start**();
// load the NVRAM
nvram_load();
// perform a soft reset -- this takes us to the running phase
soft_reset();
...
// MAIN LOOP !!
while ((!m_hard_reset_pending && !m_exit_pending) || m_saveload_schedule != saveload_schedule::NONE) {
if (!m_paused) // execute CPUs if not paused
m_scheduler.timeslice();
else // otherwise, just pump video updates through
m_video->frame_update();
}
...
}
void running_machine::start() {
// create all managers
m_configuration = std::make_unique<configuration_manager>(*this);
...
// allocate a soft_reset timer
m_soft_reset_timer = m_scheduler.timer_alloc(timer_expired_delegate(FUNC(running_machine::soft_reset), this));
// init the osd layer
m_manager.osd().**init**(*this); // sdl_osd_interface::init()
...
// register callbacks for the devices, then start them
**start_all_devices**();
...
manager().update_machine();
}
void running_machine::start_all_devices() {
for (device_t &device : device_iterator(root_device())) {
...
device.set_machine(*this);
device.**start**();
...
}
}
@src/emu/device.cpp
void device_t::start() {
// start the device
**device_start**();
...
...
// we're now officially started
m_started = true;
}
@src/emu/driver.cpp
void driver_device::device_start() {
m_system->driver_init(*this);
...
driver_start();
machine_start();
sound_start();
video_start();
}
@src/emu/machine.cpp
int running_machine::run(bool quiet) {
while ((!m_hard_reset_pending && !m_exit_pending) || m_saveload_schedule != saveload_schedule::NONE) {
// execute CPUs if not paused
if (!m_paused)
m_scheduler.**timeslice**();
// otherwise, just pump video updates through
else
m_video->**frame_update**();
}
...
}
@src/emu/schedule.cpp
void device_scheduler::timeslice() {
...
// loop until we hit the next timer
while (m_basetime < m_timer_list->m_expire) {
...
// loop over all CPUs
for (device_execute_interface *exec = m_execute_list; exec != nullptr; exec = exec->m_nextexec) {
// only process if this CPU is executing or truly halted (not yielding)
// and if our target is later than the CPU's current time (coarse check)
if (EXPECTED((exec->m_suspend == 0 || exec->m_eatcycles) && target.seconds() >= exec->m_localtime.seconds())) {
// compute how many attoseconds to execute this CPU
if (exec->m_attoseconds_per_cycle == 0) {
...
}
// if we have enough for at least 1 cycle, do the math
else if (delta >= exec->m_attoseconds_per_cycle) {
// if we're not suspended, actually execute
if (exec->m_suspend == 0) {
...
exec->**run**();
...
}
...
}
}
}
...
}
**execute_timers**();
}
inline void device_scheduler::execute_timers() {
// now process any timers that are overdue
while (m_timer_list->m_expire <= m_basetime) {
...
// call the callback
if (was_enabled) {
if (timer.m_device != nullptr) {
timer.m_device->**timer_expired**(timer, timer.m_id, timer.m_param, timer.m_ptr);
}
else if (!timer.m_callback.isnull()) {
timer.m_callback(timer.m_ptr, timer.m_param);
}
}
...
}
}
@src/emu/device.h
class device_t : public delegate_late_bin {
...
public:
void timer_expired(emu_timer &timer, device_timer_id id, int param, void *ptr) { **device_timer**(timer, id, param, ptr); }
...
}
@src/emu/screen.cpp
void screen_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) {
switch (id) {
case TID_VBLANK_START:
**vblank_begin**();
break;
case TID_VBLANK_END:
**vblank_end**();
break;
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
**update_partial**(param);
}
if (m_scanline_cb)
m_scanline_cb(param);
// compute the next visible scanline
param++;
if (param > m_visarea.bottom())
param = m_visarea.top();
m_scanline_timer->adjust(time_until_pos(param), param);
break;
}
}
void screen_device::vblank_begin() {
...
if (m_is_primary_screen && !(m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK)) {
machine().video().**frame_update**();
}
...
}
void screen_device::vblank_end() {
// if this is the primary screen and we need to update now
if (m_is_primary_screen && (m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK))
machine().video().**frame_update**();
...
}
@src/emu/video.cpp
void video_manager::frame_update(bool from_debugger) {
if (phase == machine_phase::RUNNING && (!machine().paused() || machine().options().update_in_pause())) {
bool anything_changed = **finish_screen_updates**();
// if none of the screens changed and we haven't skipped too many frames in a row,
// mark this frame as skipped to prevent throttling; this helps for games that
// don't update their screen at the monitor refresh rate
if (!anything_changed && !m_auto_frameskip && m_frameskip_level == 0 && m_empty_skip_count++ < 3)
skipped_it = true;
else
m_empty_skip_count = 0;
}
if (!from_debugger && !skipped_it && effective_throttle())
update_throttle(current_time);
...
machine().osd().**update**(!from_debugger && skipped_it);
...
}
bool video_manager::finish_screen_updates() {
// finish updating the screens
screen_device_iterator iter(machine().root_device());
bool has_live_screen = false;
for (screen_device &screen : iter) {
screen.**update_partial**(screen.visible_area().max_y);
if (machine().render().is_live(screen))
has_live_screen = true;
}
bool anything_changed = !has_live_screen || m_output_changed;
m_output_changed = false
...
return anything_changed;
}
@src/emu/screen.cpp
bool screen_device::update_partial(int scanline) {
...
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;
}
...
}
@src/mame/video/cps1.cpp
// cps video device driver (https://en.wikipedia.org/wiki/CP_System)
// each video device driver has screen update function
// which is registered to "screen_device" by set_screen_update function of it
// void cps_state::cps1_10MHz(machine_config &config) {
// ...
// m_screen->**set_screen_update**(FUNC(cps_state::screen_update_cps1));
// m_screen->screen_vblank().set(FUNC(cps_state::screen_vblank_cps1));
// ...
uint32_t cps_state::screen_update_cps1(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) {
..
}
@src/osd/sdl/video.cpp
void sdl_osd_interface::update(bool skip_redraw) {
}