diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/QuickSettings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/QuickSettings.kt index 1136923fd0..6aab69db72 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/QuickSettings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/QuickSettings.kt @@ -9,6 +9,7 @@ import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import android.widget.LinearLayout import android.widget.RadioGroup import android.widget.TextView import androidx.drawerlayout.widget.DrawerLayout @@ -673,6 +674,137 @@ class QuickSettings(val emulationFragment: EmulationFragment) { } } + private fun addFrameGenCell(inflater: LayoutInflater, row: ViewGroup): TextView { + val cell = inflater.inflate(R.layout.item_quick_settings_frame_gen_cell, row, false) + as TextView + row.addView(cell) + return cell + } + + fun addFrameGen(container: ViewGroup) { + val inflater = LayoutInflater.from(emulationFragment.requireContext()) + val itemView = inflater.inflate(R.layout.item_quick_settings_frame_gen, container, false) + + val multiplierRow = itemView.findViewById(R.id.frame_gen_multipliers) + val targetRow = itemView.findViewById(R.id.frame_gen_targets) + val motionView = itemView.findViewById(R.id.frame_gen_motion) + val motionSwitch = itemView.findViewById(R.id.frame_gen_motion_switch) + + val multiplierNames = + emulationFragment.resources.getStringArray(R.array.frameGenMultiplierNames) + val multiplierValues = + emulationFragment.resources.getIntArray(R.array.frameGenMultiplierValues) + val targetValues = + emulationFragment.resources.getIntArray(R.array.frameGenTargetRateValues) + + val columns = maxOf(multiplierValues.size + 1, targetValues.size).toFloat() + multiplierRow.weightSum = columns + targetRow.weightSum = columns + + val powerCell = addFrameGenCell(inflater, multiplierRow) + + val multiplierCells = mutableListOf() + for (name in multiplierNames) { + val cell = addFrameGenCell(inflater, multiplierRow) + cell.text = name + multiplierCells.add(cell) + } + + val targetCells = mutableListOf() + for (value in targetValues) { + val cell = addFrameGenCell(inflater, targetRow) + if (value == 0) { + cell.setText(R.string.frame_gen_fixed) + } else { + cell.text = value.toString() + } + targetCells.add(cell) + } + + val inactiveAlpha = 0.38f + + fun refresh() { + val enabled = BooleanSetting.RENDERER_FRAME_GEN.getBoolean(needsGlobal = false) + val multiplier = IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.getInt(needsGlobal = false) + val target = IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.getInt(needsGlobal = false) + val fixed = target == 0 + + var powerLabel = R.string.frame_gen_off + var rowAlpha = inactiveAlpha + if (enabled) { + powerLabel = R.string.frame_gen_on + rowAlpha = 1.0f + } + + var multiplierAlpha = inactiveAlpha + if (enabled && fixed) { + multiplierAlpha = 1.0f + } + + powerCell.setText(powerLabel) + powerCell.isSelected = enabled + + multiplierCells.forEachIndexed { index, cell -> + cell.isEnabled = enabled + cell.isSelected = enabled && fixed && multiplierValues[index] == multiplier + cell.alpha = multiplierAlpha + } + + targetCells.forEachIndexed { index, cell -> + cell.isEnabled = enabled + cell.isSelected = enabled && targetValues[index] == target + cell.alpha = rowAlpha + } + + motionView.isEnabled = enabled + motionSwitch.isEnabled = enabled + motionView.alpha = rowAlpha + } + + powerCell.setOnClickListener { + if (BooleanSetting.RENDERER_FRAME_GEN.getBoolean(needsGlobal = false)) { + BooleanSetting.RENDERER_FRAME_GEN.setBoolean(false) + } else { + IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.setInt(multiplierValues.first()) + IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.setInt(0) + BooleanSetting.RENDERER_FRAME_GEN.setBoolean(true) + } + saveSettings() + refresh() + } + + multiplierCells.forEachIndexed { index, cell -> + cell.setOnClickListener { + IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.setInt(multiplierValues[index]) + IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.setInt(0) + saveSettings() + refresh() + } + } + + targetCells.forEachIndexed { index, cell -> + cell.setOnClickListener { + IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.setInt(targetValues[index]) + saveSettings() + refresh() + } + } + + motionSwitch.isChecked = + BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.getBoolean(needsGlobal = false) + motionSwitch.setOnCheckedChangeListener { _, checked -> + BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.setBoolean(checked) + saveSettings() + } + + motionView.setOnClickListener { + motionSwitch.toggle() + } + + refresh() + container.addView(itemView) + } + fun addDivider(container: ViewGroup) { val inflater = LayoutInflater.from(emulationFragment.requireContext()) val dividerView = inflater.inflate(R.layout.item_quick_settings_divider, container, false) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index 5eaaaaecac..dae9297fd7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -40,7 +40,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { RENDERER_FRAME_GEN("frame_gen"), RENDERER_FRAME_GEN_FP16("frame_gen_fp16"), RENDERER_FRAME_GEN_FLOW_SCALE_AUTO("frame_gen_flow_scale_auto"), - RENDERER_FRAME_GEN_DUMP_FLOW("frame_gen_dump_flow"), GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"), PICTURE_IN_PICTURE("picture_in_picture"), USE_CUSTOM_RTC("custom_rtc_enabled"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index ab2ac7eb42..8d78dda333 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -124,8 +124,7 @@ abstract class SettingsItem( IntSetting.RENDERER_FRAME_GEN_QUEUE_TARGET.key, BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.key, IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.key, - BooleanSetting.RENDERER_FRAME_GEN_FP16.key, - BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW.key + BooleanSetting.RENDERER_FRAME_GEN_FP16.key ) const val TYPE_HEADER = 0 @@ -716,13 +715,6 @@ abstract class SettingsItem( descriptionId = R.string.frame_gen_fp16_description ) ) - put( - SwitchSetting( - BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW, - titleId = R.string.frame_gen_dump_flow, - descriptionId = R.string.frame_gen_dump_flow_description - ) - ) put( SingleChoiceSetting( IntSetting.RENDERER_SCREEN_LAYOUT, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 89e0084df0..c50649277c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -1553,7 +1553,6 @@ class SettingsFragmentPresenter( add(BooleanSetting.DUMP_GUEST_SHADERS.key) add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key) add(BooleanSetting.DUMP_MACROS.key) - add(BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW.key) add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key) add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key) add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 3f1f3da2ba..df8e4ab5ca 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -92,6 +92,7 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.Log +import org.yuzu.yuzu_emu.utils.LosslessScalingHelper import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig import org.yuzu.yuzu_emu.utils.NativePostProcessing @@ -1188,6 +1189,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { quickSettings.addDivider(container) + if (::emulationState.isInitialized && emulationState.frameGenAtLaunch && + LosslessScalingHelper.isInstalled() && LosslessScalingHelper.isSupportedByGpu() + ) { + quickSettings.addFrameGen(container) + quickSettings.addDivider(container) + } + quickSettings.addIntSetting( R.string.renderer_accuracy, container, @@ -2273,6 +2281,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { private var surface: Surface? = null lateinit var emulationThread: Thread + @get:Synchronized + var frameGenAtLaunch = false + private set + init { state = State.STOPPED } @@ -2357,6 +2369,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { @Synchronized fun changeProgram(programIndex: Int) { emulationThread.join() + frameGenAtLaunch = BooleanSetting.RENDERER_FRAME_GEN.getBoolean(false) emulationThread = Thread({ Log.debug("[EmulationFragment] Starting emulation thread.") NativeLibrary.run(gamePath, programIndex, false) @@ -2424,6 +2437,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { when (state) { State.STOPPED -> { NativeLibrary.surfaceChanged(currentSurface) + frameGenAtLaunch = BooleanSetting.RENDERER_FRAME_GEN.getBoolean(false) emulationThread = Thread({ Log.debug("[EmulationFragment] Starting emulation thread.") NativeLibrary.run(gamePath, programIndex, true) diff --git a/src/android/app/src/main/res/color/frame_gen_cell_text.xml b/src/android/app/src/main/res/color/frame_gen_cell_text.xml new file mode 100644 index 0000000000..6e671681bf --- /dev/null +++ b/src/android/app/src/main/res/color/frame_gen_cell_text.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/android/app/src/main/res/drawable/frame_gen_cell_background.xml b/src/android/app/src/main/res/drawable/frame_gen_cell_background.xml new file mode 100644 index 0000000000..6f76a37718 --- /dev/null +++ b/src/android/app/src/main/res/drawable/frame_gen_cell_background.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/android/app/src/main/res/layout/item_quick_settings_frame_gen.xml b/src/android/app/src/main/res/layout/item_quick_settings_frame_gen.xml new file mode 100644 index 0000000000..d02e5b5eb3 --- /dev/null +++ b/src/android/app/src/main/res/layout/item_quick_settings_frame_gen.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/android/app/src/main/res/layout/item_quick_settings_frame_gen_cell.xml b/src/android/app/src/main/res/layout/item_quick_settings_frame_gen_cell.xml new file mode 100644 index 0000000000..0d4d0af3a8 --- /dev/null +++ b/src/android/app/src/main/res/layout/item_quick_settings_frame_gen_cell.xml @@ -0,0 +1,13 @@ + + diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index a3d7b2b515..5543ea732d 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -305,8 +305,6 @@ 60 إطارًا في الثانية 90 إطارًا في الثانية 120 إطارًا في الثانية - 144 إطارًا في الثانية - 165 إطارًا في الثانية هدف قائمة انتظار الإطارات كم عدد الإطارات المكتملة التي قد تنتظر قبل عرضها؟ تعمل قوائم الانتظار الأكبر حجمًا على امتصاص الارتفاعات المفاجئة في حمل وحدة معالجة الرسومات على حساب زمن انتقال الإدخال. أقل زمن انتقال (بدون تخزين مؤقت) @@ -318,8 +316,6 @@ دقة مسار التدفق البصري، كجزء من الناتج. ويُعد خفض هذه القيمة أرخص طريقة لاستعادة الأداء. مُظلِّلات نصف الدقة استخدم نسخة التظليل 16 بت. يتم التراجع تلقائيًا إلى الخيار البديل في حالة عدم توفرها في برنامج التشغيل أو الملف. - إفراغ الإطار الذي تم إنشاؤه - قم بكتابة مستويات MIP للتدفق البصري والإطار المُستكمل إلى مجلد lossless/debug مرة واحدة، لغرض استكشاف الأخطاء وإصلاحها توليد الإطار غير متاح لا يدعم برنامج تشغيل وحدة معالجة الرسومات هذا نموذج ذاكرة Vulkan، الذي تتطلبه برامج التظليل الخاصة بـ«Lossless Scaling». اختياري. قم بتوفير ملف Lossless.dll الخاص بك لتمكين إنشاء الإطارات لاحقًا diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index fe190325f6..42a428143a 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -304,8 +304,6 @@ 60 FPS 90 FPS 120 FPS - 144 FPS - 165 FPS Ausbalanciert (1 Bild) Flüssigste (2 Bilder) Frame-Generation nicht verfügbar diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index 460ea1f0da..2fecc7a82f 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -301,8 +301,6 @@ 60 FPS 90 FPS 120 FPS - 144 FPS - 165 FPS Latencia más baja (Sin búfer) Equilibrado (1 fotograma) Más suave (2 fotogramas) diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index 630f61ffa4..e4621ec308 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -310,8 +310,6 @@ Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность. Шейдеры половинной точности Использовать 16-битную версию шейдеров. Автоматически переключается на обычную, если драйвер или файл не поддерживают её. - Сохранить сгенерированный кадр - Однократно записать уровни мип-карт оптического потока и интерполированный кадр в папку lossless/debug для диагностики. Генерация кадров недоступна Драйвер ГПУ не поддерживает модель памяти Vulkan, требуемую шейдерами Lossless Scaling. Опционально. Укажите свой Lossless.dll для включения генерации кадров позже. diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index 930058b42d..f6dc99ae67 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -305,8 +305,6 @@ 60 FPS 90 FPS 120 FPS - 144 FPS - 165 FPS 帧队列目标 在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。 最低延迟 (无缓冲) @@ -318,8 +316,6 @@ 光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。 半精度着色器 使用 16 位着色器变体。如果驱动或文件不支持会自动回退。 - 转储已生成的帧 - 为了排查问题,把光流 mip 级别和插值帧写入 lossless/debug 文件夹一次 帧生成不可用 这个 GPU 驱动不支持无损缩放着色器所需的 Vulkan 内存模型。 作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。 diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index 28d0e93b4e..d42154280f 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -305,8 +305,6 @@ 60 FPS 90 FPS 120 FPS - 144 FPS - 165 FPS 影格佇列目標 最多允許多少個已完成的影格在顯示器前等待,較大的佇列可以吸收 GPU 突發負載,但會增加輸入延遲 最低延遲(無緩衝) @@ -318,8 +316,6 @@ 光流處理的解析度,以輸出解析度的比例表示。降低此設定值是減少效能負載最有效的方法 半準確著色器 使用16位元著色器。如果驅動程式或著色器檔案不支援則會自動切換成其它版本 - 傾印生成的著色器 - 將光流的 MIP 層級與補間影格寫入 Eden 資料夾中的 lossless\debug 資料夾以便進行疑難排解 無法使用影格生成 Lossless Scaling 的著色器需要 Vulkan 記憶體模型,所選的驅動程式不支援該功能 可選擇安裝自己擁有的 Lossless.dll 以在之後啟用影格生成功能 diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 931cf2bfa7..7c06edcd5a 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -174,8 +174,6 @@ @string/frame_gen_target_rate_60 @string/frame_gen_target_rate_90 @string/frame_gen_target_rate_120 - @string/frame_gen_target_rate_144 - @string/frame_gen_target_rate_165 @@ -183,8 +181,6 @@ 60 90 120 - 144 - 165 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index a9200bbca9..1cf2948c41 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -331,8 +331,9 @@ 60 FPS 90 FPS 120 FPS - 144 FPS - 165 FPS + On + Off + Fixed Frame queue target How many finished frames may wait ahead of the display. Larger queues absorb GPU spikes at the cost of input latency. Lowest latency (Unbuffered) @@ -344,8 +345,6 @@ Resolution of the optical flow pass, as a fraction of the output. Lowering it is the cheapest way to reclaim performance. Half precision shaders Use the 16-bit shader variant. Falls back automatically if the driver or the file lacks it. - Dump generated frame - Write the optical flow mip levels and the interpolated frame to the lossless/debug folder once, for troubleshooting Frame generation unavailable This GPU driver does not support the Vulkan memory model, which the Lossless Scaling shaders require. Optional. Provide your own Lossless.dll to enable frame generation later