committed by
crueter
10 changed files with 484 additions and 4 deletions
-
1src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
-
74src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/GpuUnswizzleSetting.kt
-
13src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
-
206src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/GpuUnswizzleDialogFragment.kt
-
15src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
-
4src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
-
71src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/GpuUnswizzleViewHolder.kt
-
96src/android/app/src/main/res/layout/dialog_gpu_unswizzle.xml
-
5src/android/app/src/main/res/values/strings.xml
-
3src/common/settings.h
@ -0,0 +1,74 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
package org.yuzu.yuzu_emu.features.settings.model.view |
|||
|
|||
import androidx.annotation.ArrayRes |
|||
import androidx.annotation.StringRes |
|||
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting |
|||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting |
|||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting |
|||
|
|||
class GpuUnswizzleSetting( |
|||
@StringRes titleId: Int = 0, |
|||
titleString: String = "", |
|||
@StringRes descriptionId: Int = 0, |
|||
descriptionString: String = "", |
|||
@ArrayRes val textureSizeChoicesId: Int, |
|||
@ArrayRes val textureSizeValuesId: Int, |
|||
@ArrayRes val streamSizeChoicesId: Int, |
|||
@ArrayRes val streamSizeValuesId: Int, |
|||
@ArrayRes val chunkSizeChoicesId: Int, |
|||
@ArrayRes val chunkSizeValuesId: Int |
|||
) : SettingsItem( |
|||
object : AbstractSetting { |
|||
override val key: String = "gpu_unswizzle_combined" |
|||
override val defaultValue: Any = false |
|||
override val isSaveable = true |
|||
override val isRuntimeModifiable = false |
|||
override fun getValueAsString(needsGlobal: Boolean): String = "combined" |
|||
override fun reset() { |
|||
BooleanSetting.GPU_UNSWIZZLE_ENABLED.reset() |
|||
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.reset() |
|||
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.reset() |
|||
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.reset() |
|||
} |
|||
}, |
|||
titleId, |
|||
titleString, |
|||
descriptionId, |
|||
descriptionString |
|||
) { |
|||
override val type = SettingsItem.TYPE_GPU_UNSWIZZLE |
|||
|
|||
// Check if GPU unswizzle is enabled via the dedicated boolean setting |
|||
fun isEnabled(needsGlobal: Boolean = false): Boolean = |
|||
BooleanSetting.GPU_UNSWIZZLE_ENABLED.getBoolean(needsGlobal) |
|||
|
|||
fun setEnabled(value: Boolean) = |
|||
BooleanSetting.GPU_UNSWIZZLE_ENABLED.setBoolean(value) |
|||
|
|||
fun enable() = setEnabled(true) |
|||
|
|||
fun disable() = setEnabled(false) |
|||
|
|||
fun getTextureSize(needsGlobal: Boolean = false): Int = |
|||
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.getInt(needsGlobal) |
|||
|
|||
fun setTextureSize(value: Int) = |
|||
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.setInt(value) |
|||
|
|||
fun getStreamSize(needsGlobal: Boolean = false): Int = |
|||
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.getInt(needsGlobal) |
|||
|
|||
fun setStreamSize(value: Int) = |
|||
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.setInt(value) |
|||
|
|||
fun getChunkSize(needsGlobal: Boolean = false): Int = |
|||
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.getInt(needsGlobal) |
|||
|
|||
fun setChunkSize(value: Int) = |
|||
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.setInt(value) |
|||
|
|||
fun reset() = setting.reset() |
|||
} |
|||
@ -0,0 +1,206 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
package org.yuzu.yuzu_emu.features.settings.ui |
|||
|
|||
import android.app.Dialog |
|||
import android.content.DialogInterface |
|||
import android.os.Bundle |
|||
import android.view.LayoutInflater |
|||
import android.widget.ArrayAdapter |
|||
import androidx.fragment.app.DialogFragment |
|||
import androidx.fragment.app.activityViewModels |
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder |
|||
import org.yuzu.yuzu_emu.R |
|||
import org.yuzu.yuzu_emu.databinding.DialogGpuUnswizzleBinding |
|||
import org.yuzu.yuzu_emu.features.settings.model.view.GpuUnswizzleSetting |
|||
|
|||
class GpuUnswizzleDialogFragment : DialogFragment() { |
|||
private var position = 0 |
|||
private val settingsViewModel: SettingsViewModel by activityViewModels() |
|||
private lateinit var binding: DialogGpuUnswizzleBinding |
|||
|
|||
override fun onCreate(savedInstanceState: Bundle?) { |
|||
super.onCreate(savedInstanceState) |
|||
position = requireArguments().getInt(POSITION) |
|||
|
|||
if (settingsViewModel.clickedItem == null) dismiss() |
|||
} |
|||
|
|||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { |
|||
binding = DialogGpuUnswizzleBinding.inflate(LayoutInflater.from(requireContext())) |
|||
val item = settingsViewModel.clickedItem as GpuUnswizzleSetting |
|||
|
|||
// Setup texture size dropdown |
|||
val textureSizeEntries = resources.getStringArray(item.textureSizeChoicesId) |
|||
val textureSizeValues = resources.getIntArray(item.textureSizeValuesId) |
|||
val textureSizeAdapter = ArrayAdapter( |
|||
requireContext(), |
|||
android.R.layout.simple_dropdown_item_1line, |
|||
textureSizeEntries.toMutableList() |
|||
) |
|||
binding.dropdownTextureSize.setAdapter(textureSizeAdapter) |
|||
|
|||
// Setup stream size dropdown |
|||
val streamSizeEntries = resources.getStringArray(item.streamSizeChoicesId) |
|||
val streamSizeValues = resources.getIntArray(item.streamSizeValuesId) |
|||
val streamSizeAdapter = ArrayAdapter( |
|||
requireContext(), |
|||
android.R.layout.simple_dropdown_item_1line, |
|||
streamSizeEntries.toMutableList() |
|||
) |
|||
binding.dropdownStreamSize.setAdapter(streamSizeAdapter) |
|||
|
|||
// Setup chunk size dropdown |
|||
val chunkSizeEntries = resources.getStringArray(item.chunkSizeChoicesId) |
|||
val chunkSizeValues = resources.getIntArray(item.chunkSizeValuesId) |
|||
val chunkSizeAdapter = ArrayAdapter( |
|||
requireContext(), |
|||
android.R.layout.simple_dropdown_item_1line, |
|||
chunkSizeEntries.toMutableList() |
|||
) |
|||
binding.dropdownChunkSize.setAdapter(chunkSizeAdapter) |
|||
|
|||
// Load current values |
|||
val isEnabled = item.isEnabled() |
|||
binding.switchEnable.isChecked = isEnabled |
|||
|
|||
if (isEnabled) { |
|||
val textureSizeIndex = textureSizeValues.indexOf(item.getTextureSize()) |
|||
if (textureSizeIndex >= 0) { |
|||
binding.dropdownTextureSize.setText(textureSizeEntries[textureSizeIndex], false) |
|||
} |
|||
|
|||
val streamSizeIndex = streamSizeValues.indexOf(item.getStreamSize()) |
|||
if (streamSizeIndex >= 0) { |
|||
binding.dropdownStreamSize.setText(streamSizeEntries[streamSizeIndex], false) |
|||
} |
|||
|
|||
val chunkSizeIndex = chunkSizeValues.indexOf(item.getChunkSize()) |
|||
if (chunkSizeIndex >= 0) { |
|||
binding.dropdownChunkSize.setText(chunkSizeEntries[chunkSizeIndex], false) |
|||
} |
|||
} else { |
|||
// Set default/recommended values when disabling |
|||
binding.dropdownTextureSize.setText(textureSizeEntries[3], false) |
|||
binding.dropdownStreamSize.setText(streamSizeEntries[3], false) |
|||
binding.dropdownChunkSize.setText(chunkSizeEntries[3], false) |
|||
} |
|||
|
|||
// Clear adapter filters after setText to fix rotation bug |
|||
textureSizeAdapter.filter.filter(null) |
|||
streamSizeAdapter.filter.filter(null) |
|||
chunkSizeAdapter.filter.filter(null) |
|||
|
|||
// Enable/disable dropdowns based on switch state |
|||
updateDropdownsState(isEnabled) |
|||
binding.switchEnable.setOnCheckedChangeListener { _, checked -> |
|||
updateDropdownsState(checked) |
|||
} |
|||
|
|||
val dialog = MaterialAlertDialogBuilder(requireContext()) |
|||
.setTitle(item.title) |
|||
.setView(binding.root) |
|||
.create() |
|||
|
|||
// Setup button listeners |
|||
binding.btnDefault.setOnClickListener { |
|||
// Reset to defaults |
|||
item.reset() |
|||
// Refresh values with adapters reset |
|||
val textureSizeIndex = textureSizeValues.indexOf(item.getTextureSize()) |
|||
if (textureSizeIndex >= 0) { |
|||
binding.dropdownTextureSize.setText(textureSizeEntries[textureSizeIndex], false) |
|||
} |
|||
val streamSizeIndex = streamSizeValues.indexOf(item.getStreamSize()) |
|||
if (streamSizeIndex >= 0) { |
|||
binding.dropdownStreamSize.setText(streamSizeEntries[streamSizeIndex], false) |
|||
} |
|||
val chunkSizeIndex = chunkSizeValues.indexOf(item.getChunkSize()) |
|||
if (chunkSizeIndex >= 0) { |
|||
binding.dropdownChunkSize.setText(chunkSizeEntries[chunkSizeIndex], false) |
|||
} |
|||
// Clear filters |
|||
textureSizeAdapter.filter.filter(null) |
|||
streamSizeAdapter.filter.filter(null) |
|||
chunkSizeAdapter.filter.filter(null) |
|||
|
|||
settingsViewModel.setAdapterItemChanged(position) |
|||
settingsViewModel.setShouldReloadSettingsList(true) |
|||
} |
|||
|
|||
binding.btnCancel.setOnClickListener { |
|||
dialog.dismiss() |
|||
} |
|||
|
|||
binding.btnOk.setOnClickListener { |
|||
if (binding.switchEnable.isChecked) { |
|||
item.enable() |
|||
// Save the selected values |
|||
val selectedTextureIndex = textureSizeEntries.indexOf( |
|||
binding.dropdownTextureSize.text.toString() |
|||
) |
|||
if (selectedTextureIndex >= 0) { |
|||
item.setTextureSize(textureSizeValues[selectedTextureIndex]) |
|||
} |
|||
|
|||
val selectedStreamIndex = streamSizeEntries.indexOf( |
|||
binding.dropdownStreamSize.text.toString() |
|||
) |
|||
if (selectedStreamIndex >= 0) { |
|||
item.setStreamSize(streamSizeValues[selectedStreamIndex]) |
|||
} |
|||
|
|||
val selectedChunkIndex = chunkSizeEntries.indexOf( |
|||
binding.dropdownChunkSize.text.toString() |
|||
) |
|||
if (selectedChunkIndex >= 0) { |
|||
item.setChunkSize(chunkSizeValues[selectedChunkIndex]) |
|||
} |
|||
} else { |
|||
// Disable GPU unswizzle |
|||
item.disable() |
|||
} |
|||
|
|||
settingsViewModel.setAdapterItemChanged(position) |
|||
settingsViewModel.setShouldReloadSettingsList(true) |
|||
dialog.dismiss() |
|||
} |
|||
|
|||
// Ensure filters are cleared after dialog is shown |
|||
binding.root.post { |
|||
textureSizeAdapter.filter.filter(null) |
|||
streamSizeAdapter.filter.filter(null) |
|||
chunkSizeAdapter.filter.filter(null) |
|||
} |
|||
|
|||
return dialog |
|||
} |
|||
|
|||
private fun updateDropdownsState(enabled: Boolean) { |
|||
binding.layoutTextureSize.isEnabled = enabled |
|||
binding.dropdownTextureSize.isEnabled = enabled |
|||
binding.layoutStreamSize.isEnabled = enabled |
|||
binding.dropdownStreamSize.isEnabled = enabled |
|||
binding.layoutChunkSize.isEnabled = enabled |
|||
binding.dropdownChunkSize.isEnabled = enabled |
|||
} |
|||
|
|||
companion object { |
|||
const val TAG = "GpuUnswizzleDialogFragment" |
|||
const val POSITION = "Position" |
|||
|
|||
fun newInstance( |
|||
settingsViewModel: SettingsViewModel, |
|||
item: GpuUnswizzleSetting, |
|||
position: Int |
|||
): GpuUnswizzleDialogFragment { |
|||
val dialog = GpuUnswizzleDialogFragment() |
|||
val args = Bundle() |
|||
args.putInt(POSITION, position) |
|||
dialog.arguments = args |
|||
settingsViewModel.clickedItem = item |
|||
return dialog |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder |
|||
|
|||
import android.view.View |
|||
import org.yuzu.yuzu_emu.R |
|||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding |
|||
import org.yuzu.yuzu_emu.features.settings.model.view.GpuUnswizzleSetting |
|||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem |
|||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter |
|||
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible |
|||
|
|||
class GpuUnswizzleViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) : |
|||
SettingViewHolder(binding.root, adapter) { |
|||
private lateinit var setting: GpuUnswizzleSetting |
|||
|
|||
override fun bind(item: SettingsItem) { |
|||
setting = item as GpuUnswizzleSetting |
|||
binding.textSettingName.text = setting.title |
|||
binding.textSettingDescription.setVisible(item.description.isNotEmpty()) |
|||
binding.textSettingDescription.text = item.description |
|||
|
|||
binding.textSettingValue.setVisible(true) |
|||
val resMgr = binding.root.context.resources |
|||
|
|||
if (setting.isEnabled()) { |
|||
// Show a summary of current settings |
|||
val textureSizeEntries = resMgr.getStringArray(setting.textureSizeChoicesId) |
|||
val textureSizeValues = resMgr.getIntArray(setting.textureSizeValuesId) |
|||
val textureSizeIndex = textureSizeValues.indexOf(setting.getTextureSize()) |
|||
val textureSizeLabel = if (textureSizeIndex >= 0) textureSizeEntries[textureSizeIndex] else "?" |
|||
|
|||
val streamSizeEntries = resMgr.getStringArray(setting.streamSizeChoicesId) |
|||
val streamSizeValues = resMgr.getIntArray(setting.streamSizeValuesId) |
|||
val streamSizeIndex = streamSizeValues.indexOf(setting.getStreamSize()) |
|||
val streamSizeLabel = if (streamSizeIndex >= 0) streamSizeEntries[streamSizeIndex] else "?" |
|||
|
|||
val chunkSizeEntries = resMgr.getStringArray(setting.chunkSizeChoicesId) |
|||
val chunkSizeValues = resMgr.getIntArray(setting.chunkSizeValuesId) |
|||
val chunkSizeIndex = chunkSizeValues.indexOf(setting.getChunkSize()) |
|||
val chunkSizeLabel = if (chunkSizeIndex >= 0) chunkSizeEntries[chunkSizeIndex] else "?" |
|||
|
|||
binding.textSettingValue.text = "$textureSizeLabel ⋅ $streamSizeLabel ⋅ $chunkSizeLabel" |
|||
} else { |
|||
binding.textSettingValue.text = resMgr.getString(R.string.gpu_unswizzle_disabled) |
|||
} |
|||
|
|||
binding.buttonClear.setVisible(setting.clearable) |
|||
binding.buttonClear.setOnClickListener { |
|||
adapter.onClearClick(setting, bindingAdapterPosition) |
|||
} |
|||
|
|||
setStyle(setting.isEditable, binding) |
|||
} |
|||
|
|||
override fun onClick(clicked: View) { |
|||
if (!setting.isEditable) { |
|||
return |
|||
} |
|||
|
|||
adapter.onGpuUnswizzleClick(setting, bindingAdapterPosition) |
|||
} |
|||
|
|||
override fun onLongClick(clicked: View): Boolean { |
|||
if (setting.isEditable) { |
|||
return adapter.onLongClick(setting, bindingAdapterPosition) |
|||
} |
|||
return false |
|||
} |
|||
} |
|||
@ -0,0 +1,96 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:scrollbars="vertical"> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:orientation="vertical" |
|||
android:padding="24dp"> |
|||
|
|||
<com.google.android.material.switchmaterial.SwitchMaterial |
|||
android:id="@+id/switch_enable" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginBottom="16dp" |
|||
android:text="@string/gpu_unswizzle_enable" /> |
|||
|
|||
<com.google.android.material.textfield.TextInputLayout |
|||
android:id="@+id/layout_texture_size" |
|||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginBottom="12dp" |
|||
android:hint="@string/gpu_unswizzle_texture_size"> |
|||
|
|||
<com.google.android.material.textfield.MaterialAutoCompleteTextView |
|||
android:id="@+id/dropdown_texture_size" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:inputType="none" /> |
|||
</com.google.android.material.textfield.TextInputLayout> |
|||
|
|||
<com.google.android.material.textfield.TextInputLayout |
|||
android:id="@+id/layout_stream_size" |
|||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginBottom="12dp" |
|||
android:hint="@string/gpu_unswizzle_stream_size"> |
|||
|
|||
<com.google.android.material.textfield.MaterialAutoCompleteTextView |
|||
android:id="@+id/dropdown_stream_size" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:inputType="none" /> |
|||
</com.google.android.material.textfield.TextInputLayout> |
|||
|
|||
<com.google.android.material.textfield.TextInputLayout |
|||
android:id="@+id/layout_chunk_size" |
|||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:hint="@string/gpu_unswizzle_chunk_size"> |
|||
|
|||
<com.google.android.material.textfield.MaterialAutoCompleteTextView |
|||
android:id="@+id/dropdown_chunk_size" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:inputType="none" /> |
|||
</com.google.android.material.textfield.TextInputLayout> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginTop="24dp" |
|||
android:orientation="horizontal" |
|||
android:gravity="end" |
|||
android:spacing="8dp"> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/btn_default" |
|||
style="@style/Widget.Material3.Button.TextButton" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:text="@string/gpu_unswizzle_default_button" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/btn_cancel" |
|||
style="@style/Widget.Material3.Button.TextButton" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:text="@android:string/cancel" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/btn_ok" |
|||
style="@style/Widget.Material3.Button.TextButton" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:text="@android:string/ok" /> |
|||
</LinearLayout> |
|||
|
|||
</LinearLayout> |
|||
</ScrollView> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue