diff --git a/README.md b/README.md index 117a95ea8..a2c21be7a 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ A "BIOS" ROM image is required to to start the emulator and to play games. You c ## Latest News Older entries are available at https://github.com/stenzek/duckstation/blob/master/NEWS.md -- 2020/01/10: Option to sync to host refresh rate added (enabled by default). This will give the smoothest animation possible with zero duped frames, at the cost of running the game <1% faster. Users with variable refresh rate (GSync/FreeSync) displays will want to disable the option. -- 2020/01/10: Audio resampling added when fast forwarding to fixed speeds. Instead of crackling audio, you'll now get pitch altered audio. -- 2020/01/03: Per game settings and game properties added to Android version. +- 2021/01/10: Option to sync to host refresh rate added (enabled by default). This will give the smoothest animation possible with zero duped frames, at the cost of running the game <1% faster. Users with variable refresh rate (GSync/FreeSync) displays will want to disable the option. +- 2021/01/10: Audio resampling added when fast forwarding to fixed speeds. Instead of crackling audio, you'll now get pitch altered audio. +- 2021/01/03: Per game settings and game properties added to Android version. - 2020/12/30: Box and Adaptive downsampling modes added. Adaptive downsampling will smooth 2D backgrounds but attempt to preserve 3D geometry via pixel similarity (only supported in D3D11/Vulkan). Box is a simple average filter which will downsample to native resolution. - 2020/12/30: Hotkey binding added to Android version. You can now bind hotkeys such as fast forward, save state, etc to controller buttons. The ability to bind multi-button combinations will be added in the future. - 2020/12/29: Controller mapping/binding added for Android version. By default mappings will be clear and you will have to set them, you can do this from `Settings -> Controllers -> Controller Mapping`. Profiles can be saved and loaded as well. diff --git a/android/app/src/cpp/android_host_interface.cpp b/android/app/src/cpp/android_host_interface.cpp index e9b6b3b9e..08547ce20 100644 --- a/android/app/src/cpp/android_host_interface.cpp +++ b/android/app/src/cpp/android_host_interface.cpp @@ -44,6 +44,7 @@ static jmethodID s_EmulationActivity_method_onEmulationStarted; static jmethodID s_EmulationActivity_method_onEmulationStopped; static jmethodID s_EmulationActivity_method_onGameTitleChanged; static jmethodID s_EmulationActivity_method_setVibration; +static jmethodID s_EmulationActivity_method_getRefreshRate; static jclass s_PatchCode_class; static jmethodID s_PatchCode_constructor; static jclass s_GameListEntry_class; @@ -223,6 +224,20 @@ std::unique_ptr AndroidHostInterface::OpenPackageFile(const char* pa return ret; } +bool AndroidHostInterface::GetMainDisplayRefreshRate(float* refresh_rate) +{ + if (!m_emulation_activity_object) + return false; + + float value = AndroidHelpers::GetJNIEnv()->CallFloatMethod(m_emulation_activity_object, + s_EmulationActivity_method_getRefreshRate); + if (value <= 0.0f) + return false; + + *refresh_rate = value; + return true; +} + void AndroidHostInterface::SetUserDirectory() { // Already set in constructor. @@ -881,6 +896,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) env->GetMethodID(s_EmulationActivity_class, "onGameTitleChanged", "(Ljava/lang/String;)V")) == nullptr || (s_EmulationActivity_method_setVibration = env->GetMethodID(emulation_activity_class, "setVibration", "(Z)V")) == nullptr || + (s_EmulationActivity_method_getRefreshRate = + env->GetMethodID(emulation_activity_class, "getRefreshRate", "()F")) == nullptr || (s_PatchCode_constructor = env->GetMethodID(s_PatchCode_class, "", "(ILjava/lang/String;Z)V")) == nullptr || (s_GameListEntry_constructor = env->GetMethodID( s_GameListEntry_class, "", diff --git a/android/app/src/cpp/android_host_interface.h b/android/app/src/cpp/android_host_interface.h index 5854a050e..5586c6415 100644 --- a/android/app/src/cpp/android_host_interface.h +++ b/android/app/src/cpp/android_host_interface.h @@ -38,6 +38,7 @@ public: int GetIntSettingValue(const char* section, const char* key, int default_value = 0) override; float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f) override; std::unique_ptr OpenPackageFile(const char* path, u32 flags) override; + bool GetMainDisplayRefreshRate(float* refresh_rate) override; bool IsEmulationThreadRunning() const { return m_emulation_thread_running.load(); } bool IsEmulationThreadPaused() const; diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/EmulationActivity.java b/android/app/src/main/java/com/github/stenzek/duckstation/EmulationActivity.java index d1d566dba..76c99b4d4 100644 --- a/android/app/src/main/java/com/github/stenzek/duckstation/EmulationActivity.java +++ b/android/app/src/main/java/com/github/stenzek/duckstation/EmulationActivity.java @@ -11,10 +11,12 @@ import android.os.Build; import android.os.Bundle; import android.os.Vibrator; import android.util.Log; +import android.view.Display; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.View; +import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.Toast; @@ -137,6 +139,21 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde }); } + public float getRefreshRate() { + WindowManager windowManager = getWindowManager(); + if (windowManager == null) { + windowManager = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)); + if (windowManager == null) + return -1.0f; + } + + Display display = windowManager.getDefaultDisplay(); + if (display == null) + return -1.0f; + + return display.getRefreshRate(); + } + private void doApplySettings() { AndroidHostInterface.getInstance().applySettings(); updateRequestedOrientation(); diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index e180f170e..4e9aae80a 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -191,4 +191,6 @@ Controller Settings Audio Resampling When running outside of 100% speed, resamples audio from the target speed instead of dropping frames. Produces much nicer fast forward/slowdown audio at a small cost to performance. + Sync To Host Refresh Rate + Adjusts the emulation speed so the console\'s refresh rate matches the host\'s refresh rate, when VSync and Audio Resampling is enabled. This results in the smoothest animations possible, at the cost of potentially increasing the emulation speed by less than 1%. diff --git a/android/app/src/main/res/xml/general_preferences.xml b/android/app/src/main/res/xml/general_preferences.xml index d589feec7..da9a051a7 100644 --- a/android/app/src/main/res/xml/general_preferences.xml +++ b/android/app/src/main/res/xml/general_preferences.xml @@ -78,6 +78,13 @@ app:defaultValue="false" app:summary="@string/settings_summary_video_sync" app:iconSpaceReserved="false" /> +