Compare commits

..

1 Commits

Author SHA1 Message Date
Gamer64
4dcbc1c03c [hw_composer]: Add "Respect present interval 0 for unlocked FPS"
Co-authored-by: Zephyron <zephyron@citron-emu.org>
2025-12-07 21:01:30 -03:00
9 changed files with 31 additions and 49 deletions

View File

@@ -143,21 +143,8 @@ class ReleaseAdapter(
binding.containerDownloads.removeAllViews()
release.artifacts.forEach { artifact ->
val alreadyInstalled = try {
// Prefer fast check via ViewModel list; fallback to helper if needed
driverViewModel.driverData.any {
File(it.first).name.equals(artifact.name, ignoreCase = true)
} || GpuDriverHelper.isDriverZipInstalledByName(artifact.name)
} catch (_: Exception) {
false
}
val button = MaterialButton(binding.root.context).apply {
text = if (alreadyInstalled) {
context.getString(R.string.installed_label, artifact.name)
} else {
artifact.name
}
text = artifact.name
setTextAppearance(
com.google.android.material.R.style.TextAppearance_Material3_LabelLarge
)
@@ -167,7 +154,7 @@ class ReleaseAdapter(
com.google.android.material.R.color.m3_button_background_color_selector
)
)
setIconResource(if (alreadyInstalled) R.drawable.ic_check else R.drawable.ic_import)
setIconResource(R.drawable.ic_import)
iconTint = ColorStateList.valueOf(
MaterialColors.getColor(
this,
@@ -180,22 +167,7 @@ class ReleaseAdapter(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
isEnabled = !alreadyInstalled
setOnClickListener {
// Double-check just before starting (race-proof)
if (GpuDriverHelper.isDriverZipInstalledByName(artifact.name)) {
Toast.makeText(
context,
context.getString(R.string.driver_already_installed),
Toast.LENGTH_SHORT
).show()
// Update UI to reflect installed state
this.isEnabled = false
this.text = context.getString(R.string.installed_label, artifact.name)
this.setIconResource(R.drawable.ic_check)
return@setOnClickListener
}
val dialogBinding =
DialogProgressBinding.inflate(LayoutInflater.from(context))
dialogBinding.progressBar.isIndeterminate = true
@@ -261,10 +233,6 @@ class ReleaseAdapter(
driverViewModel.onDriverAdded(Pair(driverPath, driverData))
progressDialog.dismiss()
// Update button to installed state
this@apply.isEnabled = false
this@apply.text = context.getString(R.string.installed_label, artifact.name)
this@apply.setIconResource(R.drawable.ic_check)
Toast.makeText(
context,
context.getString(

View File

@@ -53,6 +53,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
SOC_OVERLAY_BACKGROUND("soc_overlay_background"),
ENABLE_INPUT_OVERLAY_AUTO_HIDE("enable_input_overlay_auto_hide"),
RESPECT_PRESENT_INTERVAL_ZERO("respect_present_interval_zero"),
PERF_OVERLAY_BACKGROUND("perf_overlay_background"),
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),

View File

@@ -237,6 +237,13 @@ abstract class SettingsItem(
override fun reset() = BooleanSetting.USE_DOCKED_MODE.reset()
}
put(
SwitchSetting(
BooleanSetting.RESPECT_PRESENT_INTERVAL_ZERO,
titleId = R.string.respect_present_interval_zero,
descriptionId = R.string.respect_present_interval_zero_description
)
)
put(
SwitchSetting(
dockedModeSetting,

View File

@@ -460,6 +460,7 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_SAMPLE_SHADING_FRACTION.key)
add(HeaderSetting(R.string.veil_renderer))
add(BooleanSetting.RESPECT_PRESENT_INTERVAL_ZERO.key)
add(BooleanSetting.RENDERER_EARLY_RELEASE_FENCES.key)
add(IntSetting.DMA_ACCURACY.key)
add(BooleanSetting.BUFFER_REORDER_DISABLE.key)

View File

@@ -238,18 +238,4 @@ object GpuDriverHelper {
driverStorageDirectory.mkdirs()
}
}
/**
* Checks if a driver zip with the given filename is already present and valid in the
* internal driver storage directory. Validation requires a readable meta.json with a name.
*/
fun isDriverZipInstalledByName(fileName: String): Boolean {
// Normalize separators in case upstream sent a path
val baseName = fileName.substringAfterLast('/')
.substringAfterLast('\\')
val candidate = File("$driverStoragePath$baseName")
if (!candidate.exists() || candidate.length() == 0L) return false
val metadata = getMetadataFromZip(candidate)
return metadata.name != null
}
}

View File

@@ -105,6 +105,8 @@
<string name="sample_shading_fraction_description">The intensity of the sample shading pass. Higher values improve quality more but also reduce performance to a greater extent.</string>
<string name="veil_renderer">Renderer</string>
<string name="respect_present_interval_zero">Respect present interval 0 for unlocked FPS</string>
<string name="respect_present_interval_zero_description">When enabled, present interval 0 will be used for games requesting unlocked FPS. This matches console behavior more closely, but may cause higher battery usage and frame pacing issues. When disabled (default), present interval 0 is capped at 120FPS to conserve battery.</string>
<string name="renderer_early_release_fences">Release Fences Early</string>
<string name="renderer_early_release_fences_description">Helps fix 0 FPS in games like DKCR:HD, Subnautica Below Zero and Ori 2, but may break loading or performance in Unreal Engine games.</string>
<string name="sync_memory_operations">Sync Memory Operations</string>
@@ -661,7 +663,6 @@
<string name="select_gpu_driver_default">Default</string>
<string name="select_gpu_driver_error">Invalid driver selected</string>
<string name="driver_already_installed">Driver already installed</string>
<string name="installed_label">%1$s (Installed)</string>
<string name="system_gpu_driver">System GPU driver</string>
<string name="installing_driver">Installing driver…</string>

View File

@@ -342,6 +342,9 @@ struct Values {
Category::Renderer};
SwitchableSetting<bool> use_asynchronous_gpu_emulation{
linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
SwitchableSetting<bool> respect_present_interval_zero{
linkage, false, "respect_present_interval_zero", Category::Renderer};
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
#ifdef ANDROID
AstcDecodeMode::Cpu,

View File

@@ -8,6 +8,8 @@
#include <boost/container/small_vector.hpp>
#include "common/settings.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
@@ -21,6 +23,14 @@ namespace {
s32 NormalizeSwapInterval(f32* out_speed_scale, s32 swap_interval) {
if (swap_interval <= 0) {
// If swap_interval is 0 and setting enabled, respect it as unlocked FPS
if (swap_interval == 0 && Settings::values.respect_present_interval_zero.GetValue()) {
if (out_speed_scale) {
*out_speed_scale = 1.0f;
}
return 0;
}
// As an extension, treat nonpositive swap interval as speed multiplier.
if (out_speed_scale) {
*out_speed_scale = 2.f * static_cast<f32>(1 - swap_interval);

View File

@@ -213,6 +213,11 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
use_asynchronous_gpu_emulation,
tr("Use asynchronous GPU emulation"),
tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled."));
INSERT(
Settings, respect_present_interval_zero, tr("Respect present interval 0 for unlocked FPS"),
tr("When enabled, present interval 0 will be used for games requesting unlocked FPS.\n"
"This matches console behavior more closely, but may cause higher battery usage and frame pacing issues.\n"
"When disabled (default), present interval 0 is capped at 120FPS to conserve battery."));
INSERT(Settings,
nvdec_emulation,
tr("NVDEC emulation:"),