1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4 
5     video.cpp
6 
7     Core MAME video routines.
8 
9 ***************************************************************************/
10 
11 #include "emu.h"
12 #include "emuopts.h"
13 #include "debugger.h"
14 #include "ui/uimain.h"
15 #include "crsshair.h"
16 #include "rendersw.hxx"
17 #include "output.h"
18 
19 #include "localpng.h"
20 #include "xmlfile.h"
21 
22 #include "osdepend.h"
23 
24 
25 //**************************************************************************
26 //  DEBUGGING
27 //**************************************************************************
28 
29 #define LOG_THROTTLE                (0)
30 
31 
32 
33 //**************************************************************************
34 //  GLOBAL VARIABLES
35 //**************************************************************************
36 
37 // frameskipping tables
38 const bool video_manager::s_skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS] =
39 {
40 	{ false, false, false, false, false, false, false, false, false, false, false, false },
41 	{ false, false, false, false, false, false, false, false, false, false, false, true  },
42 	{ false, false, false, false, false, true , false, false, false, false, false, true  },
43 	{ false, false, false, true , false, false, false, true , false, false, false, true  },
44 	{ false, false, true , false, false, true , false, false, true , false, false, true  },
45 	{ false, true , false, false, true , false, true , false, false, true , false, true  },
46 	{ false, true , false, true , false, true , false, true , false, true , false, true  },
47 	{ false, true , false, true , true , false, true , false, true , true , false, true  },
48 	{ false, true , true , false, true , true , false, true , true , false, true , true  },
49 	{ false, true , true , true , false, true , true , true , false, true , true , true  },
50 	{ false, true , true , true , true , true , false, true , true , true , true , true  },
51 	{ false, true , true , true , true , true , true , true , true , true , true , true  }
52 };
53 
54 
55 
56 //**************************************************************************
57 //  VIDEO MANAGER
58 //**************************************************************************
59 
video_notifier_callback(const char * outname,s32 value,void * param)60 static void video_notifier_callback(const char *outname, s32 value, void *param)
61 {
62 	video_manager *vm = (video_manager *)param;
63 
64 	vm->set_output_changed();
65 }
66 
67 
68 //-------------------------------------------------
69 //  video_manager - constructor
70 //-------------------------------------------------
71 
video_manager(running_machine & machine)72 video_manager::video_manager(running_machine &machine)
73 	: m_machine(machine)
74 	, m_screenless_frame_timer(nullptr)
75 	, m_output_changed(false)
76 	, m_throttle_last_ticks(0)
77 	, m_throttle_realtime(attotime::zero)
78 	, m_throttle_emutime(attotime::zero)
79 	, m_throttle_history(0)
80 	, m_speed_last_realtime(0)
81 	, m_speed_last_emutime(attotime::zero)
82 	, m_speed_percent(1.0)
83 	, m_overall_real_seconds(0)
84 	, m_overall_real_ticks(0)
85 	, m_overall_emutime(attotime::zero)
86 	, m_overall_valid_counter(0)
87 	, m_throttled(machine.options().throttle())
88 	, m_throttle_rate(1.0f)
89 	, m_fastforward(false)
90 	, m_seconds_to_run(machine.options().seconds_to_run())
91 	, m_auto_frameskip(machine.options().auto_frameskip())
92 	, m_speed(original_speed_setting())
93 	, m_low_latency(machine.options().low_latency())
94 	, m_empty_skip_count(0)
95 	, m_frameskip_max(m_auto_frameskip ? machine.options().frameskip() : 0)
96 	, m_frameskip_level(m_auto_frameskip ? 0 : machine.options().frameskip())
97 	, m_frameskip_counter(0)
98 	, m_frameskip_adjust(0)
99 	, m_skipping_this_frame(false)
100 	, m_average_oversleep(0)
101 	, m_snap_target(nullptr)
102 	, m_snap_native(true)
103 	, m_snap_width(0)
104 	, m_snap_height(0)
105 	, m_timecode_enabled(false)
106 	, m_timecode_write(false)
107 	, m_timecode_text("")
108 	, m_timecode_start(attotime::zero)
109 	, m_timecode_total(attotime::zero)
110 {
111 	// request a callback upon exiting
112 	machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&video_manager::exit, this));
113 	machine.save().register_postload(save_prepost_delegate(FUNC(video_manager::postload), this));
114 
115 	// extract initial execution state from global configuration settings
116 	update_refresh_speed();
117 
118 	const unsigned screen_count(screen_device_iterator(machine.root_device()).count());
119 	const bool no_screens(!screen_count);
120 
121 	// create a render target for snapshots
122 	const char *viewname = machine.options().snap_view();
123 	m_snap_native = !no_screens && (viewname[0] == 0 || strcmp(viewname, "native") == 0);
124 
125 	if (m_snap_native)
126 	{
127 		// the native target is hard-coded to our internal layout and has all options disabled
128 		util::xml::file::ptr const root(util::xml::file::create());
129 		if (!root)
130 			throw emu_fatalerror("Couldn't create XML document??");
131 		util::xml::data_node *const layoutnode(root->add_child("mamelayout", nullptr));
132 		if (!layoutnode)
133 			throw emu_fatalerror("Couldn't create XML node??");
134 		layoutnode->set_attribute_int("version", 2);
135 
136 		for (unsigned i = 0; screen_count > i; ++i)
137 		{
138 			util::xml::data_node *const viewnode(layoutnode->add_child("view", nullptr));
139 			if (!viewnode)
140 				throw emu_fatalerror("Couldn't create XML node??");
141 			viewnode->set_attribute("name", util::string_format("s%1$u", i).c_str());
142 			util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
143 			if (!screennode)
144 				throw emu_fatalerror("Couldn't create XML node??");
145 			screennode->set_attribute_int("index", i);
146 			util::xml::data_node *const boundsnode(screennode->add_child("bounds", nullptr));
147 			if (!boundsnode)
148 				throw emu_fatalerror("Couldn't create XML node??");
149 			boundsnode->set_attribute_int("left", 0);
150 			boundsnode->set_attribute_int("top", 0);
151 			boundsnode->set_attribute_int("right", 1);
152 			boundsnode->set_attribute_int("bottom", 1);
153 		}
154 
155 		m_snap_target = machine.render().target_alloc(*root, RENDER_CREATE_SINGLE_FILE | RENDER_CREATE_HIDDEN);
156 		m_snap_target->set_screen_overlay_enabled(false);
157 		m_snap_target->set_zoom_to_screen(false);
158 	}
159 	else
160 	{
161 		// otherwise, non-default targets select the specified view and turn off effects
162 		m_snap_target = machine.render().target_alloc(nullptr, RENDER_CREATE_HIDDEN);
163 		m_snap_target->set_view(m_snap_target->configured_view(viewname, 0, 1));
164 		m_snap_target->set_screen_overlay_enabled(false);
165 	}
166 
167 	// extract snap resolution if present
168 	if (sscanf(machine.options().snap_size(), "%dx%d", &m_snap_width, &m_snap_height) != 2)
169 		m_snap_width = m_snap_height = 0;
170 
171 	// if no screens, create a periodic timer to drive updates
172 	if (no_screens)
173 	{
174 		m_screenless_frame_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(video_manager::screenless_update_callback), this));
175 		m_screenless_frame_timer->adjust(screen_device::DEFAULT_FRAME_PERIOD, 0, screen_device::DEFAULT_FRAME_PERIOD);
176 		machine.output().set_notifier(nullptr, video_notifier_callback, this);
177 	}
178 }
179 
180 
181 //-------------------------------------------------
182 //  set_frameskip - set the current actual
183 //  frameskip (-1 means autoframeskip)
184 //-------------------------------------------------
185 
set_frameskip(int frameskip)186 void video_manager::set_frameskip(int frameskip)
187 {
188 	// -1 means autoframeskip
189 	if (frameskip == -1)
190 	{
191 		m_auto_frameskip = true;
192 		m_frameskip_level = 0;
193 	}
194 
195 	// any other level is a direct control
196 	else if (frameskip >= 0 && frameskip <= MAX_FRAMESKIP)
197 	{
198 		m_auto_frameskip = false;
199 		m_frameskip_level = frameskip;
200 	}
201 }
202 
203 
204 //-------------------------------------------------
205 //  frame_update - handle frameskipping and UI,
206 //  plus updating the screen during normal
207 //  operations
208 //-------------------------------------------------
209 
frame_update(bool from_debugger)210 void video_manager::frame_update(bool from_debugger)
211 {
212 	// only render sound and video if we're in the running phase
213 	machine_phase const phase = machine().phase();
214 	bool skipped_it = m_skipping_this_frame;
215 	if (phase == machine_phase::RUNNING && (!machine().paused() || machine().options().update_in_pause()))
216 	{
217 		bool anything_changed = finish_screen_updates();
218 
219 		// if none of the screens changed and we haven't skipped too many frames in a row,
220 		// mark this frame as skipped to prevent throttling; this helps for games that
221 		// don't update their screen at the monitor refresh rate
222 		if (!anything_changed && !m_auto_frameskip && m_frameskip_level == 0 && m_empty_skip_count++ < 3)
223 			skipped_it = true;
224 		else
225 			m_empty_skip_count = 0;
226 	}
227 
228 	// draw the user interface
229 	emulator_info::draw_user_interface(machine());
230 
231 	// if we're throttling, synchronize before rendering
232 	attotime current_time = machine().time();
233 	if (!from_debugger && !skipped_it && phase > machine_phase::INIT && !m_low_latency && effective_throttle())
234 		update_throttle(current_time);
235 
236 	// ask the OSD to update
237 	g_profiler.start(PROFILER_BLIT);
238 	machine().osd().update(!from_debugger && skipped_it);
239 	g_profiler.stop();
240 
241 	// we synchronize after rendering instead of before, if low latency mode is enabled
242 	if (!from_debugger && !skipped_it && phase > machine_phase::INIT && m_low_latency && effective_throttle())
243 		update_throttle(current_time);
244 
245 	// get most recent input now
246 	machine().osd().input_update();
247 
248 	emulator_info::periodic_check();
249 
250 	// perform tasks for this frame
251 	if (!from_debugger)
252 		machine().call_notifiers(MACHINE_NOTIFY_FRAME);
253 
254 	// update frameskipping
255 	if (!from_debugger && phase > machine_phase::INIT)
256 		update_frameskip();
257 
258 	// update speed computations
259 	if (!from_debugger && !skipped_it && phase > machine_phase::INIT)
260 		recompute_speed(current_time);
261 
262 	// call the end-of-frame callback
263 	if (phase == machine_phase::RUNNING)
264 	{
265 		// reset partial updates if we're paused or if the debugger is active
266 		screen_device *const screen = screen_device_iterator(machine().root_device()).first();
267 		bool const debugger_enabled = machine().debug_flags & DEBUG_FLAG_ENABLED;
268 		bool const within_instruction_hook = debugger_enabled && machine().debugger().within_instruction_hook();
269 		if (screen && ((machine().paused() && machine().options().update_in_pause()) || from_debugger || within_instruction_hook))
270 			screen->reset_partial_updates();
271 	}
272 }
273 
274 
275 //-------------------------------------------------
276 //  speed_text - print the text to be displayed
277 //  into a string buffer
278 //-------------------------------------------------
279 
speed_text()280 std::string video_manager::speed_text()
281 {
282 	std::ostringstream str;
283 
284 	// if we're paused, just display Paused
285 	bool paused = machine().paused();
286 	if (paused)
287 		str << "paused";
288 
289 	// if we're fast forwarding, just display Fast-forward
290 	else if (m_fastforward)
291 		str << "fast ";
292 
293 	// if we're auto frameskipping, display that plus the level
294 	else if (effective_autoframeskip())
295 		util::stream_format(str, "auto%2d/%d", effective_frameskip(), m_frameskip_max ? m_frameskip_max : MAX_FRAMESKIP);
296 
297 	// otherwise, just display the frameskip plus the level
298 	else
299 		util::stream_format(str, "skip %d/%d", effective_frameskip(), MAX_FRAMESKIP);
300 
301 	// append the speed for all cases except paused
302 	if (!paused)
303 		util::stream_format(str, "%4d%%", (int)(100 * m_speed_percent + 0.5));
304 
305 	// display the number of partial updates as well
306 	int partials = 0;
307 	for (screen_device &screen : screen_device_iterator(machine().root_device()))
308 		partials += screen.partial_updates();
309 	if (partials > 1)
310 		util::stream_format(str, "\n%d partial updates", partials);
311 
312 	return str.str();
313 }
314 
315 
316 //-------------------------------------------------
317 //  save_snapshot - save a snapshot to the given
318 //  file handle
319 //-------------------------------------------------
320 
save_snapshot(screen_device * screen,emu_file & file)321 void video_manager::save_snapshot(screen_device *screen, emu_file &file)
322 {
323 	// validate
324 	assert(!m_snap_native || screen != nullptr);
325 
326 	// create the bitmap to pass in
327 	create_snapshot_bitmap(screen);
328 
329 	// add two text entries describing the image
330 	std::string text1 = std::string(emulator_info::get_appname()).append(" ").append(emulator_info::get_build_version());
331 	std::string text2 = std::string(machine().system().manufacturer).append(" ").append(machine().system().type.fullname());
332 	util::png_info pnginfo;
333 	pnginfo.add_text("Software", text1.c_str());
334 	pnginfo.add_text("System", text2.c_str());
335 
336 	// now do the actual work
337 	const rgb_t *palette = (screen != nullptr && screen->has_palette()) ? screen->palette().palette()->entry_list_adjusted() : nullptr;
338 	int entries = (screen != nullptr && screen->has_palette()) ? screen->palette().entries() : 0;
339 	util::png_error error = util::png_write_bitmap(file, &pnginfo, m_snap_bitmap, entries, palette);
340 	if (error != util::png_error::NONE)
341 		osd_printf_error("Error generating PNG for snapshot: png_error = %d\n", std::underlying_type_t<util::png_error>(error));
342 }
343 
344 
345 //-------------------------------------------------
346 //  save_active_screen_snapshots - save a
347 //  snapshot of all active screens
348 //-------------------------------------------------
349 
save_active_screen_snapshots()350 void video_manager::save_active_screen_snapshots()
351 {
352 	// if we're native, then write one snapshot per visible screen
353 	if (m_snap_native)
354 	{
355 		// write one snapshot per visible screen
356 		for (screen_device &screen : screen_device_iterator(machine().root_device()))
357 			if (machine().render().is_live(screen))
358 			{
359 				emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
360 				osd_file::error filerr = open_next(file, "png");
361 				if (filerr == osd_file::error::NONE)
362 					save_snapshot(&screen, file);
363 			}
364 	}
365 
366 	// otherwise, just write a single snapshot
367 	else
368 	{
369 		emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
370 		osd_file::error filerr = open_next(file, "png");
371 		if (filerr == osd_file::error::NONE)
372 			save_snapshot(nullptr, file);
373 	}
374 }
375 
376 
377 //-------------------------------------------------
378 //  save_input_timecode - add a line of current
379 //  timestamp to inp.timecode file
380 //-------------------------------------------------
381 
save_input_timecode()382 void video_manager::save_input_timecode()
383 {
384 	// if record timecode input is not active, do nothing
385 	if (!m_timecode_enabled) {
386 		return;
387 	}
388 	m_timecode_write = true;
389 }
390 
timecode_text(std::string & str)391 std::string &video_manager::timecode_text(std::string &str)
392 {
393 	attotime elapsed_time = machine().time() - m_timecode_start;
394 	str = string_format(" %s%s%02d:%02d %s",
395 			m_timecode_text,
396 			m_timecode_text.empty() ? "" : " ",
397 			(elapsed_time.m_seconds / 60) % 60,
398 			elapsed_time.m_seconds % 60,
399 			machine().paused() ? "[paused] " : "");
400 	return str;
401 }
402 
timecode_total_text(std::string & str)403 std::string &video_manager::timecode_total_text(std::string &str)
404 {
405 	attotime elapsed_time = m_timecode_total;
406 	if (machine().ui().show_timecode_counter()) {
407 		elapsed_time += machine().time() - m_timecode_start;
408 	}
409 	str = string_format("TOTAL %02d:%02d ",
410 			(elapsed_time.m_seconds / 60) % 60,
411 			elapsed_time.m_seconds % 60);
412 	return str;
413 }
414 
415 
416 //-------------------------------------------------
417 //  begin_recording_screen - begin recording a
418 //  movie for a specific screen
419 //-------------------------------------------------
420 
begin_recording_screen(const std::string & filename,uint32_t index,screen_device * screen,movie_recording::format format)421 void video_manager::begin_recording_screen(const std::string &filename, uint32_t index, screen_device *screen, movie_recording::format format)
422 {
423 	// determine the file extension
424 	const char *extension = movie_recording::format_file_extension(format);
425 
426 	// create the emu_file
427 	bool is_absolute_path = !filename.empty() && osd_is_absolute_path(filename);
428 	std::unique_ptr<emu_file> movie_file = std::make_unique<emu_file>(
429 		is_absolute_path ? "" : machine().options().snapshot_directory(),
430 		OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
431 
432 	// and open the actual file
433 	osd_file::error filerr = filename.empty()
434 		? open_next(*movie_file, extension)
435 		: movie_file->open(filename);
436 	if (filerr != osd_file::error::NONE)
437 	{
438 		osd_printf_error("Error creating movie, osd_file::error=%d\n", int(filerr));
439 		return;
440 	}
441 
442 	// we have a file; try to create the recording
443 	std::unique_ptr<movie_recording> recording = movie_recording::create(machine(), screen, format, std::move(movie_file), m_snap_bitmap);
444 
445 	// if successful push it onto the list
446 	if (recording)
447 		m_movie_recordings.push_back(std::move(recording));
448 }
449 
450 
451 //-------------------------------------------------
452 //  begin_recording - begin recording of a movie
453 //-------------------------------------------------
454 
begin_recording(const char * name,movie_recording::format format)455 void video_manager::begin_recording(const char *name, movie_recording::format format)
456 {
457 	// create a snapshot bitmap so we know what the target size is
458 	screen_device_iterator iterator = screen_device_iterator(machine().root_device());
459 	screen_device_iterator::auto_iterator iter = iterator.begin();
460 	uint32_t count = (uint32_t)iterator.count();
461 	const bool no_screens(!count);
462 
463 	if (no_screens)
464 	{
465 		assert(!m_snap_native);
466 		count = 1;
467 	}
468 
469 	// clear out existing recordings
470 	m_movie_recordings.clear();
471 
472 	if (m_snap_native)
473 	{
474 		for (uint32_t index = 0; index < count; index++, iter++)
475 		{
476 			create_snapshot_bitmap(iter.current());
477 
478 			std::string tempname;
479 			if (name)
480 				tempname = index > 0 ? name : util::string_format("%s%d", name, index);
481 			begin_recording_screen(
482 				tempname,
483 				index,
484 				iter.current(),
485 				format);
486 		}
487 	}
488 	else
489 	{
490 		create_snapshot_bitmap(nullptr);
491 		begin_recording_screen(name ? name : "", 0, iter.current(), format);
492 	}
493 }
494 
495 
496 //-------------------------------------------------
497 //  add_sound_to_recording - add sound to a movie
498 //  recording
499 //-------------------------------------------------
500 
add_sound_to_recording(const s16 * sound,int numsamples)501 void video_manager::add_sound_to_recording(const s16 *sound, int numsamples)
502 {
503 	for (auto &recording : m_movie_recordings)
504 		recording->add_sound_to_recording(sound, numsamples);
505 }
506 
507 
508 //-------------------------------------------------
509 //  video_exit - close down the video system
510 //-------------------------------------------------
511 
exit()512 void video_manager::exit()
513 {
514 	// stop recording any movie
515 	m_movie_recordings.clear();
516 
517 	// free the snapshot target
518 	machine().render().target_free(m_snap_target);
519 	m_snap_bitmap.reset();
520 
521 	// print a final result if we have at least 2 seconds' worth of data
522 	if (!emulator_info::standalone() && m_overall_emutime.seconds() >= 1)
523 	{
524 		osd_ticks_t tps = osd_ticks_per_second();
525 		double final_real_time = (double)m_overall_real_seconds + (double)m_overall_real_ticks / (double)tps;
526 		double final_emu_time = m_overall_emutime.as_double();
527 		osd_printf_info("Average speed: %.2f%% (%d seconds)\n", 100 * final_emu_time / final_real_time, (m_overall_emutime + attotime(0, ATTOSECONDS_PER_SECOND / 2)).seconds());
528 	}
529 }
530 
531 
532 //-------------------------------------------------
533 //  screenless_update_callback - update generator
534 //  when there are no screens to drive it
535 //-------------------------------------------------
536 
screenless_update_callback(void * ptr,int param)537 void video_manager::screenless_update_callback(void *ptr, int param)
538 {
539 	// force an update
540 	frame_update(false);
541 }
542 
543 
544 //-------------------------------------------------
545 //  postload - callback for resetting things after
546 //  state has been loaded
547 //-------------------------------------------------
548 
postload()549 void video_manager::postload()
550 {
551 	for (const auto &x : m_movie_recordings)
552 		x->set_next_frame_time(machine().time());
553 }
554 
555 
556 //-------------------------------------------------
557 //  is_recording - returns whether or not any
558 //  screen is currently recording
559 //-------------------------------------------------
560 
is_recording() const561 bool video_manager::is_recording() const
562 {
563 	return !m_movie_recordings.empty();
564 }
565 
566 //-------------------------------------------------
567 //  effective_autoframeskip - return the effective
568 //  autoframeskip value, accounting for fast
569 //  forward
570 //-------------------------------------------------
571 
effective_autoframeskip() const572 inline bool video_manager::effective_autoframeskip() const
573 {
574 	// if we're fast forwarding or paused, autoframeskip is disabled
575 	if (m_fastforward || machine().paused())
576 		return false;
577 
578 	// otherwise, it's up to the user
579 	return m_auto_frameskip;
580 }
581 
582 
583 //-------------------------------------------------
584 //  effective_frameskip - return the effective
585 //  frameskip value, accounting for fast
586 //  forward
587 //-------------------------------------------------
588 
effective_frameskip() const589 int video_manager::effective_frameskip() const
590 {
591 	// if we're fast forwarding, use the maximum frameskip
592 	if (m_fastforward)
593 		return FRAMESKIP_LEVELS - 1;
594 
595 	// otherwise, it's up to the user
596 	return m_frameskip_level;
597 }
598 
599 
600 //-------------------------------------------------
601 //  effective_throttle - return the effective
602 //  throttle value, accounting for fast
603 //  forward and user interface
604 //-------------------------------------------------
605 
effective_throttle() const606 inline bool video_manager::effective_throttle() const
607 {
608 	// if we're paused, or if the UI is active, we always throttle
609 	if (machine().paused() && !machine().options().update_in_pause()) //|| machine().ui().is_menu_active())
610 		return true;
611 
612 	// if we're fast forwarding, we don't throttle
613 	if (m_fastforward)
614 		return false;
615 
616 	// otherwise, it's up to the user
617 	return throttled();
618 }
619 
620 
621 //-------------------------------------------------
622 //  original_speed_setting - return the original
623 //  speed setting
624 //-------------------------------------------------
625 
original_speed_setting() const626 inline int video_manager::original_speed_setting() const
627 {
628 	return machine().options().speed() * 1000.0f + 0.5f;
629 }
630 
631 
632 //-------------------------------------------------
633 //  finish_screen_updates - finish updating all
634 //  the screens
635 //-------------------------------------------------
636 
finish_screen_updates()637 bool video_manager::finish_screen_updates()
638 {
639 	// finish updating the screens
640 	screen_device_iterator iter(machine().root_device());
641 
642 	bool has_live_screen = false;
643 	for (screen_device &screen : iter)
644 	{
645 		if (screen.partial_scan_hpos() >= 0) // previous update ended mid-scanline
646 			screen.update_now();
647 		screen.update_partial(screen.visible_area().max_y);
648 
649 		if (machine().render().is_live(screen))
650 			has_live_screen = true;
651 	}
652 
653 	bool anything_changed = !has_live_screen || m_output_changed;
654 	m_output_changed = false;
655 
656 	// now add the quads for all the screens
657 	for (screen_device &screen : iter)
658 		if (screen.update_quads())
659 			anything_changed = true;
660 
661 	// draw HUD from LUA callback (if any)
662 	anything_changed |= emulator_info::frame_hook();
663 
664 	// update our movie recording and burn-in state
665 	if (!machine().paused())
666 	{
667 		record_frame();
668 
669 		// iterate over screens and update the burnin for the ones that care
670 		for (screen_device &screen : iter)
671 			screen.update_burnin();
672 	}
673 
674 	// draw any crosshairs
675 	for (screen_device &screen : iter)
676 		machine().crosshair().render(screen);
677 
678 	return anything_changed;
679 }
680 
681 
682 
683 //-------------------------------------------------
684 //  update_throttle - throttle to the game's
685 //  natural speed
686 //-------------------------------------------------
687 
update_throttle(attotime emutime)688 void video_manager::update_throttle(attotime emutime)
689 {
690 /*
691 
692    Throttling theory:
693 
694    This routine is called periodically with an up-to-date emulated time.
695    The idea is to synchronize real time with emulated time. We do this
696    by "throttling", or waiting for real time to catch up with emulated
697    time.
698 
699    In an ideal world, it will take less real time to emulate and render
700    each frame than the emulated time, so we need to slow things down to
701    get both times in sync.
702 
703    There are many complications to this model:
704 
705        * some games run too slow, so each frame we get further and
706            further behind real time; our only choice here is to not
707            throttle
708 
709        * some games have very uneven frame rates; one frame will take
710            a long time to emulate, and the next frame may be very fast
711 
712        * we run on top of multitasking OSes; sometimes execution time
713            is taken away from us, and this means we may not get enough
714            time to emulate one frame
715 
716        * we may be paused, and emulated time may not be marching
717            forward
718 
719        * emulated time could jump due to resetting the machine or
720            restoring from a saved state
721 
722 */
723 	static const u8 popcount[256] =
724 	{
725 		0,1,1,2,1,2,2,3, 1,2,2,3,2,3,3,4, 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5,
726 		1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6,
727 		1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6,
728 		2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7,
729 		1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6,
730 		2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7,
731 		2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7,
732 		3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 4,5,5,6,5,6,6,7, 5,6,6,7,6,7,7,8
733 	};
734 
735 	// outer scope so we can break out in case of a resync
736 	while (1)
737 	{
738 		// apply speed factor to emu time
739 		if (m_speed != 0 && m_speed != 1000)
740 		{
741 			// multiply emutime by 1000, then divide by the global speed factor
742 			emutime = (emutime * 1000) / m_speed;
743 		}
744 
745 		// compute conversion factors up front
746 		osd_ticks_t ticks_per_second = osd_ticks_per_second();
747 		attoseconds_t attoseconds_per_tick = ATTOSECONDS_PER_SECOND / ticks_per_second * m_throttle_rate;
748 
749 		// if we're paused, emutime will not advance; instead, we subtract a fixed
750 		// amount of time (1/60th of a second) from the emulated time that was passed in,
751 		// and explicitly reset our tracked real and emulated timers to that value ...
752 		// this means we pretend that the last update was exactly 1/60th of a second
753 		// ago, and was in sync in both real and emulated time
754 		if (machine().paused())
755 		{
756 			m_throttle_emutime = emutime - attotime(0, ATTOSECONDS_PER_SECOND / PAUSED_REFRESH_RATE);
757 			m_throttle_realtime = m_throttle_emutime;
758 		}
759 
760 		// attempt to detect anomalies in the emulated time by subtracting the previously
761 		// reported value from our current value; this should be a small value somewhere
762 		// between 0 and 1/10th of a second ... anything outside of this range is obviously
763 		// wrong and requires a resync
764 		attoseconds_t emu_delta_attoseconds = (emutime - m_throttle_emutime).as_attoseconds();
765 		if (emu_delta_attoseconds < 0 || emu_delta_attoseconds > ATTOSECONDS_PER_SECOND / 10)
766 		{
767 			if (LOG_THROTTLE)
768 				machine().logerror("Resync due to weird emutime delta: %s\n", attotime(0, emu_delta_attoseconds).as_string(18));
769 			break;
770 		}
771 
772 		// now determine the current real time in OSD-specified ticks; we have to be careful
773 		// here because counters can wrap, so we only use the difference between the last
774 		// read value and the current value in our computations
775 		osd_ticks_t diff_ticks = osd_ticks() - m_throttle_last_ticks;
776 		m_throttle_last_ticks += diff_ticks;
777 
778 		// if it has been more than a full second of real time since the last call to this
779 		// function, we just need to resynchronize
780 		if (diff_ticks >= ticks_per_second)
781 		{
782 			if (LOG_THROTTLE)
783 				machine().logerror("Resync due to real time advancing by more than 1 second\n");
784 			break;
785 		}
786 
787 		// convert this value into attoseconds for easier comparison
788 		attoseconds_t real_delta_attoseconds = diff_ticks * attoseconds_per_tick;
789 
790 		// now update our real and emulated timers with the current values
791 		m_throttle_emutime = emutime;
792 		m_throttle_realtime += attotime(0, real_delta_attoseconds);
793 
794 		// keep a history of whether or not emulated time beat real time over the last few
795 		// updates; this can be used for future heuristics
796 		m_throttle_history = (m_throttle_history << 1) | (emu_delta_attoseconds > real_delta_attoseconds);
797 
798 		// determine how far ahead real time is versus emulated time; note that we use the
799 		// accumulated times for this instead of the deltas for the current update because
800 		// we want to track time over a longer duration than a single update
801 		attoseconds_t real_is_ahead_attoseconds = (m_throttle_emutime - m_throttle_realtime).as_attoseconds();
802 
803 		// if we're more than 1/10th of a second out, or if we are behind at all and emulation
804 		// is taking longer than the real frame, we just need to resync
805 		if (real_is_ahead_attoseconds < -ATTOSECONDS_PER_SECOND / 10 ||
806 			(real_is_ahead_attoseconds < 0 && popcount[m_throttle_history & 0xff] < 6))
807 		{
808 			if (LOG_THROTTLE)
809 				machine().logerror("Resync due to being behind: %s (history=%08X)\n", attotime(0, -real_is_ahead_attoseconds).as_string(18), m_throttle_history);
810 			break;
811 		}
812 
813 		// if we're behind, it's time to just get out
814 		if (real_is_ahead_attoseconds < 0)
815 			return;
816 
817 		// compute the target real time, in ticks, where we want to be
818 		osd_ticks_t target_ticks = m_throttle_last_ticks + real_is_ahead_attoseconds / attoseconds_per_tick;
819 
820 		// throttle until we read the target, and update real time to match the final time
821 		diff_ticks = throttle_until_ticks(target_ticks) - m_throttle_last_ticks;
822 		m_throttle_last_ticks += diff_ticks;
823 		m_throttle_realtime += attotime(0, diff_ticks * attoseconds_per_tick);
824 		return;
825 	}
826 
827 	// reset realtime and emutime to the same value
828 	m_throttle_realtime = m_throttle_emutime = emutime;
829 }
830 
831 
832 //-------------------------------------------------
833 //  throttle_until_ticks - spin until the
834 //  specified target time, calling the OSD code
835 //  to sleep if possible
836 //-------------------------------------------------
837 
throttle_until_ticks(osd_ticks_t target_ticks)838 osd_ticks_t video_manager::throttle_until_ticks(osd_ticks_t target_ticks)
839 {
840 	// we're allowed to sleep via the OSD code only if we're configured to do so
841 	// and we're not frameskipping due to autoframeskip, or if we're paused
842 	bool const allowed_to_sleep = (machine().options().sleep() && (!effective_autoframeskip() || effective_frameskip() == 0)) || machine().paused();
843 
844 	// loop until we reach our target
845 	g_profiler.start(PROFILER_IDLE);
846 	osd_ticks_t current_ticks = osd_ticks();
847 	while (current_ticks < target_ticks)
848 	{
849 		// compute how much time to sleep for, taking into account the average oversleep
850 		osd_ticks_t delta = target_ticks - current_ticks;
851 		if (delta > m_average_oversleep / 1000)
852 			delta -= m_average_oversleep / 1000;
853 		else
854 			delta = 0;
855 
856 		// see if we can sleep
857 		bool const slept = allowed_to_sleep && delta;
858 		if (slept)
859 			osd_sleep(delta);
860 
861 		// read the new value
862 		osd_ticks_t const new_ticks = osd_ticks();
863 
864 		// keep some metrics on the sleeping patterns of the OSD layer
865 		if (slept)
866 		{
867 			// if we overslept, keep an average of the amount
868 			osd_ticks_t const actual_ticks = new_ticks - current_ticks;
869 			if (actual_ticks > delta)
870 			{
871 				// take 99% of the previous average plus 1% of the new value
872 				osd_ticks_t const oversleep_milliticks = 1000 * (actual_ticks - delta);
873 				m_average_oversleep = (m_average_oversleep * 99 + oversleep_milliticks) / 100;
874 
875 				if (LOG_THROTTLE)
876 					machine().logerror("Slept for %d ticks, got %d ticks, avgover = %d\n", (int)delta, (int)actual_ticks, (int)m_average_oversleep);
877 			}
878 		}
879 		current_ticks = new_ticks;
880 	}
881 	g_profiler.stop();
882 
883 	return current_ticks;
884 }
885 
886 
887 //-------------------------------------------------
888 //  update_frameskip - update frameskipping
889 //  counters and periodically update autoframeskip
890 //-------------------------------------------------
891 
update_frameskip()892 void video_manager::update_frameskip()
893 {
894 	// if we're throttling and autoframeskip is on, adjust
895 	if (effective_throttle() && effective_autoframeskip() && m_frameskip_counter == 0)
896 	{
897 		// calibrate the "adjusted speed" based on the target
898 		double adjusted_speed_percent = m_speed_percent / (double) m_throttle_rate;
899 
900 		// if we're too fast, attempt to decrease the frameskip
901 		double speed = m_speed * 0.001;
902 		if (adjusted_speed_percent >= 0.995 * speed)
903 		{
904 			// but only after 3 consecutive frames where we are too fast
905 			if (++m_frameskip_adjust >= 3)
906 			{
907 				m_frameskip_adjust = 0;
908 				if (m_frameskip_level > 0)
909 					m_frameskip_level--;
910 			}
911 		}
912 
913 		// if we're too slow, attempt to increase the frameskip
914 		else
915 		{
916 			// if below 80% speed, be more aggressive
917 			if (adjusted_speed_percent < 0.80 *  speed)
918 				m_frameskip_adjust -= (0.90 * speed - m_speed_percent) / 0.05;
919 
920 			// if we're close, only force it up to frameskip 8
921 			else if (m_frameskip_level < 8)
922 				m_frameskip_adjust--;
923 
924 			// perform the adjustment
925 			while (m_frameskip_adjust <= -2)
926 			{
927 				m_frameskip_adjust += 2;
928 				if (m_frameskip_level < (m_frameskip_max ? m_frameskip_max : MAX_FRAMESKIP))
929 					m_frameskip_level++;
930 			}
931 		}
932 	}
933 
934 	// increment the frameskip counter and determine if we will skip the next frame
935 	m_frameskip_counter = (m_frameskip_counter + 1) % FRAMESKIP_LEVELS;
936 	m_skipping_this_frame = s_skiptable[effective_frameskip()][m_frameskip_counter];
937 }
938 
939 
940 //-------------------------------------------------
941 //  update_refresh_speed - update the m_speed
942 //  based on the maximum refresh rate supported
943 //-------------------------------------------------
944 
update_refresh_speed()945 void video_manager::update_refresh_speed()
946 {
947 	// only do this if the refreshspeed option is used
948 	if (machine().options().refresh_speed())
949 	{
950 		double minrefresh = machine().render().max_update_rate();
951 		if (minrefresh != 0)
952 		{
953 			// find the screen with the shortest frame period (max refresh rate)
954 			// note that we first check the token since this can get called before all screens are created
955 			attoseconds_t min_frame_period = ATTOSECONDS_PER_SECOND;
956 			for (screen_device &screen : screen_device_iterator(machine().root_device()))
957 			{
958 				attoseconds_t period = screen.frame_period().attoseconds();
959 				if (period != 0)
960 					min_frame_period = std::min(min_frame_period, period);
961 			}
962 
963 			// compute a target speed as an integral percentage
964 			// note that we lop 0.25Hz off of the minrefresh when doing the computation to allow for
965 			// the fact that most refresh rates are not accurate to 10 digits...
966 			u32 target_speed = floor((minrefresh - 0.25) * 1000.0 / ATTOSECONDS_TO_HZ(min_frame_period));
967 			u32 original_speed = original_speed_setting();
968 			target_speed = std::min(target_speed, original_speed);
969 
970 			// if we changed, log that verbosely
971 			if (target_speed != m_speed)
972 			{
973 				osd_printf_verbose("Adjusting target speed to %.1f%% (hw=%.2fHz, game=%.2fHz, adjusted=%.2fHz)\n", target_speed / 10.0, minrefresh, ATTOSECONDS_TO_HZ(min_frame_period), ATTOSECONDS_TO_HZ(min_frame_period * 1000.0 / target_speed));
974 				m_speed = target_speed;
975 			}
976 		}
977 	}
978 }
979 
980 
981 //-------------------------------------------------
982 //  recompute_speed - recompute the current
983 //  overall speed; we assume this is called only
984 //  if we did not skip a frame
985 //-------------------------------------------------
986 
recompute_speed(const attotime & emutime)987 void video_manager::recompute_speed(const attotime &emutime)
988 {
989 	// if we don't have a starting time yet, or if we're paused, reset our starting point
990 	if (m_speed_last_realtime == 0 || machine().paused())
991 	{
992 		m_speed_last_realtime = osd_ticks();
993 		m_speed_last_emutime = emutime;
994 	}
995 
996 	// if it has been more than the update interval, update the time
997 	attotime delta_emutime = emutime - m_speed_last_emutime;
998 	if (delta_emutime > attotime(0, ATTOSECONDS_PER_SPEED_UPDATE))
999 	{
1000 		// convert from ticks to attoseconds
1001 		osd_ticks_t realtime = osd_ticks();
1002 		osd_ticks_t delta_realtime = realtime - m_speed_last_realtime;
1003 		osd_ticks_t tps = osd_ticks_per_second();
1004 		m_speed_percent = delta_emutime.as_double() * (double)tps / (double)delta_realtime;
1005 
1006 		// remember the last times
1007 		m_speed_last_realtime = realtime;
1008 		m_speed_last_emutime = emutime;
1009 
1010 		// if we're throttled, this time period counts for overall speed; otherwise, we reset the counter
1011 		if (!m_fastforward)
1012 			m_overall_valid_counter++;
1013 		else
1014 			m_overall_valid_counter = 0;
1015 
1016 		// if we've had at least 4 consecutive valid periods, accumulate stats
1017 		if (m_overall_valid_counter >= 4)
1018 		{
1019 			m_overall_real_ticks += delta_realtime;
1020 			while (m_overall_real_ticks >= tps)
1021 			{
1022 				m_overall_real_ticks -= tps;
1023 				m_overall_real_seconds++;
1024 			}
1025 			m_overall_emutime += delta_emutime;
1026 		}
1027 	}
1028 
1029 	// if we're past the "time-to-execute" requested, signal an exit
1030 	if (m_seconds_to_run != 0 && emutime.seconds() >= m_seconds_to_run)
1031 	{
1032 		// create a final screenshot
1033 		emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
1034 		osd_file::error filerr = file.open(machine().basename() + PATH_SEPARATOR "final.png");
1035 		if (filerr == osd_file::error::NONE)
1036 			save_snapshot(nullptr, file);
1037 
1038 		//printf("Scheduled exit at %f\n", emutime.as_double());
1039 		// schedule our demise
1040 		machine().schedule_exit();
1041 	}
1042 }
1043 
1044 
1045 //-------------------------------------------------
1046 //  create_snapshot_bitmap - creates a
1047 //  bitmap containing the screenshot for the
1048 //  given screen
1049 //-------------------------------------------------
1050 
1051 typedef software_renderer<u32, 0,0,0, 16,8,0, false, true> snap_renderer_bilinear;
1052 typedef software_renderer<u32, 0,0,0, 16,8,0, false, false> snap_renderer;
1053 
create_snapshot_bitmap(screen_device * screen)1054 void video_manager::create_snapshot_bitmap(screen_device *screen)
1055 {
1056 	// select the appropriate view in our dummy target
1057 	if (m_snap_native && screen != nullptr)
1058 	{
1059 		screen_device_iterator iter(machine().root_device());
1060 		int view_index = iter.indexof(*screen);
1061 		assert(view_index != -1);
1062 		m_snap_target->set_view(view_index);
1063 	}
1064 
1065 	// get the minimum width/height and set it on the target
1066 	s32 width, height;
1067 	compute_snapshot_size(width, height);
1068 	m_snap_target->set_bounds(width, height);
1069 
1070 	// if we don't have a bitmap, or if it's not the right size, allocate a new one
1071 	if (!m_snap_bitmap.valid() || width != m_snap_bitmap.width() || height != m_snap_bitmap.height())
1072 		m_snap_bitmap.allocate(width, height);
1073 
1074 	// render the screen there
1075 	render_primitive_list &primlist = m_snap_target->get_primitives();
1076 	primlist.acquire_lock();
1077 	if (machine().options().snap_bilinear())
1078 		snap_renderer_bilinear::draw_primitives(primlist, &m_snap_bitmap.pix(0), width, height, m_snap_bitmap.rowpixels());
1079 	else
1080 		snap_renderer::draw_primitives(primlist, &m_snap_bitmap.pix(0), width, height, m_snap_bitmap.rowpixels());
1081 	primlist.release_lock();
1082 }
1083 
1084 
1085 //-------------------------------------------------
1086 //  compute_snapshot_size - computes width and
1087 //  height of the current snapshot target
1088 //  accounting for OPTION_SNAPSIZE
1089 //-------------------------------------------------
1090 
compute_snapshot_size(s32 & width,s32 & height)1091 void video_manager::compute_snapshot_size(s32 &width, s32 &height)
1092 {
1093 	width = m_snap_width;
1094 	height = m_snap_height;
1095 	if (width == 0 || height == 0)
1096 		m_snap_target->compute_minimum_size(width, height);
1097 }
1098 
1099 
1100 //-------------------------------------------------
1101 //  pixels - fills the specified buffer with the
1102 //  RGB values of each pixel in the snapshot target
1103 //-------------------------------------------------
1104 
pixels(u32 * buffer)1105 void video_manager::pixels(u32 *buffer)
1106 {
1107 	create_snapshot_bitmap(nullptr);
1108 	for (int y = 0; y < m_snap_bitmap.height(); y++)
1109 	{
1110 		const u32 *src = &m_snap_bitmap.pix(y, 0);
1111 		for (int x = 0; x < m_snap_bitmap.width(); x++)
1112 		{
1113 			*buffer++ = *src++;
1114 		}
1115 	}
1116 }
1117 
1118 
1119 //-------------------------------------------------
1120 //  open_next - open the next non-existing file of
1121 //  type filetype according to our numbering
1122 //  scheme
1123 //-------------------------------------------------
1124 
open_next(emu_file & file,const char * extension,uint32_t added_index)1125 osd_file::error video_manager::open_next(emu_file &file, const char *extension, uint32_t added_index)
1126 {
1127 	u32 origflags = file.openflags();
1128 
1129 	// handle defaults
1130 	const char *snapname = machine().options().snap_name();
1131 
1132 	if (snapname == nullptr || snapname[0] == 0)
1133 		snapname = "%g/%i";
1134 	std::string snapstr(snapname);
1135 
1136 	// strip any extension in the provided name
1137 	int index = snapstr.find_last_of('.');
1138 	if (index != -1)
1139 		snapstr = snapstr.substr(0, index);
1140 
1141 	// handle %d in the template (for image devices)
1142 	std::string snapdev("%d_");
1143 	int pos = snapstr.find(snapdev);
1144 
1145 	if (pos != -1)
1146 	{
1147 		// if more %d are found, revert to default and ignore them all
1148 		if (snapstr.find(snapdev, pos + 3) != -1)
1149 			snapstr.assign("%g/%i");
1150 		// else if there is a single %d, try to create the correct snapname
1151 		else
1152 		{
1153 			int name_found = 0;
1154 
1155 			// find length of the device name
1156 			int end1 = snapstr.find('/', pos + 3);
1157 			int end2 = snapstr.find('%', pos + 3);
1158 			int end;
1159 
1160 			if ((end1 != -1) && (end2 != -1))
1161 				end = std::min(end1, end2);
1162 			else if (end1 != -1)
1163 				end = end1;
1164 			else if (end2 != -1)
1165 				end = end2;
1166 			else
1167 				end = snapstr.length();
1168 
1169 			if (end - pos < 3)
1170 				fatalerror("Something very wrong is going on!!!\n");
1171 
1172 			// copy the device name to an std::string
1173 			std::string snapdevname;
1174 			snapdevname.assign(snapstr.substr(pos + 3, end - pos - 3));
1175 			//printf("check template: %s\n", snapdevname.c_str());
1176 
1177 			// verify that there is such a device for this system
1178 			for (device_image_interface &image : image_interface_iterator(machine().root_device()))
1179 			{
1180 				// get the device name
1181 				std::string tempdevname(image.brief_instance_name());
1182 				//printf("check device: %s\n", tempdevname.c_str());
1183 
1184 				if (snapdevname.compare(tempdevname) == 0)
1185 				{
1186 					// verify that such a device has an image mounted
1187 					if (image.basename() != nullptr)
1188 					{
1189 						std::string filename(image.basename());
1190 
1191 						// strip extension
1192 						filename = filename.substr(0, filename.find_last_of('.'));
1193 
1194 						// setup snapname and remove the %d_
1195 						strreplace(snapstr, snapdevname, filename);
1196 						snapstr.erase(pos, 3);
1197 						//printf("check image: %s\n", filename.c_str());
1198 
1199 						name_found = 1;
1200 					}
1201 				}
1202 			}
1203 
1204 			// or fallback to default
1205 			if (name_found == 0)
1206 				snapstr.assign("%g/%i");
1207 		}
1208 	}
1209 
1210 	// add our own extension
1211 	snapstr.append(".").append(extension);
1212 
1213 	// substitute path and gamename up front
1214 	strreplace(snapstr, "/", PATH_SEPARATOR);
1215 	strreplace(snapstr, "%g", machine().basename());
1216 
1217 	// determine if the template has an index; if not, we always use the same name
1218 	std::string fname;
1219 	if (snapstr.find("%i") == -1)
1220 		fname.assign(snapstr);
1221 
1222 	// otherwise, we scan for the next available filename
1223 	else
1224 	{
1225 		// try until we succeed
1226 		file.set_openflags(OPEN_FLAG_WRITE);
1227 		for (int seq = 0; ; seq++)
1228 		{
1229 			// build up the filename
1230 			fname.assign(snapstr);
1231 			strreplace(fname, "%i", string_format("%04d", seq));
1232 
1233 			// try to open the file; stop when we fail
1234 			osd_file::error filerr = file.open(fname);
1235 			if (filerr == osd_file::error::NOT_FOUND)
1236 			{
1237 				break;
1238 			}
1239 		}
1240 	}
1241 
1242 	// create the final file
1243 	file.set_openflags(origflags);
1244 	return file.open(fname);
1245 }
1246 
1247 
1248 //-------------------------------------------------
1249 //  record_frame - record a frame of a movie
1250 //-------------------------------------------------
1251 
record_frame()1252 void video_manager::record_frame()
1253 {
1254 	// ignore if nothing to do
1255 	if (!is_recording())
1256 		return;
1257 
1258 	// start the profiler and get the current time
1259 	g_profiler.start(PROFILER_MOVIE_REC);
1260 	attotime curtime = machine().time();
1261 
1262 	bool error = false;
1263 	for (auto &recording : m_movie_recordings)
1264 	{
1265 		// create the bitmap
1266 		create_snapshot_bitmap(recording->screen());
1267 
1268 		// and append the frame
1269 		if (!recording->append_video_frame(m_snap_bitmap, curtime))
1270 		{
1271 			error = true;
1272 			break;
1273 		}
1274 	}
1275 
1276 	if (error)
1277 		end_recording();
1278 	g_profiler.stop();
1279 }
1280 
1281 //-------------------------------------------------
1282 //  toggle_throttle
1283 //-------------------------------------------------
1284 
toggle_throttle()1285 void video_manager::toggle_throttle()
1286 {
1287 	set_throttled(!throttled());
1288 }
1289 
1290 
1291 //-------------------------------------------------
1292 //  toggle_record_movie
1293 //-------------------------------------------------
1294 
toggle_record_movie(movie_recording::format format)1295 void video_manager::toggle_record_movie(movie_recording::format format)
1296 {
1297 	if (!is_recording())
1298 	{
1299 		begin_recording(nullptr, format);
1300 		machine().popmessage("REC START (%s)", format == movie_recording::format::MNG ? "MNG" : "AVI");
1301 	}
1302 	else
1303 	{
1304 		end_recording();
1305 		machine().popmessage("REC STOP");
1306 	}
1307 }
1308 
end_recording()1309 void video_manager::end_recording()
1310 {
1311 	m_movie_recordings.clear();
1312 }
1313