Skip to content

Call flow about Frame Update

gamz edited this page Jan 17, 2021 · 1 revision

MAME Machine Start-up

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();
}

MAME Machine Main Loop

@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) {
}
Clone this wiki locally