void vsyncarch_verticalblank(video_canvas_t *c, float rate, int frames) { unsigned long nowi, lastx, max, frm, vbl; if (c->refreshrate <= 0.0f) return; nowi = vsyncarch_frequency(); /* calculate counter cycles per frame */ frm = (unsigned long)((float)(nowi * frames) / rate); nowi = vsyncarch_gettime(); lastx = last - (frm * nosynccount); max = (frm * 7) >> 3; vbl = 0; while (max >= (nowi - lastx)) { IDirectDraw2_WaitForVerticalBlank(c->dd_object2, DDWAITVB_BLOCKBEGIN, 0); nowi = vsyncarch_gettime(); vbl = 1; } if ((!vbl) && (nosynccount < 16)) { nosynccount ++; } else { last = nowi; nosynccount = 0; } }
static void network_hook_connected_send(void) { BYTE *local_event_buf = NULL; unsigned int send_len; BYTE send_len4[4]; /* create and send current event buffer */ network_event_record(EVENT_LIST_END, NULL, 0); send_len = network_create_event_buffer(&local_event_buf, &(frame_event_list[current_frame])); #ifdef NETWORK_DEBUG t1 = vsyncarch_gettime(); #endif util_int_to_le_buf4(send_len4, (int)send_len); if (network_send_buffer(network_socket, send_len4, 4) < 0 || network_send_buffer(network_socket, local_event_buf, send_len) < 0) { ui_display_statustext(translate_text(IDGS_REMOTE_HOST_DISCONNECTED), 1); network_disconnect(); } #ifdef NETWORK_DEBUG t2 = vsyncarch_gettime(); #endif lib_free(local_event_buf); }
void vsyncarch_verticalblank(video_canvas_t *c, float rate, int frames) { unsigned long nowi, lastx, max, frm, vbl; if (c->refreshrate <= 0.0f) { return; } nowi = vsyncarch_frequency(); /* calculate counter cycles per frame */ frm = (unsigned long)((float)(nowi * frames) / rate); nowi = vsyncarch_gettime(); lastx = last - (frm * nosynccount); max = (frm * 7) >> 3; vbl = 0; while (max >= (nowi - lastx)) { vsyncarch_sync_with_raster(c); nowi = vsyncarch_gettime(); vbl = 1; } if ((!vbl) && (nosynccount < 16)) { nosynccount++; } else { last = nowi; nosynccount = 0; } }
static void network_test_delay(void) { int i, j; BYTE new_frame_delta; BYTE buf[0x60]; long packet_delay[NUM_OF_TESTPACKETS]; char st[256]; vsyncarch_init(); ui_display_statustext(translate_text(IDGS_TESTING_BEST_FRAME_DELAY), 0); if (network_mode == NETWORK_SERVER_CONNECTED) { for (i = 0; i < NUM_OF_TESTPACKETS; i++) { *((unsigned long*)buf) = vsyncarch_gettime(); if (network_send_buffer(network_socket, buf, sizeof(buf)) < 0 || network_recv_buffer(network_socket, buf, sizeof(buf)) < 0) return; packet_delay[i] = vsyncarch_gettime() - *((unsigned long*)buf); } /* Sort the packets delays*/ for (i = 0; i < NUM_OF_TESTPACKETS - 1; i++) { for (j = i + 1; j < NUM_OF_TESTPACKETS; j++) { if (packet_delay[i] < packet_delay[j]) { long d = packet_delay[i]; packet_delay[i] = packet_delay[j]; packet_delay[j] = d; } } #ifdef NETWORK_DEBUG log_debug("packet_delay[%d]=%ld",i,packet_delay[i]); #endif } #ifdef NETWORK_DEBUG log_debug("vsyncarch_frequency = %ld", vsyncarch_frequency()); #endif /* calculate delay with 90% of packets beeing fast enough */ /* FIXME: This needs some further investigation */ new_frame_delta = 5 + (BYTE)(vsync_get_refresh_frequency() * packet_delay[(int)(0.1 * NUM_OF_TESTPACKETS)] / (float)vsyncarch_frequency()); network_send_buffer(network_socket, &new_frame_delta, sizeof(new_frame_delta)); } else { /* network_mode == NETWORK_CLIENT */ for (i = 0; i < NUM_OF_TESTPACKETS; i++) { if (network_recv_buffer(network_socket, buf, sizeof(buf)) < 0 || network_send_buffer(network_socket, buf, sizeof(buf)) < 0) return; } network_recv_buffer(network_socket, &new_frame_delta, sizeof(new_frame_delta)); } network_free_frame_event_list(); frame_delta = new_frame_delta; network_init_frame_event_list(); sprintf(st, translate_text(IDGS_USING_D_FRAMES_DELAY), frame_delta); log_debug("netplay connected with %d frames delta.", frame_delta); ui_display_statustext(st, 1); }
void vsyncarch_sleep(signed long delay) { unsigned long start, now; if (delay <= vsyncarch_frequency() / 1000) return; start = vsyncarch_gettime(); do { Sleep(1); now = vsyncarch_gettime(); } while (((signed long)(now - start)) < delay); }
static void archdep_create_mutex_sem(HMTX *hmtx, const char *pszName, int fState) { APIRET rc; char *sem = lib_malloc(13+strlen(pszName) + 5 + 1); sprintf(sem, "\\SEM32\\VICE2\\%s_%04x", pszName, vsyncarch_gettime()&0xffff); rc = DosCreateMutexSem(sem, hmtx, 0, fState); }
/* ------------------------------------------------------------------------ */ void archdep_create_mutex_sem(HMTX *hmtx, const char *pszName, int fState) { APIRET rc; char *sem = lib_malloc(13+strlen(pszName) + 5 + 1); sprintf(sem, "\\SEM32\\VICE2\\%s_%04x", pszName, vsyncarch_gettime()&0xffff); if (rc = DosCreateMutexSem(sem, hmtx, 0, fState)) { log_error(archlog, "DosCreateMutexSem '%s' (rc=%i)", pszName, rc); } }
void vsyncarch_prepare_vbl(void) { /* keep vertical blank data prepared */ last = vsyncarch_gettime(); nosynccount = 0; }
/* This is called at the end of each screen frame. It flushes the audio buffer and keeps control of the emulation speed. */ int vsync_do_vsync(struct video_canvas_s *c, int been_skipped) { static unsigned long next_frame_start = 0; unsigned long network_hook_time = 0; /* * these are the counters to show how many frames are skipped * since the last vsync_display_speed */ static int frame_counter = 0; static int skipped_frames = 0; /* * This are the frames which are skipped in a row */ static int skipped_redraw = 0; /* Adjustment of frame output frequency. */ static unsigned long adjust_start; static int frames_adjust; static signed long avg_sdelay, prev_sdelay; double sound_delay; int skip_next_frame; signed long delay; long frame_ticks_remainder, frame_ticks_integer, compval; #if (defined(HAVE_OPENGL_SYNC)) && !defined(USE_SDLUI) float refresh_cmp; int refresh_div; #endif #ifdef HAVE_NETWORK /* check if someone wants to connect remotely to the monitor */ monitor_check_remote(); #endif vsync_frame_counter++; /* * process everything wich should be done before the synchronisation * e.g. OS/2: exit the programm if trigger_shutdown set */ vsyncarch_presync(); /* Run vsync jobs. */ if (network_connected()) { network_hook_time = vsyncarch_gettime(); } vsync_hook(); if (network_connected()) { network_hook_time = vsyncarch_gettime() - network_hook_time; if (network_hook_time > (unsigned long)frame_ticks) { next_frame_start += network_hook_time; now += network_hook_time; } } #ifdef DEBUG /* switch between recording and playback in history debug mode */ debug_check_autoplay_mode(); #endif /* * Update display every two second (pc system time) * This has some reasons: * - we have a better statistic in case of a fastly running emulator * - we don't slow down fast emulations by updating this value * too often (eg more then 10 times a second) * - I don't want to have a value jumping around for example * between 99% and 101% if the user chooses 100% (s.above) * - We need some statistict to get an avarage number for the * frame-rate without staticstics it would also jump around */ frame_counter++; if (!speed_eval_suspended && (signed long)(now - display_start) >= 2 * vsyncarch_freq) { display_speed(frame_counter - skipped_frames); display_start = now; frame_counter = 0; skipped_frames = 0; } if (been_skipped) { skipped_frames++; } /* Flush sound buffer, get delay in seconds. */ sound_delay = sound_flush(); /* Get current time, directly after getting the sound delay. */ now = vsyncarch_gettime(); /* Start afresh after pause in frame output. */ if (speed_eval_suspended) { speed_eval_suspended = 0; speed_eval_prev_clk = maincpu_clk; display_start = now; frame_counter = 0; skipped_frames = 0; next_frame_start = now; skipped_redraw = 0; } /* Start afresh after "out of sync" cases. */ if (sync_reset) { sync_reset = 0; adjust_start = now; frames_adjust = 0; avg_sdelay = 0; prev_sdelay = 0; frame_ticks = (frame_ticks_orig + frame_ticks) / 2; } /* This is the time between the start of the next frame and now. */ delay = (signed long)(now - next_frame_start); #if (defined(HAVE_OPENGL_SYNC)) && !defined(USE_SDLUI) refresh_cmp = (float)(c->refreshrate / refresh_frequency); refresh_div = (int)(refresh_cmp + 0.5f); refresh_cmp /= (float)refresh_div; if ((timer_speed == 100) && (!warp_mode_enabled) && vsyncarch_vbl_sync_enabled() && (refresh_cmp <= 1.02f) && (refresh_cmp > 0.98f) && (refresh_div == 1)) { vsyncarch_verticalblank(c, c->refreshrate, refresh_div); skip_next_frame = 0; skipped_redraw = 0; } else { #endif /* * We sleep until the start of the next frame, if: * - warp_mode is disabled * - a limiting speed is given * - we have not reached next_frame_start yet * * We could optimize by sleeping only if a frame is to be output. */ /*log_debug("vsync_do_vsync: sound_delay=%f frame_ticks=%d delay=%d", sound_delay, frame_ticks, delay);*/ if (!warp_mode_enabled && timer_speed && delay < 0) { vsyncarch_sleep(-delay); } #if (defined(HAVE_OPENGL_SYNC)) && !defined(USE_SDLUI) vsyncarch_prepare_vbl(); #endif /* * Check whether we should skip the next frame or not. * Allow delay of up to one frame before skipping frames. * Frames are skipped: * - only if maximum skipped frames are not reached * - if warp_mode enabled * - if speed is not limited or we are too slow and * refresh rate is automatic or fixed and needs correction * * Remark: The time_deviation should be the equivalent of two * frames and must be scaled to make sure, that we * don't start skipping frames before the CPU reaches 100%. * If we are becoming faster a small deviation because of * threading results in a frame rate correction suddenly. */ frame_ticks_remainder = frame_ticks % 100; frame_ticks_integer = frame_ticks / 100; compval = frame_ticks_integer * 3 * timer_speed + frame_ticks_remainder * 3 * timer_speed / 100; if (skipped_redraw < MAX_SKIPPED_FRAMES && (warp_mode_enabled || (skipped_redraw < refresh_rate - 1) || ((!timer_speed || delay > compval) && !refresh_rate ) ) ) { skip_next_frame = 1; skipped_redraw++; } else { skip_next_frame = 0; skipped_redraw = 0; } #if (defined(HAVE_OPENGL_SYNC)) && !defined(USE_SDLUI) } #endif /* * Check whether the hardware can keep up. * Allow up to 0,25 second error before forcing a correction. */ if ((signed long)(now - next_frame_start) >= vsyncarch_freq / 8) { #if !defined(__OS2__) && !defined(DEBUG) if (!warp_mode_enabled && relative_speed) { log_warning(LOG_DEFAULT, "Your machine is too slow for current settings!"); } #endif vsync_sync_reset(); next_frame_start = now; } /* Adjust frame output frequency to match sound speed. This only kicks in for cycle based sound and SOUND_ADJUST_EXACT. */ if (frames_adjust < INT_MAX) { frames_adjust++; } /* Adjust audio-video sync */ if (!network_connected() && (signed long)(now - adjust_start) >= vsyncarch_freq / 5) { signed long adjust; avg_sdelay /= frames_adjust; /* Account for both relative and absolute delay. */ adjust = (avg_sdelay - prev_sdelay + avg_sdelay / 8) / frames_adjust; /* Maximum adjustment step 1%. */ if (labs(adjust) > frame_ticks / 100) { adjust = adjust / labs(adjust) * frame_ticks / 100; } frame_ticks -= adjust; frames_adjust = 0; prev_sdelay = avg_sdelay; avg_sdelay = 0; adjust_start = now; } else { /* Actual sound delay is sound delay minus vsync delay. */ signed long sdelay = (signed long)(sound_delay * vsyncarch_freq); avg_sdelay += sdelay; } next_frame_start += frame_ticks; vsyncarch_postsync(); #if 0 FILE *fd = fopen("latencylog.txt", "a"); fprintf(fd, "%d %ld %ld %lf\n", vsync_frame_counter, frame_ticks, delay, sound_delay * 1000000); fclose(fd); #endif return skip_next_frame; }
void mouse_move(int x, int y) { mouse_x += x * mouse_accelx; mouse_y -= y * mouse_accely; mouse_timestamp = vsyncarch_gettime(); }
void mouse_button(HWND hwnd, ULONG msg, MPARAM mp1) { if (!_mouse_enabled) { return; } switch (msg) { case WM_MOUSEMOVE: _mouse_x = SHORT1FROMMP(mp1); _mouse_y = SHORT2FROMMP(mp1); mouse_timestamp = vsyncarch_gettime(); { SWP swp; WinQueryWindowPos(hwnd, &swp); // // check whether the pointer is outside or inside the window // if (FullscreenIsNow()) { visible = TRUE; } if (_mouse_x >= 0 && _mouse_x < swp.cx && _mouse_y >= 0 && _mouse_y < swp.cy) { // // FIXME: Don't capture the mouse pointer if it is in front // of a client dialog! // if (WinQueryCapture(HWND_DESKTOP)!= hwnd && hide_mouseptr && !FullscreenIsNow()) { WinSetCapture(HWND_DESKTOP, hwnd); } if (visible && hide_mouseptr && !FullscreenIsNow()) { WinShowPointer(HWND_DESKTOP, FALSE); visible = FALSE; } } else { if (WinQueryCapture(HWND_DESKTOP) == hwnd && !FullscreenIsNow()) { WinSetCapture(HWND_DESKTOP, NULLHANDLE); } if (!visible && !FullscreenIsNow()) { WinShowPointer(HWND_DESKTOP, TRUE); visible = TRUE; } // // don't use 'outside'-values which appears one times // if the mouse pointer leaves the window // if (_mouse_x < 0) { _mouse_x = 0; } else { if (_mouse_x >= swp.cx) { _mouse_x = swp.cx - 1; } } if (_mouse_y < 0) { _mouse_y = 0; } else { if (_mouse_y >= swp.cy) { _mouse_y = swp.cy - 1; } } } } return; case WM_BUTTON1DOWN: mouse_button_left(1); return; case WM_BUTTON1UP: mouse_button_left(0); return; case WM_BUTTON2DOWN: mouse_button_right(1); return; case WM_BUTTON2UP: mouse_button_right(0); return; case WM_BUTTON3DOWN: mouse_button_middle(1); return; case WM_BUTTON3UP: mouse_button_middle(0); return; } }
static void network_hook_connected_receive(void) { BYTE *remote_event_buf = NULL; unsigned int recv_len; BYTE recv_len4[4]; event_list_state_t *remote_event_list; event_list_state_t *client_event_list, *server_event_list; suspended = 0; if (current_frame == frame_delta - 1) frame_buffer_full = 1; if (frame_buffer_full) { do { if (network_recv_buffer(network_socket, recv_len4, 4) < 0) { ui_display_statustext(translate_text(IDGS_REMOTE_HOST_DISCONNECTED), 1); network_disconnect(); return; } recv_len = util_le_buf4_to_int(recv_len4); if (recv_len == 0 && suspended == 0) { /* remote host suspended emulation */ ui_display_statustext(translate_text(IDGS_REMOTE_HOST_SUSPENDING), 0); suspended = 1; vsync_suspend_speed_eval(); } } while (recv_len == 0); if (suspended == 1) ui_display_statustext("", 0); remote_event_buf = lib_malloc(recv_len); if (network_recv_buffer(network_socket, remote_event_buf, recv_len) < 0) { lib_free(remote_event_buf); return; } #ifdef NETWORK_DEBUG t3 = vsyncarch_gettime(); #endif remote_event_list = network_create_event_list(remote_event_buf); lib_free(remote_event_buf); if (network_mode == NETWORK_SERVER_CONNECTED) { client_event_list = remote_event_list; server_event_list = &(frame_event_list[frame_to_play]); } else { server_event_list = remote_event_list; client_event_list = &(frame_event_list[frame_to_play]); } /* test for sync */ if (client_event_list->base->type == EVENT_SYNC_TEST && server_event_list->base->type == EVENT_SYNC_TEST) { int i; for (i = 0; i < 5; i++) { if (((DWORD *)client_event_list->base->data)[i] != ((DWORD *)server_event_list->base->data)[i]) { ui_error(translate_text(IDGS_NETWORK_OUT_OF_SYNC)); network_disconnect(); /* shouldn't happen but resyncing would be nicer */ break; } } } /* replay the event_lists; server first, then client */ event_playback_event_list(server_event_list); event_playback_event_list(client_event_list); event_clear_list(remote_event_list); lib_free(remote_event_list); } network_prepare_next_frame(); #ifdef NETWORK_DEBUG t4 = vsyncarch_gettime(); #endif }