void osd_update_video_and_audio(struct mame_display *display) { cycles_t cps = osd_cycles_per_second(); // if this is the first time through, initialize the previous time value if (warming_up) { last_skipcount0_time = osd_cycles() - (int)((double)FRAMESKIP_LEVELS * (double)cps / video_fps); warming_up = 0; } // if this is the first frame in a sequence, adjust the base time for this frame if (frameskip_counter == 0) this_frame_base = last_skipcount0_time + (int)((double)FRAMESKIP_LEVELS * (double)cps / video_fps); // if we're not skipping this frame, draw it if (display->changed_flags & GAME_BITMAP_CHANGED) update_timing(); //// if the LEDs have changed, update them //if (display->changed_flags & LED_STATE_CHANGED) // osd_set_leds(display->led_state); // increment the frameskip counter frameskip_counter = (frameskip_counter + 1) % FRAMESKIP_LEVELS; // check for inputs check_inputs(); }
static void recompute_fps(int skipped_it) { /* increment the frame counters */ frames_since_last_fps++; if (!skipped_it) rendered_frames_since_last_fps++; /* if we didn't skip this frame, we may be able to compute a new FPS */ if (!skipped_it && frames_since_last_fps >= FRAMES_PER_FPS_UPDATE) { cycles_t cps = osd_cycles_per_second(); cycles_t curr = osd_cycles(); double seconds_elapsed = (double)(curr - last_fps_time) * (1.0 / (double)cps); double frames_per_sec = (double)frames_since_last_fps / seconds_elapsed; /* compute the performance data */ performance.game_speed_percent = 100.0 * frames_per_sec / Machine->refresh_rate; performance.frames_per_second = (double)rendered_frames_since_last_fps / seconds_elapsed; /* reset the info */ last_fps_time = curr; frames_since_last_fps = 0; rendered_frames_since_last_fps = 0; } /* for vector games, compute the vector update count once/second */ vfcount++; if (vfcount >= (int)Machine->refresh_rate) { performance.vector_updates_last_second = vector_updates; vector_updates = 0; vfcount -= (int)Machine->refresh_rate; } }
void profiler_mark(int type) { unsigned int curr_cycles; if (!use_profiler) { FILO_length = 0; return; } if (type >= PROFILER_CPU1 && type <= PROFILER_CPU8) profile.cpu_context_switches[memory]++; curr_cycles = osd_cycles(); if (type != PROFILER_END) { if (FILO_length >= 10) { logerror("Profiler error: FILO buffer overflow\n"); return; } if (FILO_length > 0) { /* handle nested calls */ profile.count[memory][FILO_type[FILO_length-1]] += (unsigned int)(curr_cycles - FILO_start[FILO_length-1]); } FILO_type[FILO_length] = type; FILO_start[FILO_length] = curr_cycles; FILO_length++; } else { if (FILO_length <= 0) { logerror("Profiler error: FILO buffer underflow\n"); return; } profile.count[memory][FILO_type[FILO_length-1]] += (unsigned int)(curr_cycles - FILO_start[FILO_length-1]); FILO_length--; if (FILO_length > 0) { /* handle nested calls */ FILO_start[FILO_length-1] = curr_cycles; } } }
static void update_timing() { cycles_t curr; if (fastfrms >= 0) { if (fastfrms-- == 0) throttle = 1; else throttle = 0; } // if we're throttling, synchronize if (throttle || game_is_paused) throttle_speed(); // at the end, we need the current time curr = osd_cycles(); // update stats for the FPS average calculation if (start_time == 0) { // start the timer going 1 second into the game if (timer_get_time() > 1.0) start_time = curr; } else { frames_displayed++; if (frames_displayed + 1 == frames_to_display) win_trying_to_quit = 1; end_time = curr; } // if we're at the start of a frameskip sequence, compute the speed if (frameskip_counter == 0) last_skipcount0_time = curr; //// update the bitmap we're drawing //profiler_mark(PROFILER_BLIT); //win_update_video_window(bitmap, bounds, vector_dirty_pixels); //profiler_mark(PROFILER_END); // if we're throttling and autoframeskip is on, adjust if (throttle && autoframeskip && frameskip_counter == 0) update_autoframeskip(); }
int video_init(void) { osd_create_params params; artwork_callbacks *artcallbacks; int bmwidth = Machine->drv->screen_width; int bmheight = Machine->drv->screen_height; movie_file = NULL; movie_frame = 0; add_pause_callback(video_pause); add_exit_callback(video_exit); /* first allocate the necessary palette structures */ if (palette_start()) return 1; #ifndef NEW_RENDER /* if we're a vector game, override the screen width and height */ if (Machine->drv->video_attributes & VIDEO_TYPE_VECTOR) scale_vectorgames(options.vector_width, options.vector_height, &bmwidth, &bmheight); /* compute the visible area for raster games */ if (!(Machine->drv->video_attributes & VIDEO_TYPE_VECTOR)) { params.width = Machine->drv->default_visible_area.max_x - Machine->drv->default_visible_area.min_x + 1; params.height = Machine->drv->default_visible_area.max_y - Machine->drv->default_visible_area.min_y + 1; } else { params.width = bmwidth; params.height = bmheight; } /* fill in the rest of the display parameters */ compute_aspect_ratio(Machine->drv, ¶ms.aspect_x, ¶ms.aspect_y); params.depth = Machine->color_depth; params.colors = palette_get_total_colors_with_ui(); params.fps = Machine->drv->frames_per_second; params.video_attributes = Machine->drv->video_attributes; #ifdef MESS artcallbacks = &mess_artwork_callbacks; #else artcallbacks = &mame_artwork_callbacks; #endif /* initialize the display through the artwork (and eventually the OSD) layer */ if (artwork_create_display(¶ms, direct_rgb_components, artcallbacks)) return 1; /* the create display process may update the vector width/height, so recompute */ if (Machine->drv->video_attributes & VIDEO_TYPE_VECTOR) scale_vectorgames(options.vector_width, options.vector_height, &bmwidth, &bmheight); /* now allocate the screen bitmap */ scrbitmap[0] = auto_bitmap_alloc_depth(bmwidth, bmheight, Machine->color_depth); if (!scrbitmap[0]) return 1; #endif /* set the default refresh rate */ set_refresh_rate(Machine->drv->frames_per_second); /* set the default visible area */ set_visible_area(0,1,0,1); // make sure everything is recalculated on multiple runs set_visible_area( Machine->drv->default_visible_area.min_x, Machine->drv->default_visible_area.max_x, Machine->drv->default_visible_area.min_y, Machine->drv->default_visible_area.max_y); /* create spriteram buffers if necessary */ if (Machine->drv->video_attributes & VIDEO_BUFFERS_SPRITERAM) if (init_buffered_spriteram()) return 1; #ifndef NEW_RENDER #if defined(MAME_DEBUG) && !defined(NEW_DEBUGGER) /* if the debugger is enabled, initialize its bitmap and font */ if (Machine->debug_mode) { int depth = options.debug_depth ? options.debug_depth : Machine->color_depth; /* first allocate the debugger bitmap */ Machine->debug_bitmap = auto_bitmap_alloc_depth(options.debug_width, options.debug_height, depth); if (!Machine->debug_bitmap) return 1; /* then create the debugger font */ Machine->debugger_font = build_debugger_font(); if (Machine->debugger_font == NULL) return 1; } #endif #endif /* convert the gfx ROMs into character sets. This is done BEFORE calling the driver's */ /* palette_init() routine because it might need to check the Machine->gfx[] data */ if (Machine->drv->gfxdecodeinfo) if (allocate_graphics(Machine->drv->gfxdecodeinfo)) return 1; /* initialize the palette - must be done after osd_create_display() */ if (palette_init()) return 1; /* force the first update to be full */ set_vh_global_attribute(NULL, 0); /* actually decode the graphics */ if (Machine->drv->gfxdecodeinfo) decode_graphics(Machine->drv->gfxdecodeinfo); /* reset performance data */ last_fps_time = osd_cycles(); rendered_frames_since_last_fps = frames_since_last_fps = 0; performance.game_speed_percent = 100; performance.frames_per_second = Machine->refresh_rate; performance.vector_updates_last_second = 0; /* reset video statics and get out of here */ pdrawgfx_shadow_lowpri = 0; leds_status = 0; /* initialize tilemaps */ if (tilemap_init() != 0) fatalerror("tilemap_init failed"); return 0; }
void throttle_speed_part(int part, int totalparts) { static double ticks_per_sleep_msec = 0; cycles_t target, curr, cps; #ifdef VPINMAME if ((g_hEnterThrottle != INVALID_HANDLE_VALUE) && g_iSyncFactor) { if (g_iSyncFactor >= 1024) SetEvent(g_hEnterThrottle); else { iCurrentSyncValue += g_iSyncFactor; if (iCurrentSyncValue >= 1024) { SetEvent(g_hEnterThrottle); iCurrentSyncValue -= 1024; } } } #endif //// if we're only syncing to the refresh, bail now //if (win_sync_refresh) // return; // this counts as idle time profiler_mark(PROFILER_IDLE); // get the current time and the target time curr = osd_cycles(); cps = osd_cycles_per_second(); target = this_frame_base + (int)((double)frameskip_counter * (double)cps / video_fps); // If we are throttling to a fractional vsync, adjust target to the partial target. if (totalparts != 1) { // Meh. The points in the code where frameskip counter gets updated is different from where the frame base is // reset. Makes this delay computation complicated. if (frameskip_counter == 0) target += (int)((double)(FRAMESKIP_LEVELS) * (double)cps / video_fps); // MAGIC: Experimentation with actual resuts show the most even distribution if I throttle to 1/7th increments at each 25% timestep. target -= ((cycles_t)((double)cps / (video_fps * (totalparts + 3)))) * (totalparts - part + 3); } // initialize the ticks per sleep if (ticks_per_sleep_msec == 0) ticks_per_sleep_msec = (double)cps / 1000.; // Adjust target for sound catchup if (g_iThrottleAdj) { target -= (cycles_t)(g_iThrottleAdj*ticks_per_sleep_msec); } // sync if (curr - target < 0) { #ifdef DEBUG_THROTTLE { char tmp[91]; sprintf(tmp, "Throt: part %d of %d FS: %d Delta: %lld\n", part, totalparts, frameskip_counter, curr - target); OutputDebugString(tmp); } #endif // loop until we reach the target time while (curr - target < 0) { #if 1 // VPINMAME //if((INT64)((target - curr)/(ticks_per_sleep_msec*1.1))-1 > 0) // pessimistic estimate of stuff below, but still stutters then // uSleep((UINT64)((target - curr)*1000/(ticks_per_sleep_msec*1.1))-1); if (totalparts > 1) uUnderSleep((UINT64)((target - curr) * 1000 / ticks_per_sleep_msec)); // will sleep too short else uOverSleep((UINT64)((target - curr) * 1000 / ticks_per_sleep_msec)); // will sleep too long #else // if we have enough time to sleep, do it // ...but not if we're autoframeskipping and we're behind if (allow_sleep && (!autoframeskip || frameskip == 0) && (target - curr) > (cycles_t)(ticks_per_sleep_msec * 1.1)) { cycles_t next; // keep track of how long we actually slept uSleep(100); //1000? next = osd_cycles(); ticks_per_sleep_msec = (ticks_per_sleep_msec * 0.90) + ((double)(next - curr) * 0.10); curr = next; } else #endif { // update the current time curr = osd_cycles(); } } } else if (curr - target >= (int)(cps / video_fps) && totalparts == 1) { // We're behind schedule by a frame or more. Something must // have taken longer than it should have (e.g., a CPU emulator // time slice must have gone on too long). We don't have a // time machine, so we can't go back and sync this frame to a // time in the past, but we can at least sync up the current // frame with the current real time. // // Note that the 12-frame "skip" cycle would eventually get // things back in sync even without this adjustment, but it // can cause audio glitching if we wait until then. The skip // cycle will try to make up for the lost time by giving shorter // time slices to the next batch of 12 frames, but the way it // does its calculation, the time taken out of those short // frames will pile up in the *next next* skip cycle, causing // a long (order of 100ms) pause that can manifset as an audio // glitch and/or video hiccup. // // The adjustment here is simply the amount of real time by // which we're behind schedule. Add this to the base time, // since the real time for this frame is later than we expected. this_frame_base += curr - target; } // idle time done profiler_mark(PROFILER_END); }
//--------------------------------------------------------------------- // MoveCursor //--------------------------------------------------------------------- void CVirtualKeyboardView::MoveCursor( CInputManager &gp, BOOL unused ) { static UINT64 lastTime = 0; UINT64 curTime = osd_cycles(); FLOAT elapsedTime = (FLOAT)(curTime - lastTime) / (FLOAT)osd_cycles_per_second(); if( !lastTime ) { // lastTime isn't valid yet, so wait for the next frame lastTime = curTime; return; } lastTime = curTime; // Decrement the dpad movement timer if( m_dpadCursorDelay > 0.0f ) { m_dpadCursorDelay -= elapsedTime; if( m_dpadCursorDelay < 0.0f || !gp.IsOneOfButtonsPressed( GP_DPAD_MASK | GP_LA_MASK ) ) m_dpadCursorDelay = 0.0f; } if( m_buttonDelay > 0.0f ) { m_buttonDelay -= elapsedTime; if( m_buttonDelay < 0.0f || !(gp.IsOneOfButtonsPressed( GP_A | GP_B ) || gp.IsAnyKeyPressed()) ) m_buttonDelay = 0.0f; } if( m_dpadCursorDelay == 0.0f ) { if( gp.IsOneOfButtonsPressed( GP_DPAD_DOWN | GP_LA_DOWN ) ) { if( m_cursorPositionY < GetNumBodyLines() - 1 ) ++m_cursorPositionY; else m_cursorPositionY = 0; // Force m_cursorPositionX to be in range if( m_cursorPositionY < 4 ) { if( m_cursorPositionX >= wcslen( g_keyboardData[m_cursorPositionY] ) ) m_cursorPositionX = wcslen( g_keyboardData[m_cursorPositionY] ) - 1; } // else // m_cursorPositionX = 0; m_dpadCursorDelay = DPADCURSORMOVE_TIMEOUT; } else if( gp.IsOneOfButtonsPressed( GP_DPAD_UP | GP_LA_UP ) ) { if( m_cursorPositionY ) --m_cursorPositionY; else m_cursorPositionY = GetNumBodyLines() - 1; // Force m_cursorPositionX to be in range if( m_cursorPositionY < 4 ) { if( m_cursorPositionX >= wcslen( g_keyboardData[m_cursorPositionY] ) ) m_cursorPositionX = wcslen( g_keyboardData[m_cursorPositionY] ) - 1; } m_dpadCursorDelay = DPADCURSORMOVE_TIMEOUT; } if( gp.IsOneOfButtonsPressed( GP_DPAD_LEFT | GP_LA_LEFT ) ) { if( m_cursorPositionY < 4 ) { if( m_cursorPositionX ) --m_cursorPositionX; else m_cursorPositionX = wcslen( g_keyboardData[m_cursorPositionY] ) - 1; } m_dpadCursorDelay = DPADCURSORMOVE_TIMEOUT; } else if( gp.IsOneOfButtonsPressed( GP_DPAD_RIGHT | GP_LA_RIGHT ) ) { if( m_cursorPositionY < 4 ) { if( m_cursorPositionX < wcslen( g_keyboardData[m_cursorPositionY] ) - 1 ) ++m_cursorPositionX; else m_cursorPositionX = 0; } m_dpadCursorDelay = DPADCURSORMOVE_TIMEOUT; } } if( m_buttonDelay == 0.0f ) { if( gp.IsButtonPressed( GP_A ) ) { m_buttonDelay = DPADCURSORMOVE_TIMEOUT; // See if the cursor is on OK or Cancel if( m_cursorPositionY == 5 ) { m_inputState = MENU_ACCEPTED; return; } else if( m_cursorPositionY == 4 ) { m_inputState = MENU_CANCELLED; return; } // Add the new char char newChar[2] = {0}; wctomb( &newChar[0], g_keyboardData[m_cursorPositionY][m_cursorPositionX] ); m_data += newChar; if( m_data.length() >= (m_maxDisplayableChars - 1) ) ++m_dataDrawStartPosition; } else if( gp.IsButtonPressed( GP_B ) ) { // Remove the last character from the data string m_buttonDelay = DPADCURSORMOVE_TIMEOUT; if( m_data.length() ) { m_data = m_data.substr( 0, m_data.length() - 1 ); if( m_dataDrawStartPosition ) --m_dataDrawStartPosition; } } // Check the keyboard if( gp.IsKeyPressed( VK_BACK ) ) { m_buttonDelay = KEYBOARDINPUT_TIMEOUT; if( m_data.length() ) { m_data = m_data.substr( 0, m_data.length() - 1 ); if( m_dataDrawStartPosition ) --m_dataDrawStartPosition; } } else { // Colon (PERIOD on a German KB, Semicolon on a US) BYTE keys[2] = { VK_OEM_PERIOD, VK_OEM_1 }; if( gp.IsOneOfKeysPressed( keys, 2 ) ) { m_buttonDelay = KEYBOARDINPUT_TIMEOUT; m_data += ":"; if( m_data.length() >= (m_maxDisplayableChars - 1) ) ++m_dataDrawStartPosition; } // Slash (German / is handled elsewhere) if( gp.IsKeyPressed( VK_OEM_2 ) ) { m_buttonDelay = KEYBOARDINPUT_TIMEOUT; m_data += "/"; if( m_data.length() >= (m_maxDisplayableChars - 1) ) ++m_dataDrawStartPosition; } // Backslash (German: VK_OEM_MINUS, US: VK_OEM_5) keys[0] = VK_OEM_MINUS; keys[1] = VK_OEM_5; if( gp.IsOneOfKeysPressed( keys, 2 ) ) { m_buttonDelay = KEYBOARDINPUT_TIMEOUT; m_data += "\\"; if( m_data.length() >= (m_maxDisplayableChars - 1) ) ++m_dataDrawStartPosition; } BYTE keyIndex = 'A'; for( ; keyIndex < 'Z'; ++keyIndex ) { if( gp.IsKeyPressed( keyIndex ) ) { m_buttonDelay = KEYBOARDINPUT_TIMEOUT; // Add the new char char newChar[2] = {0}; newChar[0] = (char)keyIndex; // Handle the @ symbol (German KB) if( keyIndex == 'Q' && gp.IsKeyPressed( VK_RMENU ) ) newChar[0] = '@'; m_data += newChar; if( m_data.length() >= (m_maxDisplayableChars - 1) ) ++m_dataDrawStartPosition; break; } } keys[0] = VK_LSHIFT; keys[1] = VK_RSHIFT; for( keyIndex = '0'; keyIndex < '9'; ++keyIndex ) { if( gp.IsKeyPressed( keyIndex ) ) { m_buttonDelay = KEYBOARDINPUT_TIMEOUT; // Add the new char char newChar[2] = {0}; newChar[0] = (char)keyIndex; // Handle the @ symbol if( keyIndex == '2' && gp.IsOneOfKeysPressed( keys, 2 ) ) newChar[0] = '@'; else if( keyIndex == '7' && gp.IsOneOfKeysPressed( keys, 2 ) ) { // German '/' key newChar[0] = '/'; } m_data += newChar; if( m_data.length() >= (m_maxDisplayableChars - 1) ) ++m_dataDrawStartPosition; break; } } } } if( gp.IsButtonPressed( GP_BACK ) || gp.IsKeyPressed( VK_DELETE ) ) m_inputState = MENU_CANCELLED; else if( gp.IsButtonPressed( GP_START ) || gp.IsKeyPressed( VK_RETURN ) ) m_inputState = MENU_ACCEPTED; }