void video_manager::exit() { // stop recording any movie end_recording(); // free all the graphics elements for (int i = 0; i < MAX_GFX_ELEMENTS; i++) gfx_element_free(machine().gfx[i]); // free the snapshot target machine().render().target_free(m_snap_target); if (m_snap_bitmap != NULL) global_free(m_snap_bitmap); // print a final result if we have at least 5 seconds' worth of data if (m_overall_emutime.seconds >= 5) { osd_ticks_t tps = osd_ticks_per_second(); double final_real_time = (double)m_overall_real_seconds + (double)m_overall_real_ticks / (double)tps; double final_emu_time = m_overall_emutime.as_double(); 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); } }
void poly_wait(poly_manager *poly, const char *debug_reason) { osd_ticks_t time; /* remember the start time if we're logging */ if (LOG_WAITS) time = get_profile_ticks(); /* wait for all pending work items to complete */ if (poly->queue != NULL) osd_work_queue_wait(poly->queue, osd_ticks_per_second() * 100); /* if we don't have a queue, just run the whole list now */ else { int unitnum; for (unitnum = 0; unitnum < poly->unit_next; unitnum++) poly_item_callback(poly->unit[unitnum], 0); } /* log any long waits */ if (LOG_WAITS) { time = get_profile_ticks() - time; if (time > LOG_WAIT_THRESHOLD) logerror("Poly:Waited %d cycles for %s\n", (int)time, debug_reason); } /* reset the state */ poly->polygon_next = poly->unit_next = 0; memset(poly->unit_bucket, 0xff, sizeof(poly->unit_bucket)); /* we need to preserve the last extra data that was supplied */ if (poly->extra_next > 1) memcpy(poly->extra[0], poly->extra[poly->extra_next - 1], poly->extra_size); poly->extra_next = 1; }
bool ui_input_pressed_repeat(running_machine &machine, int code, int speed) { ui_input_private *uidata = machine.ui_input_data; int pressed; g_profiler.start(PROFILER_INPUT); /* get the status of this key (assumed to be only in the defaults) */ assert(code >= IPT_UI_CONFIGURE && code <= IPT_OSD_16); pressed = (uidata->seqpressed[code] == SEQ_PRESSED_TRUE); /* if down, handle it specially */ if (pressed) { osd_ticks_t tps = osd_ticks_per_second(); /* if this is the first press, set a 3x delay and leave pressed = 1 */ if (uidata->next_repeat[code] == 0) uidata->next_repeat[code] = osd_ticks() + 3 * speed * tps / 60; /* if this is an autorepeat case, set a 1x delay and leave pressed = 1 */ else if (speed > 0 && (osd_ticks() + tps - uidata->next_repeat[code]) >= tps) uidata->next_repeat[code] += 1 * speed * tps / 60; /* otherwise, reset pressed = 0 */ else pressed = false; } /* if we're not pressed, reset the memory field */ else uidata->next_repeat[code] = 0; g_profiler.stop(); return pressed; }
void renderer_sdl2::render_quad(texture_info *texture, const render_primitive &prim, const int x, const int y) { SDL_Rect target_rect; target_rect.x = x; target_rect.y = y; target_rect.w = round_nearest(prim.bounds.x1 - prim.bounds.x0); target_rect.h = round_nearest(prim.bounds.y1 - prim.bounds.y0); if (texture) { copy_info_t *copyinfo = texture->m_copyinfo; copyinfo->time -= osd_ticks(); texture->render_quad(prim, x, y); copyinfo->time += osd_ticks(); copyinfo->pixel_count += std::max(STAT_PIXEL_THRESHOLD , (texture->raw_width() * texture->raw_height())); if (m_last_blit_pixels) { copyinfo->time += (m_last_blit_time * (INT64) (texture->raw_width() * texture->raw_height())) / (INT64) m_last_blit_pixels; } copyinfo->samples++; copyinfo->perf = ( texture->m_copyinfo->pixel_count * (osd_ticks_per_second()/1000)) / texture->m_copyinfo->time; } else { UINT32 sr = (UINT32)(255.0f * prim.color.r); UINT32 sg = (UINT32)(255.0f * prim.color.g); UINT32 sb = (UINT32)(255.0f * prim.color.b); UINT32 sa = (UINT32)(255.0f * prim.color.a); SDL_SetRenderDrawBlendMode(m_sdl_renderer, map_blendmode(PRIMFLAG_GET_BLENDMODE(prim.flags))); SDL_SetRenderDrawColor(m_sdl_renderer, sr, sg, sb, sa); SDL_RenderFillRect(m_sdl_renderer, &target_rect); } }
void watchdog::setTimeout(int timeout) { m_timeout = timeout * osd_ticks_per_second(); this->reset(); }
void mc146818_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { int year/*, month*/; if (id == TIMER_PERIODIC) { m_data[0x0c] |= 0xc0; if (!m_out_irq_func.isnull()) m_out_irq_func(CLEAR_LINE); return; } if (m_type == MC146818_UTC) { // hack: set correct real time even for overloaded emulation // (at least for apollo) static osd_ticks_t t0 = 0; osd_ticks_t t1 = osd_ticks(); int n_seconds; if (t0 == 0) { t0 = t1; } n_seconds = (t1 - t0) / osd_ticks_per_second(); t0 = t1 - (t1 - t0) % osd_ticks_per_second(); if (n_seconds <= 0) { // we were called to early return; } m_data[0] += n_seconds; } else { m_data[0] += 1; } if (BCD_MODE) { m_data[0]=bcd_adjust(m_data[0]/*+1*/); if (m_data[0]>=0x60) { m_data[0]=0; m_data[2]=bcd_adjust(m_data[2]+1); if (m_data[2]>=0x60) { m_data[2]=0; m_data[4]=bcd_adjust(m_data[4]+1); // different handling of hours if (m_data[4]>=0x24) { m_data[4]=0; WEEK_DAY=bcd_adjust(WEEK_DAY+1)%7; DAY=bcd_adjust(DAY+1); //month=bcd_2_dec(MONTH); year=bcd_2_dec(YEAR); if (m_type!=MC146818_IGNORE_CENTURY) year+=bcd_2_dec(CENTURY)*100; else year+=2000; // save for julian_days_in_month calculation DAY=bcd_adjust(DAY+1); if (DAY>gregorian_days_in_month(MONTH, year)) { DAY=1; MONTH=bcd_adjust(MONTH+1); if (MONTH>0x12) { MONTH=1; YEAR=year=bcd_adjust(YEAR+1); if (m_type!=MC146818_IGNORE_CENTURY) { if (year>=0x100) { CENTURY=bcd_adjust(CENTURY+1); } } } } } } } } else { /*m_data[0]=m_data[0]+1;*/ if (m_data[0]>=60) { m_data[0] -= 60; m_data[2]=m_data[2]+1; if (m_data[2]>=60) { m_data[2]=0; m_data[4]=m_data[4]+1; // different handling of hours //? if (m_data[4]>=24) { m_data[4]=0; WEEK_DAY=(WEEK_DAY+1)%7; year=YEAR; if (m_type!=MC146818_IGNORE_CENTURY) year+=CENTURY*100; else year+=2000; // save for julian_days_in_month calculation if (++DAY>gregorian_days_in_month(MONTH, year)) { DAY=1; if (++MONTH>12) { MONTH=1; YEAR++; if (m_type!=MC146818_IGNORE_CENTURY) { if (YEAR>=100) { CENTURY++;YEAR=0; } } else { YEAR%=100; } } } } } } } if (m_data[1] == m_data[0] && // m_data[3] == m_data[2] && // m_data[5] == m_data[4]) { // set the alarm interrupt flag AF m_data[0x0c] |= 0x20; } else { // clear the alarm interrupt flag AF m_data[0x0c] &= ~0x20; if ((m_data[0x0c] & 0x70) == 0) { // clear IRQF m_data[0x0c] &= ~0x80; } } // set the update-ended interrupt Flag UF m_data[0x0c] |= 0x10; // set the interrupt request flag IRQF // FIXME: should throw IRQ line as well if ((m_data[0x0b] & m_data[0x0c] & 0x30) != 0) { m_data[0x0c] |= 0x80; } // IRQ line is active low if (!m_out_irq_func.isnull()) m_out_irq_func((m_data[0x0c] & 0x80) ? CLEAR_LINE : ASSERT_LINE); m_updated = true; /* clock has been updated */ m_last_refresh = machine().time(); }
void video_manager::update_throttle(attotime emutime) { /* Throttling theory: This routine is called periodically with an up-to-date emulated time. The idea is to synchronize real time with emulated time. We do this by "throttling", or waiting for real time to catch up with emulated time. In an ideal world, it will take less real time to emulate and render each frame than the emulated time, so we need to slow things down to get both times in sync. There are many complications to this model: * some games run too slow, so each frame we get further and further behind real time; our only choice here is to not throttle * some games have very uneven frame rates; one frame will take a long time to emulate, and the next frame may be very fast * we run on top of multitasking OSes; sometimes execution time is taken away from us, and this means we may not get enough time to emulate one frame * we may be paused, and emulated time may not be marching forward * emulated time could jump due to resetting the machine or restoring from a saved state */ static const UINT8 popcount[256] = { 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, 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, 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, 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, 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, 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, 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, 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 }; // outer scope so we can break out in case of a resync while (1) { // apply speed factor to emu time if (m_speed != 0 && m_speed != 1000) { // multiply emutime by 1000, then divide by the global speed factor emutime = (emutime * 1000) / m_speed; } // compute conversion factors up front osd_ticks_t ticks_per_second = osd_ticks_per_second(); attoseconds_t attoseconds_per_tick = ATTOSECONDS_PER_SECOND / ticks_per_second * m_throttle_rate; // if we're paused, emutime will not advance; instead, we subtract a fixed // amount of time (1/60th of a second) from the emulated time that was passed in, // and explicitly reset our tracked real and emulated timers to that value ... // this means we pretend that the last update was exactly 1/60th of a second // ago, and was in sync in both real and emulated time if (machine().paused()) { m_throttle_emutime = emutime - attotime(0, ATTOSECONDS_PER_SECOND / PAUSED_REFRESH_RATE); m_throttle_realtime = m_throttle_emutime; } // attempt to detect anomalies in the emulated time by subtracting the previously // reported value from our current value; this should be a small value somewhere // between 0 and 1/10th of a second ... anything outside of this range is obviously // wrong and requires a resync attoseconds_t emu_delta_attoseconds = (emutime - m_throttle_emutime).as_attoseconds(); if (emu_delta_attoseconds < 0 || emu_delta_attoseconds > ATTOSECONDS_PER_SECOND / 10) { if (LOG_THROTTLE) machine().logerror("Resync due to weird emutime delta: %s\n", attotime(0, emu_delta_attoseconds).as_string(18)); break; } // now determine the current real time in OSD-specified ticks; we have to be careful // here because counters can wrap, so we only use the difference between the last // read value and the current value in our computations osd_ticks_t diff_ticks = osd_ticks() - m_throttle_last_ticks; m_throttle_last_ticks += diff_ticks; // if it has been more than a full second of real time since the last call to this // function, we just need to resynchronize if (diff_ticks >= ticks_per_second) { if (LOG_THROTTLE) machine().logerror("Resync due to real time advancing by more than 1 second\n"); break; } // convert this value into attoseconds for easier comparison attoseconds_t real_delta_attoseconds = diff_ticks * attoseconds_per_tick; // now update our real and emulated timers with the current values m_throttle_emutime = emutime; m_throttle_realtime += attotime(0, real_delta_attoseconds); // keep a history of whether or not emulated time beat real time over the last few // updates; this can be used for future heuristics m_throttle_history = (m_throttle_history << 1) | (emu_delta_attoseconds > real_delta_attoseconds); // determine how far ahead real time is versus emulated time; note that we use the // accumulated times for this instead of the deltas for the current update because // we want to track time over a longer duration than a single update attoseconds_t real_is_ahead_attoseconds = (m_throttle_emutime - m_throttle_realtime).as_attoseconds(); // if we're more than 1/10th of a second out, or if we are behind at all and emulation // is taking longer than the real frame, we just need to resync if (real_is_ahead_attoseconds < -ATTOSECONDS_PER_SECOND / 10 || (real_is_ahead_attoseconds < 0 && popcount[m_throttle_history & 0xff] < 6)) { if (LOG_THROTTLE) machine().logerror("Resync due to being behind: %s (history=%08X)\n", attotime(0, -real_is_ahead_attoseconds).as_string(18), m_throttle_history); break; } // if we're behind, it's time to just get out if (real_is_ahead_attoseconds < 0) return; // compute the target real time, in ticks, where we want to be osd_ticks_t target_ticks = m_throttle_last_ticks + real_is_ahead_attoseconds / attoseconds_per_tick; // throttle until we read the target, and update real time to match the final time diff_ticks = throttle_until_ticks(target_ticks) - m_throttle_last_ticks; m_throttle_last_ticks += diff_ticks; m_throttle_realtime += attotime(0, diff_ticks * attoseconds_per_tick); return; } // reset realtime and emutime to the same value m_throttle_realtime = m_throttle_emutime = emutime; }
void sdlwindow_video_window_update(running_machine &machine, sdl_window_info *window) { osd_ticks_t event_wait_ticks; ASSERT_MAIN_THREAD(); // adjust the cursor state sdlwindow_update_cursor_state(machine, window); // if we're visible and running and not in the middle of a resize, draw if (window->target != NULL) { int tempwidth, tempheight; // see if the games video mode has changed window->target->compute_minimum_size(tempwidth, tempheight); if (video_config.switchres && window->fullscreen && machine.options().changeres()) { if (machine.switchRes.resolution.changeres) switchres_resolution_change(machine, window, tempwidth, tempheight); } else if (tempwidth != window->minwidth || tempheight != window->minheight) { window->minwidth = tempwidth; window->minheight = tempheight; if (!window->fullscreen) { sdlwindow_blit_surface_size(window, window->width, window->height); sdlwindow_resize(window, window->blitwidth, window->blitheight); } else if (video_config.switchres) { pick_best_mode(window, &tempwidth, &tempheight); sdlwindow_resize(window, tempwidth, tempheight); } } if (video_config.waitvsync || video_config.syncrefresh) event_wait_ticks = osd_ticks_per_second(); // block at most a second else event_wait_ticks = 0; if (osd_event_wait(window->rendered_event, event_wait_ticks)) { worker_param wp; render_primitive_list *primlist; clear_worker_param(&wp); // ensure the target bounds are up-to-date, and then get the primitives primlist = &window->get_primitives(window); // and redraw now wp.list = primlist; wp.window = window; wp.m_machine = &machine; execute_async(&draw_video_contents_wt, &wp); } } }
void sdl_window_info::update() { osd_ticks_t event_wait_ticks; ASSERT_MAIN_THREAD(); // adjust the cursor state //sdlwindow_update_cursor_state(machine, window); execute_async(&update_cursor_state_wt, worker_param(this)); // if we're visible and running and not in the middle of a resize, draw if (m_target != NULL) { int tempwidth, tempheight; // see if the games video mode has changed m_target->compute_minimum_size(tempwidth, tempheight); if (video_config.switchres && m_fullscreen && machine().options().changeres() && machine().switchres.game.changeres) { switchres_resolution_change(this); return; } else if (osd_dim(tempwidth, tempheight) != m_minimum_dim) { m_minimum_dim = osd_dim(tempwidth, tempheight); if (!this->m_fullscreen) { //Don't resize window without user interaction; //window_resize(blitwidth, blitheight); } else if (video_config.switchres) { osd_dim tmp = this->pick_best_mode(); resize(tmp.width(), tmp.height()); } } int got_lock = false; got_lock = osd_lock_try(m_render_lock); // only render if we were able to get the lock if (got_lock) { // don't hold the lock; we just used it to see if rendering was still happening osd_lock_release(m_render_lock); // ensure the target bounds are up-to-date, and then get the primitives render_primitive_list &primlist = *m_renderer->get_primitives(); // and redraw now osd_event_reset(m_rendered_event); execute_async(&draw_video_contents_wt, worker_param(this, primlist)); if (video_config.waitvsync && machine().video().throttled()) { event_wait_ticks = osd_ticks_per_second(); // block at most a second osd_event_wait(m_rendered_event, event_wait_ticks); } } } }
void sdl_window_info::update() { osd_ticks_t event_wait_ticks; // adjust the cursor state //sdlwindow_update_cursor_state(machine, window); update_cursor_state(); // if we're visible and running and not in the middle of a resize, draw if (m_target != nullptr) { int tempwidth, tempheight; // see if the games video mode has changed m_target->compute_minimum_size(tempwidth, tempheight); if (osd_dim(tempwidth, tempheight) != m_minimum_dim) { m_minimum_dim = osd_dim(tempwidth, tempheight); if (!this->m_fullscreen) { //Don't resize window without user interaction; //window_resize(blitwidth, blitheight); } else if (video_config.switchres) { osd_dim tmp = this->pick_best_mode(); resize(tmp.width(), tmp.height()); } } if (video_config.waitvsync && video_config.syncrefresh) event_wait_ticks = osd_ticks_per_second(); // block at most a second else event_wait_ticks = 0; if (m_rendered_event.wait(event_wait_ticks)) { const int update = 1; // ensure the target bounds are up-to-date, and then get the primitives render_primitive_list &primlist = *renderer().get_primitives(); // and redraw now // Some configurations require events to be polled in the worker thread downcast< sdl_osd_interface& >(machine().osd()).process_events_buf(); // Check whether window has vector screens { const screen_device *screen = screen_device_iterator(machine().root_device()).byindex(m_index); if ((screen != nullptr) && (screen->screen_type() == SCREEN_TYPE_VECTOR)) renderer().set_flags(osd_renderer::FLAG_HAS_VECTOR_SCREEN); else renderer().clear_flags(osd_renderer::FLAG_HAS_VECTOR_SCREEN); } m_primlist = &primlist; // if no bitmap, just fill if (m_primlist == nullptr) { } // otherwise, render with our drawing system else { if( video_config.perftest ) measure_fps(update); else renderer().draw(update); } /* all done, ready for next */ m_rendered_event.set(); } } }
virtual void process_event(SDL_Event &sdlevent) override { switch (sdlevent.type) { case SDL_MOUSEMOTION: mouse.lX += sdlevent.motion.xrel * INPUT_RELATIVE_PER_PIXEL; mouse.lY += sdlevent.motion.yrel * INPUT_RELATIVE_PER_PIXEL; { int cx = -1, cy = -1; auto window = GET_FOCUS_WINDOW(&sdlevent.motion); if (window != nullptr && window->xy_to_render_target(sdlevent.motion.x, sdlevent.motion.y, &cx, &cy)) machine().ui_input().push_mouse_move_event(window->target(), cx, cy); } break; case SDL_MOUSEBUTTONDOWN: mouse.buttons[sdlevent.button.button - 1] = 0x80; //printf("But down %d %d %d %d %s\n", event.button.which, event.button.button, event.button.x, event.button.y, devinfo->name.c_str()); if (sdlevent.button.button == 1) { // FIXME Move static declaration static osd_ticks_t last_click = 0; static int last_x = 0; static int last_y = 0; int cx, cy; osd_ticks_t click = osd_ticks() * 1000 / osd_ticks_per_second(); auto window = GET_FOCUS_WINDOW(&sdlevent.button); if (window != nullptr && window->xy_to_render_target(sdlevent.button.x, sdlevent.button.y, &cx, &cy)) { machine().ui_input().push_mouse_down_event(window->target(), cx, cy); // FIXME Parameter ? if ((click - last_click < 250) && (cx >= last_x - 4 && cx <= last_x + 4) && (cy >= last_y - 4 && cy <= last_y + 4)) { last_click = 0; machine().ui_input().push_mouse_double_click_event(window->target(), cx, cy); } else { last_click = click; last_x = cx; last_y = cy; } } } else if (sdlevent.button.button == 3) { int cx, cy; auto window = GET_FOCUS_WINDOW(&sdlevent.button); if (window != nullptr && window->xy_to_render_target(sdlevent.button.x, sdlevent.button.y, &cx, &cy)) { machine().ui_input().push_mouse_rdown_event(window->target(), cx, cy); } } break; case SDL_MOUSEBUTTONUP: mouse.buttons[sdlevent.button.button - 1] = 0; //printf("But up %d %d %d %d\n", event.button.which, event.button.button, event.button.x, event.button.y); if (sdlevent.button.button == 1) { int cx, cy; auto window = GET_FOCUS_WINDOW(&sdlevent.button); if (window != nullptr && window->xy_to_render_target(sdlevent.button.x, sdlevent.button.y, &cx, &cy)) { machine().ui_input().push_mouse_up_event(window->target(), cx, cy); } } else if (sdlevent.button.button == 3) { int cx, cy; auto window = GET_FOCUS_WINDOW(&sdlevent.button); if (window != nullptr && window->xy_to_render_target(sdlevent.button.x, sdlevent.button.y, &cx, &cy)) { machine().ui_input().push_mouse_rup_event(window->target(), cx, cy); } } break; case SDL_MOUSEWHEEL: auto window = GET_FOCUS_WINDOW(&sdlevent.wheel); if (window != nullptr) machine().ui_input().push_mouse_wheel_event(window->target(), 0, 0, sdlevent.wheel.y, 3); break; } }
int osd_event_wait(osd_event *event, osd_ticks_t timeout) { #ifdef WIN32 int ret = WaitForSingleObject((HANDLE) event, timeout * 1000 / osd_ticks_per_second()); if (ret != WAIT_OBJECT_0) return FALSE; #else pthread_mutex_lock(&event->mutex); if (!timeout) { if (!event->signalled) { pthread_mutex_unlock(&event->mutex); return FALSE; } } else { if (!event->signalled) { struct timespec ts; struct timeval tp; UINT64 msec ;//= timeout * 1000 / osd_ticks_per_second(); UINT64 nsec; gettimeofday(&tp, NULL); if(timeout!=OSD_EVENT_WAIT_INFINITE) msec = timeout * 1000 / osd_ticks_per_second(); else msec = (OSD_EVENT_WAIT_INFINITE - tp.tv_usec * (UINT64) 1000)/osd_ticks_per_second(); ts.tv_sec = tp.tv_sec; nsec = (UINT64) tp.tv_usec * (UINT64) 1000 + (msec * (UINT64) 1000000); ts.tv_nsec = nsec % (UINT64) 1000000000; ts.tv_sec += nsec / (UINT64) 1000000000; do { int ret = pthread_cond_timedwait(&event->cond, &event->mutex, &ts); if ( ret == ETIMEDOUT ) { if (event->signalled) break; pthread_mutex_unlock(&event->mutex); return FALSE; } if (ret == 0) break; } while (TRUE); } } if (event->autoreset) event->signalled = 0; pthread_mutex_unlock(&event->mutex); #endif return TRUE; }
static void update_throttle(mame_time emutime) { static double ticks_per_sleep_msec = 0; osd_ticks_t target, curr, cps, diffticks; int allowed_to_sleep; subseconds_t subsecs_per_cycle; int paused = mame_is_paused(Machine); // if we're only syncing to the refresh, bail now if (video_config.syncrefresh) return; // if we're paused, emutime will not advance; explicitly resync and set us backwards // 1/60th of a second if (paused) throttle_realtime = throttle_emutime = sub_subseconds_from_mame_time(emutime, MAX_SUBSECONDS / PAUSED_REFRESH_RATE); // if time moved backwards (reset), or if it's been more than 1 second in emulated time, resync if (compare_mame_times(emutime, throttle_emutime) < 0 || sub_mame_times(emutime, throttle_emutime).seconds > 0) goto resync; // get the current realtime; if it's been more than 1 second realtime, just resync cps = osd_ticks_per_second(); diffticks = osd_ticks() - throttle_last_ticks; throttle_last_ticks += diffticks; if (diffticks >= cps) goto resync; // add the time that has passed to the real time subsecs_per_cycle = MAX_SUBSECONDS / cps; throttle_realtime = add_subseconds_to_mame_time(throttle_realtime, diffticks * subsecs_per_cycle); // update the emulated time throttle_emutime = emutime; // if we're behind, just sync if (compare_mame_times(throttle_emutime, throttle_realtime) <= 0) goto resync; // determine our target ticks value target = throttle_last_ticks + sub_mame_times(throttle_emutime, throttle_realtime).subseconds / subsecs_per_cycle; // initialize the ticks per sleep if (ticks_per_sleep_msec == 0) ticks_per_sleep_msec = (double)(cps / 1000); // this counts as idle time profiler_mark(PROFILER_IDLE); // determine whether or not we are allowed to sleep allowed_to_sleep = video_config.sleep && (!effective_autoframeskip() || effective_frameskip() == 0); // sync for (curr = osd_ticks(); curr - target < 0; curr = osd_ticks()) { // if we have enough time to sleep, do it // ...but not if we're autoframeskipping and we're behind if (paused || (allowed_to_sleep && (target - curr) > (osd_ticks_t)(ticks_per_sleep_msec * 1.1))) { osd_ticks_t next; // keep track of how long we actually slept Sleep(1); next = osd_ticks(); ticks_per_sleep_msec = (ticks_per_sleep_msec * 0.90) + ((double)(next - curr) * 0.10); } } // idle time done profiler_mark(PROFILER_END); // update realtime diffticks = osd_ticks() - throttle_last_ticks; throttle_last_ticks += diffticks; throttle_realtime = add_subseconds_to_mame_time(throttle_realtime, diffticks * subsecs_per_cycle); return; resync: // reset realtime and emutime to the same value throttle_realtime = throttle_emutime = emutime; }
void video_manager::recompute_speed(attotime emutime) { // if we don't have a starting time yet, or if we're paused, reset our starting point if (m_speed_last_realtime == 0 || machine().paused()) { m_speed_last_realtime = osd_ticks(); m_speed_last_emutime = emutime; } // if it has been more than the update interval, update the time attotime delta_emutime = emutime - m_speed_last_emutime; if (delta_emutime > attotime(0, ATTOSECONDS_PER_SPEED_UPDATE)) { // convert from ticks to attoseconds osd_ticks_t realtime = osd_ticks(); osd_ticks_t delta_realtime = realtime - m_speed_last_realtime; osd_ticks_t tps = osd_ticks_per_second(); m_speed_percent = delta_emutime.as_double() * (double)tps / (double)delta_realtime; // Redraw patch m_machine.speed_percent = m_speed_percent; // remember the last times m_speed_last_realtime = realtime; m_speed_last_emutime = emutime; // if we're throttled, this time period counts for overall speed; otherwise, we reset the counter if (!m_fastforward) m_overall_valid_counter++; else m_overall_valid_counter = 0; // if we've had at least 4 consecutive valid periods, accumulate stats if (m_overall_valid_counter >= 4) { m_overall_real_ticks += delta_realtime; while (m_overall_real_ticks >= tps) { m_overall_real_ticks -= tps; m_overall_real_seconds++; } m_overall_emutime += delta_emutime; } } // if we're past the "time-to-execute" requested, signal an exit if (m_seconds_to_run != 0 && emutime.seconds >= m_seconds_to_run) { if (machine().primary_screen != NULL) { // create a final screenshot emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); file_error filerr = file.open(machine().basename(), PATH_SEPARATOR "final.png"); if (filerr == FILERR_NONE) save_snapshot(machine().primary_screen, file); } // schedule our demise machine().schedule_exit(); } }
int osd_event_wait(osd_event *event, osd_ticks_t timeout) { #ifdef WIN32 int ret = WaitForSingleObject((HANDLE) event, timeout * 1000 / osd_ticks_per_second()); return ( ret == WAIT_OBJECT_0); #else pthread_mutex_lock(&event->mutex); if (!timeout) { if (!event->signalled) { pthread_mutex_unlock(&event->mutex); return FALSE; } } else { if (!event->signalled) { struct timespec ts; struct timeval tp; UINT64 msec = timeout * 1000 / osd_ticks_per_second(); UINT64 nsec; gettimeofday(&tp, NULL); ts.tv_sec = tp.tv_sec; nsec = (UINT64) tp.tv_usec * (UINT64) 1000 + (msec * (UINT64) 1000000); ts.tv_nsec = nsec % (UINT64) 1000000000; ts.tv_sec += nsec / (UINT64) 1000000000; do { int ret = pthread_cond_timedwait(&event->cond, &event->mutex, &ts); if ( ret == ETIMEDOUT ) { if (!event->signalled) { pthread_mutex_unlock(&event->mutex); return FALSE; } else break; } if (ret == 0) break; if ( ret != EINTR) { printf("Error %d while waiting for pthread_cond_timedwait: %s\n", ret, strerror(ret)); } } while (TRUE); } } if (event->autoreset) event->signalled = 0; pthread_mutex_unlock(&event->mutex); return TRUE; #endif }
bool input_manager::seq_poll() { int curlen = m_poll_seq.length(); input_code lastcode = m_poll_seq[curlen - 1]; // switch case: see if we have a new code to process input_code newcode; if (m_poll_seq_class == ITEM_CLASS_SWITCH) { newcode = poll_switches(); if (newcode != INPUT_CODE_INVALID) { // if code is duplicate, toggle the NOT state on the code if (curlen > 0 && newcode == lastcode) { // back up over the existing code m_poll_seq.backspace(); // if there was a NOT preceding it, delete it as well, otherwise append a fresh one if (m_poll_seq[curlen - 2] == input_seq::not_code) m_poll_seq.backspace(); else m_poll_seq += input_seq::not_code; } } } // absolute/relative case: see if we have an analog change of sufficient amount else { bool has_or = false; if (lastcode == input_seq::or_code) { lastcode = m_poll_seq[curlen - 2]; has_or = true; } newcode = poll_axes(); // if the last code doesn't match absolute/relative of this code, ignore the new one if ((lastcode.item_class() == ITEM_CLASS_ABSOLUTE && newcode.item_class() != ITEM_CLASS_ABSOLUTE) || (lastcode.item_class() == ITEM_CLASS_RELATIVE && newcode.item_class() != ITEM_CLASS_RELATIVE)) newcode = INPUT_CODE_INVALID; // if the new code is valid, check for half-axis toggles on absolute controls if (newcode != INPUT_CODE_INVALID && curlen > 0 && newcode.item_class() == ITEM_CLASS_ABSOLUTE) { input_code last_nomodifier = lastcode; last_nomodifier.set_item_modifier(ITEM_MODIFIER_NONE); if (newcode == last_nomodifier) { // increment the modifier, wrapping back to none switch (lastcode.item_modifier()) { case ITEM_MODIFIER_NONE: newcode.set_item_modifier(ITEM_MODIFIER_POS); break; case ITEM_MODIFIER_POS: newcode.set_item_modifier(ITEM_MODIFIER_NEG); break; default: case ITEM_MODIFIER_NEG: newcode.set_item_modifier(ITEM_MODIFIER_NONE); break; } // back up over the previous code so we can re-append if (has_or) m_poll_seq.backspace(); m_poll_seq.backspace(); } } } // if we got a new code to append it, append it and reset the timer if (newcode != INPUT_CODE_INVALID) { m_poll_seq += newcode; m_poll_seq_last_ticks = osd_ticks(); } // if we're recorded at least one item and 2/3 of a second has passed, we're done if (m_poll_seq_last_ticks != 0 && osd_ticks() > m_poll_seq_last_ticks + osd_ticks_per_second() * 2 / 3) { // if the final result is invalid, reset to nothing if (!m_poll_seq.is_valid()) m_poll_seq.reset(); // return true to indicate that we are finished return true; } // return false to indicate we are still polling return false; }