mirror of
https://github.com/Ishan09811/pine.git
synced 2025-04-24 08:55:10 +00:00
Implement speed limit option (#51)
This commit is contained in:
parent
0a710eaf10
commit
5302dad837
@ -31,6 +31,8 @@ namespace skyline {
|
||||
}
|
||||
|
||||
void Update() override {
|
||||
enableSpeedLimit = ktSettings.GetBool("enableSpeedLimit");
|
||||
speedLimit = ktSettings.GetFloat("speedLimit");
|
||||
isDocked = ktSettings.GetBool("isDocked");
|
||||
usernameValue = std::move(ktSettings.GetString("usernameValue"));
|
||||
profilePictureValue = ktSettings.GetString("profilePictureValue");
|
||||
|
@ -61,6 +61,8 @@ namespace skyline {
|
||||
|
||||
public:
|
||||
// System
|
||||
Setting<bool> enableSpeedLimit;
|
||||
Setting<float> speedLimit;
|
||||
Setting<bool> isDocked; //!< If the emulated Switch should be handheld or docked
|
||||
Setting<std::string> usernameValue; //!< The user name to be supplied to the guest
|
||||
Setting<std::string> profilePictureValue; //!< The profile picture path to be supplied to the guest
|
||||
|
@ -238,6 +238,7 @@ namespace skyline::gpu {
|
||||
} else {
|
||||
frameTimestamp = timestamp;
|
||||
}
|
||||
if (*state.settings->enableSpeedLimit) LimitSpeed(constant::NsInSecond / 60);
|
||||
}
|
||||
|
||||
void PresentationEngine::PresentationThread() {
|
||||
@ -448,6 +449,23 @@ namespace skyline::gpu {
|
||||
return nextFrameId++;
|
||||
}
|
||||
|
||||
void PresentationEngine::LimitSpeed(i64 targetFrameTimeNs) {
|
||||
static i64 lastFrameTime = 0;
|
||||
i64 currentTime = util::GetTimeNs();
|
||||
|
||||
i64 adjustedFrameTimeNs = static_cast<i64>(targetFrameTimeNs / (*state.settings->speedLimit / 100.0f));
|
||||
|
||||
if (lastFrameTime != 0) {
|
||||
i64 elapsedTime = currentTime - lastFrameTime;
|
||||
if (elapsedTime < adjustedFrameTimeNs) {
|
||||
// Sleep for the remaining time to meet the adjusted frame time
|
||||
std::this_thread::sleep_for(std::chrono::nanoseconds(adjustedFrameTimeNs - elapsedTime));
|
||||
}
|
||||
}
|
||||
|
||||
lastFrameTime = util::GetTimeNs(); // Update last frame time
|
||||
}
|
||||
|
||||
void PresentationEngine::Pause() {
|
||||
paused.store(true, std::memory_order_release);
|
||||
LOGI("PresentationEngine paused.");
|
||||
|
@ -105,6 +105,8 @@ namespace skyline::gpu {
|
||||
*/
|
||||
void UpdateSwapchain(texture::Format format, texture::Dimensions extent);
|
||||
|
||||
void LimitSpeed(i64 targetFrameTimeNs);
|
||||
|
||||
public:
|
||||
PresentationEngine(const DeviceState &state, GPU &gpu);
|
||||
|
||||
|
@ -68,6 +68,10 @@ namespace skyline {
|
||||
JniString GetString(const std::string_view &key) {
|
||||
return {env, static_cast<jstring>(env->GetObjectField(settingsInstance, env->GetFieldID(settingsClass, key.data(), "Ljava/lang/String;")))};
|
||||
}
|
||||
|
||||
float GetFloat(const std::string_view &key) {
|
||||
return static_cast<float>(env->GetFloatField(settingsInstance, env->GetFieldID(settingsClass, key.data(), "F")));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,9 @@
|
||||
package emu.skyline.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.preference.DialogPreference
|
||||
@ -35,13 +37,17 @@ class SeekBarPreference(context: Context, attrs: AttributeSet) : DialogPreferenc
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetDefaultValue(a: TypedArray, index: Int): Any {
|
||||
return a.getInt(index, 0)
|
||||
}
|
||||
|
||||
override fun onClick() { showMaterialDialog() }
|
||||
|
||||
private fun showMaterialDialog() {
|
||||
val dialogView = LayoutInflater.from(context).inflate(R.layout.preference_dialog_seekbar, null)
|
||||
val slider = dialogView.findViewById<Slider>(R.id.seekBar)
|
||||
val valueText = dialogView.findViewById<MaterialTextView>(R.id.value)
|
||||
|
||||
|
||||
// Configure slider
|
||||
slider.valueFrom = if (isPercentage) minValue.toFloat() else minValue.toInt().toFloat()
|
||||
slider.valueTo = if (isPercentage) maxValue.toFloat() else maxValue.toInt().toFloat()
|
||||
@ -56,11 +62,14 @@ class SeekBarPreference(context: Context, attrs: AttributeSet) : DialogPreferenc
|
||||
currentValue = if (isPercentage) value else value.toInt()
|
||||
}
|
||||
|
||||
var dismissTrigger: String? = null
|
||||
|
||||
// Build and show the MaterialAlertDialog
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(title)
|
||||
.setView(dialogView)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
dismissTrigger = "positive_button"
|
||||
if (isPercentage) {
|
||||
persistFloat(currentValue.toFloat())
|
||||
} else {
|
||||
@ -69,8 +78,10 @@ class SeekBarPreference(context: Context, attrs: AttributeSet) : DialogPreferenc
|
||||
updateSummary()
|
||||
callChangeListener(currentValue)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
slider.value = summary.toString().replace("%", "").toInt().toFloat()
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setOnDismissListener {
|
||||
if (dismissTrigger != "positive_button")
|
||||
slider.value = summary?.toString()?.replace("%", "")?.toIntOrNull()?.toFloat() ?: minValue.toFloat()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
@ -92,11 +103,21 @@ class SeekBarPreference(context: Context, attrs: AttributeSet) : DialogPreferenc
|
||||
}
|
||||
|
||||
override fun onSetInitialValue(defaultValue: Any?) {
|
||||
currentValue = if (isPercentage) {
|
||||
getPersistedFloat((defaultValue as? Float) ?: minValue.toFloat()).toFloat()
|
||||
} else {
|
||||
getPersistedInt((defaultValue as? Int) ?: minValue.toInt())
|
||||
val actualDefaultValue = when (defaultValue) {
|
||||
is String -> defaultValue.toIntOrNull() ?: minValue.toInt()
|
||||
is Int -> defaultValue ?: minValue.toInt()
|
||||
is Float -> defaultValue.toInt()
|
||||
else -> minValue.toInt() // fallback to minValue if default is invalid
|
||||
}
|
||||
currentValue = if (!isPercentage) getPersistedInt(actualDefaultValue!!)!! else getPersistedFloat(actualDefaultValue.toFloat()!!).toInt()!!
|
||||
updateSummary()
|
||||
}
|
||||
|
||||
fun setMaxValue(max: Any) {
|
||||
if (isPercentage) maxValue = max as Float else maxValue = max as Int
|
||||
}
|
||||
|
||||
fun setMinValue(min: Any) {
|
||||
if (isPercentage) minValue = min as Float else minValue = min as Int
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ class EmulationSettings private constructor(context : Context, prefName : String
|
||||
var useCustomSettings by sharedPreferences(context, false, prefName = prefName)
|
||||
|
||||
// System
|
||||
var enableSpeedLimit by sharedPreferences(context, false, prefName = prefName)
|
||||
var speedLimit by sharedPreferences(context, 100f, prefName = prefName)
|
||||
var isDocked by sharedPreferences(context, true, prefName = prefName)
|
||||
var usernameValue by sharedPreferences(context, context.getString(R.string.username_default), prefName = prefName)
|
||||
var profilePictureValue by sharedPreferences(context, "", prefName = prefName)
|
||||
|
@ -50,20 +50,23 @@ class GameSettingsFragment : PreferenceFragmentCompat() {
|
||||
findPreference("category_debug")
|
||||
).forEach { it?.dependency = "use_custom_settings" }
|
||||
|
||||
findPreference<Preference>("enable_speed_limit")?.setOnPreferenceChangeListener { _, newValue ->
|
||||
disablePreference("speed_limit", !(newValue as Boolean), null)
|
||||
true
|
||||
}
|
||||
|
||||
// Only show validation layer setting in debug builds
|
||||
@Suppress("SENSELESS_COMPARISON")
|
||||
if (BuildConfig.BUILD_TYPE != "release")
|
||||
findPreference<Preference>("validation_layer")?.isVisible = true
|
||||
|
||||
if (!GpuDriverHelper.supportsForceMaxGpuClocks()) {
|
||||
val forceMaxGpuClocksPref = findPreference<TwoStatePreference>("force_max_gpu_clocks")!!
|
||||
forceMaxGpuClocksPref.isSelectable = false
|
||||
forceMaxGpuClocksPref.isChecked = false
|
||||
forceMaxGpuClocksPref.summary = context!!.getString(R.string.force_max_gpu_clocks_desc_unsupported)
|
||||
}
|
||||
|
||||
findPreference<GpuDriverPreference>("gpu_driver")?.item = item
|
||||
|
||||
findPreference<SwitchPreferenceCompat>("enable_speed_limit")?.isChecked?.let {
|
||||
disablePreference("speed_limit", !it, null)
|
||||
}
|
||||
disablePreference("force_max_gpu_clocks", !GpuDriverHelper.supportsForceMaxGpuClocks(), context?.getString(R.string.force_max_gpu_clocks_desc_unsupported))
|
||||
|
||||
// Hide settings that don't support per-game configuration
|
||||
var prefToRemove = findPreference<Preference>("profile_picture_value")
|
||||
prefToRemove?.parent?.removePreference(prefToRemove)
|
||||
@ -76,4 +79,20 @@ class GameSettingsFragment : PreferenceFragmentCompat() {
|
||||
if (BuildConfig.BUILD_TYPE == "release")
|
||||
findPreference<PreferenceCategory>("category_debug")?.isVisible = false
|
||||
}
|
||||
|
||||
private fun disablePreference(
|
||||
preferenceId: String,
|
||||
isDisabled: Boolean,
|
||||
disabledSummary: String? = null
|
||||
) {
|
||||
val preference = findPreference<Preference>(preferenceId)!!
|
||||
preference.isSelectable = !isDisabled
|
||||
preference.isEnabled = !isDisabled
|
||||
if (preference is TwoStatePreference && isDisabled) {
|
||||
preference.isChecked = false
|
||||
}
|
||||
if (isDisabled && disabledSummary != null) {
|
||||
preference.summary = disabledSummary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,11 @@ class GlobalSettingsFragment : PreferenceFragmentCompat() {
|
||||
true
|
||||
}
|
||||
|
||||
findPreference<Preference>("enable_speed_limit")?.setOnPreferenceChangeListener { _, newValue ->
|
||||
disablePreference("speed_limit", !(newValue as Boolean), null)
|
||||
true
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
WindowInfoTracker.getOrCreate(requireContext()).windowLayoutInfo(requireActivity()).collect { newLayoutInfo ->
|
||||
withContext(Dispatchers.Main) {
|
||||
@ -68,6 +73,9 @@ class GlobalSettingsFragment : PreferenceFragmentCompat() {
|
||||
|
||||
disablePreference("use_material_you", Build.VERSION.SDK_INT < Build.VERSION_CODES.S, null)
|
||||
disablePreference("force_max_gpu_clocks", !GpuDriverHelper.supportsForceMaxGpuClocks(), context!!.getString(R.string.force_max_gpu_clocks_desc_unsupported))
|
||||
findPreference<SwitchPreferenceCompat>("enable_speed_limit")?.isChecked?.let {
|
||||
disablePreference("speed_limit", !it, null)
|
||||
}
|
||||
resources.getStringArray(R.array.credits_entries).asIterable().shuffled().forEach {
|
||||
findPreference<PreferenceCategory>("category_credits")?.addPreference(Preference(context!!).apply {
|
||||
title = it
|
||||
|
@ -17,6 +17,8 @@ import kotlinx.serialization.Serializable
|
||||
@Suppress("unused")
|
||||
data class NativeSettings(
|
||||
// System
|
||||
var enableSpeedLimit : Boolean,
|
||||
var speedLimit : Float,
|
||||
var isDocked : Boolean,
|
||||
var usernameValue : String,
|
||||
var profilePictureValue : String,
|
||||
@ -50,6 +52,8 @@ data class NativeSettings(
|
||||
var validationLayer : Boolean
|
||||
) {
|
||||
constructor(context : Context, pref : EmulationSettings) : this(
|
||||
pref.enableSpeedLimit,
|
||||
pref.speedLimit,
|
||||
pref.isDocked,
|
||||
pref.usernameValue,
|
||||
pref.profilePictureValue,
|
||||
|
@ -97,6 +97,10 @@
|
||||
<string name="copy_global_settings_warning">Are you sure you want to copy global settings to this game? <b>Current settings will be lost</b></string>
|
||||
<!-- Settings - System -->
|
||||
<string name="system">System</string>
|
||||
<string name="enable_speed_limit">Enable Speed Limit</string>
|
||||
<string name="enable_speed_limit_desc">Emulation speed will be limited</string>
|
||||
<string name="speed_limit">Speed Limit</string>
|
||||
<string name="speed_limit_desc">Set the emulation speed limit</string>
|
||||
<string name="use_docked">Use Docked Mode</string>
|
||||
<string name="handheld_enabled">The system will emulate being in handheld mode</string>
|
||||
<string name="docked_enabled">The system will emulate being in docked mode</string>
|
||||
|
@ -3,6 +3,19 @@
|
||||
<PreferenceCategory
|
||||
android:key="category_system"
|
||||
android:title="@string/system">
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:summary="@string/enable_speed_limit_desc"
|
||||
app:key="enable_speed_limit"
|
||||
app:title="@string/enable_speed_limit" />
|
||||
<emu.skyline.preference.SeekBarPreference
|
||||
android:summary="@string/speed_limit_desc"
|
||||
android:defaultValue="100"
|
||||
android:key="speed_limit"
|
||||
android:title="@string/speed_limit"
|
||||
app:maxValue="200"
|
||||
app:minValue="0"
|
||||
app:isPercentage="true" />
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:summaryOff="@string/handheld_enabled"
|
||||
@ -148,16 +161,16 @@
|
||||
app:title="@string/vsync_mode"
|
||||
app:useSimpleSummaryProvider="true"/>
|
||||
<emu.skyline.preference.SeekBarPreference
|
||||
android:defaultValue="4"
|
||||
android:summary="@string/executor_slot_count_scale_desc"
|
||||
android:defaultValue="4"
|
||||
android:key="executor_slot_count_scale"
|
||||
android:title="@string/executor_slot_count_scale"
|
||||
app:maxValue="6"
|
||||
app:minValue="1"
|
||||
app:isPercentage="false" />
|
||||
<emu.skyline.preference.SeekBarPreference
|
||||
android:defaultValue="256"
|
||||
android:summary="@string/executor_flush_threshold_desc"
|
||||
android:defaultValue="256"
|
||||
android:key="executor_flush_threshold"
|
||||
android:title="@string/executor_flush_threshold"
|
||||
app:maxValue="1024"
|
||||
|
Loading…
x
Reference in New Issue
Block a user