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