Guest User

Untitled

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