static int wait_for_frame_no_netplay(void) { #if 0 while (1) { fs_ml_usleep(100 * 1000); } #endif if (g_fs_emu_benchmarking) { return 1; } #ifdef FSE_DRIVERS if (g_fs_emu_benchmark_mode) { return 1; } #endif if (!g_fs_emu_throttling) { return 1; } //fs_log("wait_for_frame_no_netplay\n"); static int64_t last_time = 0; static int64_t frame_time = 0; if (last_time == 0) { last_time = fs_emu_monotonic_time(); } static double last_frame_rate = 0.0; double frame_rate = fs_emu_get_video_frame_rate(); if (frame_rate != last_frame_rate) { frame_time = ((int64_t) 1000000) / frame_rate; fs_log("wait_for_frame_no_netplay: new frame rate %0.2f (time: %d)\n", frame_rate, (int) frame_time); last_frame_rate = frame_rate; } int64_t wait_until = last_time + frame_time; //int64_t sleep_until = wait_until; int64_t sleep_until = wait_until - 100; int64_t t = fs_emu_monotonic_time(); //fs_log("%lld %lld\n", sleep_until, t); while (t < sleep_until) { int64_t sleep_time = sleep_until - t; //fs_log("%lld %lld %lld\n", sleep_until, t, sleep_time); fs_ml_usleep(sleep_time); t = fs_emu_monotonic_time(); } while (t < wait_until) { t = fs_emu_monotonic_time(); } last_time = last_time + frame_time; if (fs_emu_monotonic_time() > last_time + frame_time) { // time has elapsed too far, probably due to pause function having // been used last_time = fs_emu_monotonic_time(); } return 1; }
static void opengl_fence(int command) { #ifdef USE_GLES #else if (command == FENCE_SET) { if (g_has_nv_fence) { //printf("...\n"); glSetFenceNV(g_fence, GL_ALL_COMPLETED_NV); CHECK_GL_ERROR_MSG("glSetFenceNV(g_fence, GL_ALL_COMPLETED_NV)"); } else if (g_has_apple_fence) { glSetFenceAPPLE(g_fence); CHECK_GL_ERROR_MSG("glSetFenceAPPLE(g_fence)"); } else if (g_has_arb_sync) { g_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); CHECK_GL_ERROR_MSG("glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)"); } } else if (command == FENCE_WAIT) { if (g_has_nv_fence) { //printf("-- f --\n"); //glFinishFenceNV(g_fence); //int64_t t1 = fs_get_monotonic_time(); //fs_ml_usleep(10000); while (!glTestFenceNV(g_fence)) { CHECK_GL_ERROR_MSG("glTestFenceNV(g_fence)"); //printf("-> %lld\n", fs_get_monotonic_time() - t1); //printf("%d\n", glGetError()); fs_ml_usleep(1000); //printf("-> %lld\n", fs_get_monotonic_time() - t1); } CHECK_GL_ERROR_MSG("glTestFenceNV(g_fence)"); } else if (g_has_apple_fence) { while (!glTestFenceAPPLE(g_fence)) { CHECK_GL_ERROR_MSG("glTestFenceAPPLE(g_fence)"); fs_ml_usleep(1000); } CHECK_GL_ERROR_MSG("glTestFenceAPPLE(g_fence)"); } else if (g_has_arb_sync) { int flags = GL_SYNC_FLUSH_COMMANDS_BIT; while (glClientWaitSync(g_sync, flags, 0) == GL_TIMEOUT_EXPIRED) { CHECK_GL_ERROR_MSG("glClientWaitSync(g_sync, flags, 0)"); flags = 0; fs_ml_usleep(1000); } CHECK_GL_ERROR_MSG("glClientWaitSync(g_sync, flags, 0)"); } } #endif }
static void *force_quit_thread(void *data) { for (int i = 0; i < 10; i++) { fs_ml_usleep(1000 * 1000); fs_log("force_quit_thread: %d seconds passed\n", i + 1); } fs_log("force_quit_thread: should force quit (FIXME: not implemented)\n"); printf("force_quit_thread: should force quit (FIXME: not implemented)\n"); return NULL; }
static void full_sleep_until_vsync() { // FIXME: use this instead of sleep_until_vsync int sleep_time = 0; int time_left = 3000; int64_t t = fs_emu_monotonic_time(); if (g_fs_ml_target_frame_time > 0) { sleep_time = g_sleep_until_vsync_last_time + g_fs_ml_target_frame_time - t - time_left; } if (sleep_time > g_fs_ml_target_frame_time - time_left) { sleep_time = 0; } if (sleep_time > 0) { fs_ml_usleep(sleep_time); } }
static void sleep_until_vsync() { int sleep_time = 5000; int64_t t = fs_emu_monotonic_time(); //int64_t sleep_until = 0; if (g_fs_ml_target_frame_time > 0) { // calculate sleep time based on frame rate // (allowing 4 ms busy-waiting by OpenGL driver, if necessary) //sleep_time = g_fs_ml_target_frame_time - 4000; sleep_time = g_sleep_until_vsync_last_time + g_fs_ml_target_frame_time - t - 5000; } if (sleep_time > g_fs_ml_target_frame_time - 4000) { sleep_time = 0; } if (sleep_time > 0) { //printf("sleep %d\n", sleep_time); fs_ml_usleep(sleep_time); } }
static void init_manymouse(void) { /* On OS X with HIDManager driver at least, the mice must be polled from * the same thread as the one which called ManyMouse_Init, so we do * everything (also enumeration in a worker thread) and wait for * enumeration to complete.*/ fs_log("[MANYMOUSE] Copyright (c) 2005-2012 Ryan C. Gordon\n"); g_manymouse_thread = fs_thread_create("manymouse", manymouse_thread, NULL); if (g_manymouse_thread == NULL) { fs_log("[MANYMOUSE] Error - could not create ManyMouse thread\n"); // ManyMouse_Quit(); } else { while (g_manymouse_last_index < 0) { fs_ml_usleep(1000); } g_fs_ml_input_device_count = g_manymouse_last_index; } }
void fs_ml_render_iteration() { static int first = 1; if (first) { first = 0; initialize_opengl_sync(); } if (g_fs_ml_vblank_sync) { render_iteration_vsync(); } else if (g_fs_ml_benchmarking) { update_frame(); render_frame(); swap_opengl_buffers(); } else { // when vsync is off, we wait until a new frame is ready and // then we display it immediately if (fs_ml_is_quitting()) { // but when the emulation is quitting, we can't expect any new // frames so there's no point waiting for them. Instead, we just // sleep a bit to throttle the frame rate for the quit animation fs_ml_usleep(10000); } else { // wait max 33 ms to allow the user interface to work even if // the emu hangs // int64_t dest_time = fs_get_real_time() + 33 * 1000; int64_t end_time = fs_condition_get_wait_end_time(33 * 1000); int64_t check_time = 0; fs_mutex_lock(g_frame_available_mutex); // fs_log("cond wait until %lld\n", end_time); while (g_rendered_frame == g_available_frame) { fs_condition_wait_until( g_frame_available_cond, g_frame_available_mutex, end_time); check_time = fs_condition_get_wait_end_time(0); if (check_time >= end_time) { // fs_log("timed out at %lld\n", check_time); break; } else { // fs_log("wake-up at %lld (end_time = %lld)\n", check_time, end_time); } } fs_mutex_unlock(g_frame_available_mutex); } update_frame(); render_frame(); swap_opengl_buffers(); //gl_finish(); } if (g_fs_ml_video_screenshot_path) { fs_mutex_lock(g_fs_ml_video_screenshot_mutex); if (g_fs_ml_video_screenshot_path) { save_screenshot_of_opengl_framebuffer( g_fs_ml_video_screenshot_path); g_free(g_fs_ml_video_screenshot_path); g_fs_ml_video_screenshot_path = NULL; } fs_mutex_unlock(g_fs_ml_video_screenshot_mutex); } if (g_fs_ml_video_post_render_function) { g_fs_ml_video_post_render_function(); } }
static void render_iteration_vsync() { if (g_fs_ml_video_sync_low_latency) { int current_frame_at_start = g_available_frame; //int64_t t1 = fs_ml_monotonic_time(); int sleep_time = 0; int time_left = g_estimated_upload_render_duration; int64_t t = fs_emu_monotonic_time(); if (g_fs_ml_target_frame_time > 0) { sleep_time = g_estimated_next_vblank_time - t - time_left; } if (sleep_time > g_fs_ml_target_frame_time - time_left) { sleep_time = 0; } if (sleep_time > 0) { fs_ml_usleep(sleep_time); } if (g_available_frame > current_frame_at_start) { //printf("low latency %d\n", g_available_frame); } else { //printf("...\n"); } } update_frame(); CHECK_GL_ERROR_MSG("update_frame"); render_frame(); CHECK_GL_ERROR_MSG("render_frame"); //opengl_fence(FENCE_SET); //glFlush(); //opengl_fence(FENCE_WAIT); //int64_t upload_render_time = fs_ml_monotonic_time() - t1; //printf("urt %lld\n", upload_render_time); opengl_swap_synchronous(); g_measured_vblank_time = fs_ml_monotonic_time(); g_vblank_count++; fs_mutex_lock(g_vblank_mutex); g_measured_vblank_times[g_vblank_index] = g_measured_vblank_time; g_vblank_index = (g_vblank_index + 1) % VBLANK_COUNT; fs_mutex_unlock(g_vblank_mutex); // FIXME: adjust g_measured_vblank_time based on historical data (smooth out // irregularities) and save the result in g_adjusted_vblank_time g_adjusted_vblank_time = g_measured_vblank_time; g_sleep_until_vsync_last_time = g_adjusted_vblank_time; g_estimated_next_vblank_time = g_adjusted_vblank_time + \ g_fs_ml_target_frame_time; // g_start_new_frame_cond is used to signal that a new frame can be // generated when the emulation is running in sync - this is not used // when only display flipping is synced to vblank fs_mutex_lock(g_start_new_frame_mutex); g_start_new_frame = 1; fs_condition_signal(g_start_new_frame_cond); fs_mutex_unlock(g_start_new_frame_mutex); }
void fs_emu_msleep(int msec) { fs_ml_usleep(msec * 1000); }
static void *manymouse_thread(void* data) { fs_log("[MANYMOUSE] Thread running\n"); int k = g_fs_ml_input_device_count; g_first_manymouse_index = k; int mouse_count = ManyMouse_Init(); if (mouse_count < 0) { fs_log("[MANYMOUSE] Initialization failed (%d)\n", mouse_count); } else if (mouse_count == 0) { fs_log("MANYMOUSE: no mice found\n"); // no mice found, so we just quit using the library } for (int i = 0; i < mouse_count; i++) { const char *device = ManyMouse_DeviceName(i); const char *driver = ManyMouse_DriverName(); char *name; if (device[0] == 0 || g_ascii_strcasecmp(device, "mouse") == 0) { name = g_strdup("Mouse: Unnamed Mouse"); } else { name = g_strdup_printf("Mouse: %s", device); } // fs_ml_input_unique_device_name either returns name, or frees it // and return another name, so name must be malloced and owned by // caller name = fs_ml_input_unique_device_name(name); fs_log("MANYMOUSE: Adding %s (%s)\n", name, driver); g_fs_ml_input_devices[k].type = FS_ML_MOUSE; g_fs_ml_input_devices[k].index = k; g_fs_ml_input_devices[k].name = name; g_fs_ml_input_devices[k].alias = g_strdup_printf("MOUSE #%d", i + 2); k += 1; } // when done like this, I believe no memory barrier is needed when the // other thread polls g_manymouse_last_index g_manymouse_last_index = k; if (mouse_count < 0) { // ManyMouse library was not initialized return NULL; } ManyMouseEvent event; fs_ml_event *new_event; while (!fs_ml_is_quitting()) { // printf("..\n"); while (ManyMouse_PollEvent(&event)) { // printf(" -- event type %d -- \n", event.type); if (event.type == MANYMOUSE_EVENT_RELMOTION) { // printf("MANYMOUSE_EVENT_RELMOTION\n"); new_event = fs_ml_alloc_event(); new_event->type = FS_ML_MOUSEMOTION; new_event->motion.device = g_first_manymouse_index + \ event.device; if (event.item == 0) { new_event->motion.xrel = event.value; new_event->motion.yrel = 0; } else if (event.item == 1) { new_event->motion.xrel = 0; new_event->motion.yrel = event.value; } new_event->motion.x = FS_ML_NO_ABSOLUTE_MOUSE_POS; new_event->motion.y = FS_ML_NO_ABSOLUTE_MOUSE_POS; fs_ml_post_event(new_event); // ManyMouseEventType type; // unsigned int device; // unsigned int item; // int value; // int minval; // int maxval; } else if (event.type == MANYMOUSE_EVENT_BUTTON) { db_log(input, "MANYMOUSE: EVENT_BUTTON " "device %d item %d value %d\n", event.device, event.item, event.value); new_event = fs_ml_alloc_event(); new_event->type = event.value ? FS_ML_MOUSEBUTTONDOWN : FS_ML_MOUSEBUTTONUP; new_event->button.state = event.value != 0; new_event->button.device = g_first_manymouse_index + \ event.device; if (event.item == 0) { new_event->button.button = FS_ML_BUTTON_LEFT; } else if (event.item == 1) { new_event->button.button = FS_ML_BUTTON_RIGHT; } else if (event.item == 2) { new_event->button.button = FS_ML_BUTTON_MIDDLE; } else { new_event->button.button = 0; } fs_ml_post_event(new_event); } else if (event.type == MANYMOUSE_EVENT_ABSMOTION) { // printf("MANYMOUSE_EVENT_ABSMOTION\n"); } else if (event.type == MANYMOUSE_EVENT_SCROLL) { db_log(input, "MANYMOUSE: EVENT_SCROLL " "device %d item %d value %d\n", event.device, event.item, event.value); new_event = fs_ml_alloc_event(); new_event->type = FS_ML_MOUSEBUTTONDOWN; new_event->button.state = 1; new_event->button.device = g_first_manymouse_index + \ event.device; new_event->button.button = 0; if (event.item == 0) { if (event.value == 1) { new_event->button.button = FS_ML_BUTTON_WHEELUP; } else { new_event->button.button = FS_ML_BUTTON_WHEELDOWN; } } fs_ml_post_event(new_event); } } fs_ml_usleep(1000); } ManyMouse_Quit(); return NULL; }
static void *force_quit_thread(void *data) { for (int i = 0; i < 5; i++) { fs_ml_usleep(1000 * 1000); } return NULL; }