跳转至

设置

[!WARNING] 本指南仅供开发者使用。如果你是在寻找如何配置模拟器本身,请阅读 User Handbook。

模拟器上的设置非常重要,开关之类的选项可用于保护和/或添加分支路径;在某些路径下,一些游戏可能会崩溃,而其他游戏则不会,反之亦然。

不过,对于不熟悉此过程的人来说,这可能会很繁琐;本文档用作设置子系统的概述/文档。

索引

添加布尔设置开关

本指南将介绍如何在 Eden 的配置中新增一个布尔开关设置,涵盖 Qt(PC)和 Kotlin(Android)两套界面。


步骤 1 - 通用设置

首先,添加你想要的开关:

示例:src/common/setting.h

SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};

记住,要将你的开关添加到合适的类别中,例如:

常见类别:

  • Category::Renderer
  • Category::RendererAdvanced
  • Category::RendererExtensions
  • Category::System
  • Category::Core

[!WARNING] 如果你希望你的开关on by default(默认开启),请将 linkage, 之后的 false 改为 true。


步骤 2 - Qt 开关

将开关添加到 Qt 界面中你希望它出现的位置。

示例:src/qt_common/config/shared_translation.cpp

INSERT(Settings,
       your_setting_name,
       tr("Your Setting Display Name"),
       tr("Detailed description of what this setting does.\n"
          "You can use multiple lines.\n"
          "Explain any caveats or requirements."));

请确保:

  • 保持显示命名一致
  • 在描述中提供详细信息
  • 在描述中使用 \n 进行换行

步骤 3 - Kotlin(Android)

步骤 3.1 - BooleanSetting.kt

在设置中应有的位置添加它。

示例:src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt

RENDERER_YOUR_SETTING_NAME("your_setting_name"),

请确保:

  • 确保前缀命名与预期的类别匹配。

步骤 3.2 - SettingsItem.kt

将开关添加到 Kotlin(Android)界面中。

示例:src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt

put(
    SwitchSetting(
        BooleanSetting.RENDERER_YOUR_SETTING_NAME,
        titleId = R.string.your_setting_name,
        descriptionId = R.string.your_setting_name_description
    )
)

步骤 3.3 - SettingsFragmentPresenter.kt

在正确的类别中添加你的设置。

示例:src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt

add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)

[!WARNING] 请记住,位置很重要!设置会按照你添加它们的顺序显示。


步骤 3.4 - 本地化

在合适的位置添加你的设置及描述。

示例:src/android/app/src/main/res/values/strings.xml

<string name="your_setting_name">Your Setting Display Name</string>
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>

步骤 4 - 使用你的开关!

现在 UI 部分已经完成,在代码中为这个开关找一个位置,然后尽情使用它吧!

示例:

const bool your_value = Settings::values.your_setting_name.GetValue();

if (your_value) {
    // Do something when enabled
}

如果你只想在开关被禁用时执行某些操作,请使用 if (!your_value) { 而不是 if (your_value) {。


最佳实践

  • 命名 - 使用清晰、具有描述性的名称,让开发者和用户都能一目了然。
  • 默认值 - 选择安全的默认值(新功能通常为 false)。
  • 文档 - 编写清晰的描述,说明何时以及为何要使用该设置。
  • 类别 - 将设置放在合适的类别中。
  • 顺序 - 将相关设置放在彼此附近。
  • 测试 - 在提交之前,尽可能同时在 PC 和 Android 上进行测试。

感谢阅读,希望本指南能帮助你创建自己的开关!

添加 Debug Knobs

Debug Knobs 是 Eden 模拟器中的一个 16 位整数设置(debug_knobs),用作位掩码(bitmask)来门控各种测试和调试功能。这让开发者和高级用户能够在不部署完整但临时的开关的情况下,启用或禁用特定的调试行为。

该设置的范围为 0 到 65535(0x0000 到 0xFFFF),其中每一位代表一个不同的调试功能标志。


优点

主要优点是避免部署新的一次性开关(那些仅为测试阶段制作、一旦新功能可以合并就被弃用的开关)。这让开发者能够摆脱所有前端流程中的繁文缛节以及新增开关的麻烦。

常见优势回顾:

  • 精细控制:通过单个构建上的位操作,独立启用或禁用多达 16 个独立的调试功能
  • 运行时配置:在运行时更改调试行为,就像新增的开关所做的那样
  • 安全的增量开发:可以添加新的调试功能,同时将影响与之前的部署隔离开来

使用

访问调试开关(开发者侧)

使用 Settings::GetDebugKnobAt(u8 i) 函数检查某个特定位是否被设置:

//cpp side
#include "common/settings.h"

//To use it as a general purpose uint var:
unsigned int debug_knobs = Settings::values.debug_knobs.GetValue();

// Check if bit 0 is set
bool feature_enabled = Settings::GetDebugKnobAt(0);

// Check if bit 15 is set
bool another_feature = Settings::GetDebugKnobAt(15);
//kotlin side
import org.yuzu.yuzu_emu.features.settings.model.Settings

//To use it as a general purpose uint var
val debug_knobs: Int = UShortSetting.DEBUG_KNOBS.getInt()

// Check if bit x is set
bool feature_enabled = Settings.GetDebugKnobAt(x); //x as integer from 0 to 15

如果 debug_knobs 值中设置了指定的位(0-15),该函数返回 true,否则返回 false。

设置调试开关(用户侧)

开发者必须说明每个待测试功能与哪些开关相关联。

调试开关值可以通过以下方式设置:

  1. 桌面 UI:在 Debug 配置选项卡中,有一个用于 “Debug knobs” 的数字选择框(0-65535)
  2. Android UI:在 Debug 部分可作为整数设置使用
  3. 配置文件:在模拟器的配置中设置 debug_knobs 值

位操作示例

要启用特定功能,请通过设置相应的位来计算十进制值:

  • 仅启用位 0:值 = 1(2^0)
  • 仅启用位 1:值 = 2(2^1)
  • 启用位 0 和 1:值 = 3(2^0 + 2^1)
  • 启用位 15:值 = 32768(2^15)

术语与用户沟通

在讨论开关时,主要有两个容易混淆的地方:

是从零开始还是从一开始

有时用户报告:“knobs 1 和 2 的性能更好”,开发者可能会困惑:他指的是字面意义上的 knobs 1 和 2,还是第 1 个和第 2 个开关(即 knobs 0 和 1)。

调试开关是从零开始编号的,这意味着:

  • 第一个开关是 knob(0)(以下简称 knob0),最后一个开关是 knob(15)(同理简称 knob15)
  • 你可以说:“knob0 已启用/已禁用”、“在这个视频中我只使用了 knobs 0 和 2”等

讨论的是开关本身还是整个参数值(代表所有开关)

有时用户报告:“knob 3 的结果”,但不清楚他指的是取值为 3 的开关设置(这意味着 knob 0 和 knob 1 均已启用),还是特指 knob(3)。

无论你是在指导测试还是在报告结果,都要明确指出你所指的是哪一种,以避免混淆:

基于设置的术语

指代设置(即一次表示多个开关)时,始终使用复数形式的词(knobs),且不指明具体是哪一个:

  • knobs=0:未启用任何开关
  • knobs=1:启用 knob0,其余禁用
  • knobs=2:启用 knob1,其余禁用
  • knobs=3:启用开关 0 和 1,其余禁用

...

基于开关的术语

指代单个开关时,使用单数形式(knob);指代多个开关时,使用复数形式并指明具体是哪些:

  • knob0:启用开关 0,其余禁用
  • knob1:启用开关 1,其余禁用
  • knobs 0 and 1:启用开关 0 和 1,其余禁用

...

示例

示例 1:条件式调试日志

void SomeFunction() {
    if (Settings::GetDebugKnobAt(0)) {
        LOG_DEBUG(Common, "Debug feature 0 is enabled");
        // Additional debug code here
    }

    if (Settings::GetDebugKnobAt(1)) {
        LOG_DEBUG(Common, "Debug feature 1 is enabled");
        // Different debug behavior
    }
}

示例 2:性能调优

bool UseOptimizedPath() {
    // Skip optimization if debug bit 2 is set for testing
    return !Settings::GetDebugKnobAt(2);
}

示例 3:功能门控

void ExperimentalFeature() {
    static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;

    if (!Settings::GetDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
        // Fallback to stable implementation
        StableImplementation();
        return;
    }

    // Experimental implementation
    ExperimentalImplementation();
}

最佳实践

  • 此设置仅用于开发和测试目的
  • 在创建 PR 之前,必须解除开关的绑定
  • 该设置可按游戏单独配置,从而允许不同游戏使用不同的调试配置

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 eg/Eglantine