duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

graphicssettingswidget.cpp (67771B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "graphicssettingswidget.h"
      5 #include "qtutils.h"
      6 #include "settingswindow.h"
      7 #include "settingwidgetbinder.h"
      8 
      9 #include "core/game_database.h"
     10 #include "core/gpu.h"
     11 #include "core/settings.h"
     12 
     13 #include "util/media_capture.h"
     14 
     15 #include <algorithm>
     16 
     17 static QVariant GetMSAAModeValue(uint multisamples, bool ssaa)
     18 {
     19   const uint userdata = (multisamples & 0x7FFFFFFFu) | (static_cast<uint>(ssaa) << 31);
     20   return QVariant(userdata);
     21 }
     22 
     23 static void DecodeMSAAModeValue(const QVariant& userdata, uint* multisamples, bool* ssaa)
     24 {
     25   bool ok;
     26   const uint value = userdata.toUInt(&ok);
     27   if (!ok || value == 0)
     28   {
     29     *multisamples = 1;
     30     *ssaa = false;
     31     return;
     32   }
     33 
     34   *multisamples = value & 0x7FFFFFFFu;
     35   *ssaa = (value & (1u << 31)) != 0u;
     36 }
     37 
     38 GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* parent)
     39   : QWidget(parent), m_dialog(dialog)
     40 {
     41   SettingsInterface* sif = dialog->getSettingsInterface();
     42 
     43   m_ui.setupUi(this);
     44   setupAdditionalUi();
     45   removePlatformSpecificUi();
     46 
     47   // Rendering Tab
     48 
     49   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.renderer, "GPU", "Renderer", &Settings::ParseRendererName,
     50                                                &Settings::GetRendererName, Settings::DEFAULT_GPU_RENDERER);
     51   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.textureFiltering, "GPU", "TextureFilter",
     52                                                &Settings::ParseTextureFilterName, &Settings::GetTextureFilterName,
     53                                                Settings::DEFAULT_GPU_TEXTURE_FILTER);
     54   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.spriteTextureFiltering, "GPU", "SpriteTextureFilter",
     55                                                &Settings::ParseTextureFilterName, &Settings::GetTextureFilterName,
     56                                                Settings::DEFAULT_GPU_TEXTURE_FILTER);
     57   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.gpuDownsampleMode, "GPU", "DownsampleMode",
     58                                                &Settings::ParseDownsampleModeName, &Settings::GetDownsampleModeName,
     59                                                Settings::DEFAULT_GPU_DOWNSAMPLE_MODE);
     60   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayAspectRatio, "Display", "AspectRatio",
     61                                                &Settings::ParseDisplayAspectRatio, &Settings::GetDisplayAspectRatioName,
     62                                                Settings::DEFAULT_DISPLAY_ASPECT_RATIO);
     63   SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.customAspectRatioNumerator, "Display",
     64                                               "CustomAspectRatioNumerator", 1);
     65   SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.customAspectRatioDenominator, "Display",
     66                                               "CustomAspectRatioDenominator", 1);
     67   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.widescreenHack, "GPU", "WidescreenHack", false);
     68   SettingWidgetBinder::BindWidgetToEnumSetting(
     69     sif, m_ui.displayDeinterlacing, "Display", "DeinterlacingMode", &Settings::ParseDisplayDeinterlacingMode,
     70     &Settings::GetDisplayDeinterlacingModeName, Settings::DEFAULT_DISPLAY_DEINTERLACING_MODE);
     71   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayCropMode, "Display", "CropMode",
     72                                                &Settings::ParseDisplayCropMode, &Settings::GetDisplayCropModeName,
     73                                                Settings::DEFAULT_DISPLAY_CROP_MODE);
     74   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayScaling, "Display", "Scaling",
     75                                                &Settings::ParseDisplayScaling, &Settings::GetDisplayScalingName,
     76                                                Settings::DEFAULT_DISPLAY_SCALING);
     77   SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.gpuDownsampleScale, "GPU", "DownsampleScale", 1);
     78   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.trueColor, "GPU", "TrueColor", false);
     79   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableInterlacing, "GPU", "DisableInterlacing", true);
     80   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpEnable, "GPU", "PGXPEnable", false);
     81   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpDepthBuffer, "GPU", "PGXPDepthBuffer", false);
     82   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.force43For24Bit, "Display", "Force4_3For24Bit", false);
     83   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.chromaSmoothingFor24Bit, "GPU", "ChromaSmoothing24Bit", false);
     84   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.forceNTSCTimings, "GPU", "ForceNTSCTimings", false);
     85 
     86   connect(m_ui.renderer, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
     87           &GraphicsSettingsWidget::updateRendererDependentOptions);
     88   connect(m_ui.textureFiltering, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
     89           &GraphicsSettingsWidget::updateResolutionDependentOptions);
     90   connect(m_ui.displayAspectRatio, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
     91           &GraphicsSettingsWidget::onAspectRatioChanged);
     92   connect(m_ui.gpuDownsampleMode, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
     93           &GraphicsSettingsWidget::onDownsampleModeChanged);
     94   connect(m_ui.trueColor, &QCheckBox::checkStateChanged, this, &GraphicsSettingsWidget::onTrueColorChanged);
     95   connect(m_ui.pgxpEnable, &QCheckBox::checkStateChanged, this, &GraphicsSettingsWidget::updatePGXPSettingsEnabled);
     96 
     97   SettingWidgetBinder::SetAvailability(m_ui.renderer,
     98                                        !m_dialog->hasGameTrait(GameDatabase::Trait::ForceSoftwareRenderer));
     99   SettingWidgetBinder::SetAvailability(m_ui.resolutionScale,
    100                                        !m_dialog->hasGameTrait(GameDatabase::Trait::DisableUpscaling));
    101   SettingWidgetBinder::SetAvailability(m_ui.textureFiltering,
    102                                        !m_dialog->hasGameTrait(GameDatabase::Trait::DisableTextureFiltering));
    103   SettingWidgetBinder::SetAvailability(m_ui.trueColor, !m_dialog->hasGameTrait(GameDatabase::Trait::DisableTrueColor));
    104   SettingWidgetBinder::SetAvailability(m_ui.pgxpEnable, !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXP));
    105   SettingWidgetBinder::SetAvailability(m_ui.disableInterlacing,
    106                                        !m_dialog->hasGameTrait(GameDatabase::Trait::ForceInterlacing));
    107   SettingWidgetBinder::SetAvailability(m_ui.widescreenHack,
    108                                        !m_dialog->hasGameTrait(GameDatabase::Trait::DisableWidescreen));
    109   SettingWidgetBinder::SetAvailability(m_ui.forceNTSCTimings,
    110                                        !m_dialog->hasGameTrait(GameDatabase::Trait::DisableForceNTSCTimings));
    111 
    112   // Advanced Tab
    113 
    114   SettingWidgetBinder::BindWidgetToEnumSetting(
    115     sif, m_ui.exclusiveFullscreenControl, "Display", "ExclusiveFullscreenControl",
    116     &Settings::ParseDisplayExclusiveFullscreenControl, &Settings::GetDisplayExclusiveFullscreenControlName,
    117     Settings::DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL);
    118   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayAlignment, "Display", "Alignment",
    119                                                &Settings::ParseDisplayAlignment, &Settings::GetDisplayAlignmentName,
    120                                                Settings::DEFAULT_DISPLAY_ALIGNMENT);
    121   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayRotation, "Display", "Rotation",
    122                                                &Settings::ParseDisplayRotation, &Settings::GetDisplayRotationName,
    123                                                Settings::DEFAULT_DISPLAY_ROTATION);
    124   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gpuThread, "GPU", "UseThread", true);
    125   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.threadedPresentation, "GPU", "ThreadedPresentation",
    126                                                Settings::DEFAULT_THREADED_PRESENTATION);
    127   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableMailboxPresentation, "Display",
    128                                                "DisableMailboxPresentation", false);
    129   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.stretchDisplayVertically, "Display", "StretchVertically",
    130                                                false);
    131 #ifdef _WIN32
    132   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.blitSwapChain, "Display", "UseBlitSwapChain", false);
    133 #endif
    134   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.gpuLineDetectMode, "GPU", "LineDetectMode",
    135                                                &Settings::ParseLineDetectModeName, &Settings::GetLineDetectModeName,
    136                                                Settings::DEFAULT_GPU_LINE_DETECT_MODE);
    137   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.gpuWireframeMode, "GPU", "WireframeMode",
    138                                                Settings::ParseGPUWireframeMode, Settings::GetGPUWireframeModeName,
    139                                                Settings::DEFAULT_GPU_WIREFRAME_MODE);
    140   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.debanding, "GPU", "Debanding", false);
    141   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.scaledDithering, "GPU", "ScaledDithering", false);
    142   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useSoftwareRendererForReadbacks, "GPU",
    143                                                "UseSoftwareRendererForReadbacks", false);
    144   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.forceRoundedTexcoords, "GPU", "ForceRoundTextureCoordinates",
    145                                                false);
    146   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.accurateBlending, "GPU", "AccurateBlending", false);
    147 
    148   SettingWidgetBinder::SetAvailability(m_ui.scaledDithering,
    149                                        !m_dialog->hasGameTrait(GameDatabase::Trait::DisableScaledDithering));
    150 
    151   // PGXP Tab
    152 
    153   SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.pgxpGeometryTolerance, "GPU", "PGXPTolerance", -1.0f);
    154   SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.pgxpDepthClearThreshold, "GPU", "PGXPDepthClearThreshold",
    155                                                 Settings::DEFAULT_GPU_PGXP_DEPTH_THRESHOLD);
    156   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpTextureCorrection, "GPU", "PGXPTextureCorrection", true);
    157   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpColorCorrection, "GPU", "PGXPColorCorrection", false);
    158   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpCulling, "GPU", "PGXPCulling", true);
    159   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpPreserveProjPrecision, "GPU", "PGXPPreserveProjFP", false);
    160   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpCPU, "GPU", "PGXPCPU", false);
    161   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpVertexCache, "GPU", "PGXPVertexCache", false);
    162   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpDisableOn2DPolygons, "GPU", "PGXPDisableOn2DPolygons",
    163                                                false);
    164 
    165   connect(m_ui.pgxpTextureCorrection, &QCheckBox::checkStateChanged, this,
    166           &GraphicsSettingsWidget::updatePGXPSettingsEnabled);
    167   connect(m_ui.pgxpDepthBuffer, &QCheckBox::checkStateChanged, this,
    168           &GraphicsSettingsWidget::updatePGXPSettingsEnabled);
    169 
    170   SettingWidgetBinder::SetAvailability(m_ui.pgxpTextureCorrection,
    171                                        !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPTextureCorrection));
    172   SettingWidgetBinder::SetAvailability(m_ui.pgxpColorCorrection,
    173                                        !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPColorCorrection));
    174   SettingWidgetBinder::SetAvailability(m_ui.pgxpCulling,
    175                                        !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPCulling));
    176   SettingWidgetBinder::SetAvailability(m_ui.pgxpPreserveProjPrecision,
    177                                        !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPPreserveProjFP));
    178 
    179   // OSD Tab
    180 
    181   SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.osdScale, "Display", "OSDScale", 100);
    182   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showOSDMessages, "Display", "ShowOSDMessages", true);
    183   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showFPS, "Display", "ShowFPS", false);
    184   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showSpeed, "Display", "ShowSpeed", false);
    185   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showResolution, "Display", "ShowResolution", false);
    186   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showCPU, "Display", "ShowCPU", false);
    187   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showGPU, "Display", "ShowGPU", false);
    188   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showInput, "Display", "ShowInputs", false);
    189   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showGPUStatistics, "Display", "ShowGPUStatistics", false);
    190   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showLatencyStatistics, "Display", "ShowLatencyStatistics",
    191                                                false);
    192   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showStatusIndicators, "Display", "ShowStatusIndicators", true);
    193   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showFrameTimes, "Display", "ShowFrameTimes", false);
    194   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.showSettings, "Display", "ShowEnhancements", false);
    195 
    196   // Capture Tab
    197 
    198   SettingWidgetBinder::BindWidgetToEnumSetting(
    199     sif, m_ui.screenshotSize, "Display", "ScreenshotMode", &Settings::ParseDisplayScreenshotMode,
    200     &Settings::GetDisplayScreenshotModeName, Settings::DEFAULT_DISPLAY_SCREENSHOT_MODE);
    201   SettingWidgetBinder::BindWidgetToEnumSetting(
    202     sif, m_ui.screenshotFormat, "Display", "ScreenshotFormat", &Settings::ParseDisplayScreenshotFormat,
    203     &Settings::GetDisplayScreenshotFormatName, Settings::DEFAULT_DISPLAY_SCREENSHOT_FORMAT);
    204   SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.screenshotQuality, "Display", "ScreenshotQuality",
    205                                               Settings::DEFAULT_DISPLAY_SCREENSHOT_QUALITY);
    206 
    207   SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.mediaCaptureBackend, "MediaCapture", "Backend",
    208                                                &MediaCapture::ParseBackendName, &MediaCapture::GetBackendName,
    209                                                Settings::DEFAULT_MEDIA_CAPTURE_BACKEND);
    210   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableVideoCapture, "MediaCapture", "VideoCapture", true);
    211   SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.videoCaptureWidth, "MediaCapture", "VideoWidth",
    212                                               Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_WIDTH);
    213   SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.videoCaptureHeight, "MediaCapture", "VideoHeight",
    214                                               Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_HEIGHT);
    215   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.videoCaptureResolutionAuto, "MediaCapture", "VideoAutoSize",
    216                                                false);
    217   SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.videoCaptureBitrate, "MediaCapture", "VideoBitrate",
    218                                               Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_BITRATE);
    219   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableVideoCaptureArguments, "MediaCapture",
    220                                                "VideoCodecUseArgs", false);
    221   SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.videoCaptureArguments, "MediaCapture", "AudioCodecArgs");
    222   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableAudioCapture, "MediaCapture", "AudioCapture", true);
    223   SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.audioCaptureBitrate, "MediaCapture", "AudioBitrate",
    224                                               Settings::DEFAULT_MEDIA_CAPTURE_AUDIO_BITRATE);
    225   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableVideoCaptureArguments, "MediaCapture",
    226                                                "VideoCodecUseArgs", false);
    227   SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.audioCaptureArguments, "MediaCapture", "AudioCodecArgs");
    228 
    229   connect(m_ui.mediaCaptureBackend, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
    230           &GraphicsSettingsWidget::onMediaCaptureBackendChanged);
    231   connect(m_ui.enableVideoCapture, &QCheckBox::checkStateChanged, this,
    232           &GraphicsSettingsWidget::onMediaCaptureVideoEnabledChanged);
    233   connect(m_ui.videoCaptureResolutionAuto, &QCheckBox::checkStateChanged, this,
    234           &GraphicsSettingsWidget::onMediaCaptureVideoAutoResolutionChanged);
    235   connect(m_ui.enableAudioCapture, &QCheckBox::checkStateChanged, this,
    236           &GraphicsSettingsWidget::onMediaCaptureAudioEnabledChanged);
    237 
    238   // Texture Replacements Tab
    239 
    240   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vramWriteReplacement, "TextureReplacements",
    241                                                "EnableVRAMWriteReplacements", false);
    242   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preloadTextureReplacements, "TextureReplacements",
    243                                                "PreloadTextures", false);
    244   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useOldMDECRoutines, "Hacks", "UseOldMDECRoutines", false);
    245   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vramWriteDumping, "TextureReplacements", "DumpVRAMWrites",
    246                                                false);
    247   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.setVRAMWriteAlphaChannel, "TextureReplacements",
    248                                                "DumpVRAMWriteForceAlphaChannel", true);
    249   SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.minDumpedVRAMWriteWidth, "TextureReplacements",
    250                                               "DumpVRAMWriteWidthThreshold",
    251                                               Settings::DEFAULT_VRAM_WRITE_DUMP_WIDTH_THRESHOLD);
    252   SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.minDumpedVRAMWriteHeight, "TextureReplacements",
    253                                               "DumpVRAMWriteHeightThreshold",
    254                                               Settings::DEFAULT_VRAM_WRITE_DUMP_HEIGHT_THRESHOLD);
    255 
    256   connect(m_ui.vramWriteReplacement, &QCheckBox::checkStateChanged, this,
    257           &GraphicsSettingsWidget::onEnableAnyTextureReplacementsChanged);
    258   connect(m_ui.vramWriteDumping, &QCheckBox::checkStateChanged, this,
    259           &GraphicsSettingsWidget::onEnableVRAMWriteDumpingChanged);
    260 
    261   // Debugging Tab
    262 
    263   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useDebugDevice, "GPU", "UseDebugDevice", false);
    264   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableShaderCache, "GPU", "DisableShaderCache", false);
    265   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDualSource, "GPU", "DisableDualSourceBlend", false);
    266   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableFramebufferFetch, "GPU", "DisableFramebufferFetch",
    267                                                false);
    268   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableTextureBuffers, "GPU", "DisableTextureBuffers", false);
    269   SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableTextureCopyToSelf, "GPU", "DisableTextureCopyToSelf",
    270                                                false);
    271 
    272   // Init all dependent options.
    273   updateRendererDependentOptions();
    274   onAspectRatioChanged();
    275   onDownsampleModeChanged();
    276   updateResolutionDependentOptions();
    277   onMediaCaptureBackendChanged();
    278   onMediaCaptureAudioEnabledChanged();
    279   onMediaCaptureVideoEnabledChanged();
    280   onEnableAnyTextureReplacementsChanged();
    281   onEnableVRAMWriteDumpingChanged();
    282   onShowDebugSettingsChanged(QtHost::ShouldShowDebugOptions());
    283 
    284   // Rendering Tab
    285 
    286   dialog->registerWidgetHelp(
    287     m_ui.renderer, tr("Renderer"), QString::fromUtf8(Settings::GetRendererDisplayName(Settings::DEFAULT_GPU_RENDERER)),
    288     tr("Chooses the backend to use for rendering the console/game visuals. <br>Depending on your system and hardware, "
    289        "Direct3D 11 and OpenGL hardware backends may be available. <br>The software renderer offers the best "
    290        "compatibility, but is the slowest and does not offer any enhancements."));
    291   dialog->registerWidgetHelp(
    292     m_ui.adapter, tr("Adapter"), tr("(Default)"),
    293     tr("If your system contains multiple GPUs or adapters, you can select which GPU you wish to use for the hardware "
    294        "renderers. <br>This option is only supported in Direct3D and Vulkan. OpenGL will always use the default "
    295        "device."));
    296   dialog->registerWidgetHelp(
    297     m_ui.resolutionScale, tr("Internal Resolution"), "1x",
    298     tr("Setting this beyond 1x will enhance the resolution of rendered 3D polygons and lines. Only applies "
    299        "to the hardware backends. <br>This option is usually safe, with most games looking fine at "
    300        "higher resolutions. Higher resolutions require a more powerful GPU."));
    301   dialog->registerWidgetHelp(
    302     m_ui.gpuDownsampleMode, tr("Down-Sampling"), tr("Disabled"),
    303     tr("Downsamples the rendered image prior to displaying it. Can improve overall image quality in mixed 2D/3D games, "
    304        "but should be disabled for pure 3D games."));
    305   dialog->registerWidgetHelp(m_ui.gpuDownsampleScale, tr("Down-Sampling Display Scale"), tr("1x"),
    306                              tr("Selects the resolution scale that will be applied to the final image. 1x will "
    307                                 "downsample to the original console resolution."));
    308   dialog->registerWidgetHelp(
    309     m_ui.textureFiltering, tr("Texture Filtering"),
    310     QString::fromUtf8(Settings::GetTextureFilterDisplayName(Settings::DEFAULT_GPU_TEXTURE_FILTER)),
    311     tr("Smooths out the blockiness of magnified textures on 3D objects by using filtering. <br>Will have a "
    312        "greater effect on higher resolution scales. <br>The JINC2 and especially xBR filtering modes are very "
    313        "demanding, and may not be worth the speed penalty."));
    314   dialog->registerWidgetHelp(
    315     m_ui.spriteTextureFiltering, tr("Sprite Texture Filtering"),
    316     QString::fromUtf8(Settings::GetTextureFilterDisplayName(Settings::DEFAULT_GPU_TEXTURE_FILTER)),
    317     tr("Smooths out the blockiness of magnified textures on 2D objects by using filtering. This filter only applies to "
    318        "sprites and other 2D elements, such as the HUD."));
    319   dialog->registerWidgetHelp(
    320     m_ui.displayAspectRatio, tr("Aspect Ratio"),
    321     QString::fromUtf8(Settings::GetDisplayAspectRatioDisplayName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO)),
    322     tr("Changes the aspect ratio used to display the console's output to the screen. The default is Auto (Game Native) "
    323        "which automatically adjusts the aspect ratio to match how a game would be shown on a typical TV of the era."));
    324   dialog->registerWidgetHelp(
    325     m_ui.displayDeinterlacing, tr("Deinterlacing"),
    326     QString::fromUtf8(Settings::GetDisplayDeinterlacingModeName(Settings::DEFAULT_DISPLAY_DEINTERLACING_MODE)),
    327     tr("Determines which algorithm is used to convert interlaced frames to progressive for display on your system. "
    328        "Generally, the \"Disable Interlacing\" enhancement provides better quality output, but some games require "
    329        "interlaced rendering."));
    330   dialog->registerWidgetHelp(
    331     m_ui.displayCropMode, tr("Crop"),
    332     QString::fromUtf8(Settings::GetDisplayCropModeDisplayName(Settings::DEFAULT_DISPLAY_CROP_MODE)),
    333     tr("Determines how much of the area typically not visible on a consumer TV set to crop/hide. Some games display "
    334        "content in the overscan area, or use it for screen effects. May not display correctly with the \"All Borders\" "
    335        "setting. \"Only Overscan\" offers a good compromise between stability and hiding black borders."));
    336   dialog->registerWidgetHelp(
    337     m_ui.displayScaling, tr("Scaling"), tr("Bilinear (Smooth)"),
    338     tr("Determines how the emulated console's output is upscaled or downscaled to your monitor's resolution."));
    339   dialog->registerWidgetHelp(
    340     m_ui.trueColor, tr("True Color Rendering"), tr("Checked"),
    341     tr("Forces the precision of colours output to the console's framebuffer to use the full 8 bits of precision per "
    342        "channel. This produces nicer looking gradients at the cost of making some colours look slightly different. "
    343        "Disabling the option also enables dithering, which makes the transition between colours less sharp by applying "
    344        "a pattern around those pixels. Most games are compatible with this option, but there is a number which aren't "
    345        "and will have broken effects with it enabled."));
    346   dialog->registerWidgetHelp(
    347     m_ui.widescreenHack, tr("Widescreen Rendering"), tr("Unchecked"),
    348     tr("Scales vertex positions in screen-space to a widescreen aspect ratio, essentially "
    349        "increasing the field of view from 4:3 to the chosen display aspect ratio in 3D games. <b><u>May not be "
    350        "compatible with all games.</u></b>"));
    351   dialog->registerWidgetHelp(m_ui.pgxpEnable, tr("PGXP Geometry Correction"), tr("Unchecked"),
    352                              tr("Reduces \"wobbly\" polygons and \"warping\" textures that are common in PS1 games. "
    353                                 "<strong>May not be compatible with all games.</strong>"));
    354   dialog->registerWidgetHelp(
    355     m_ui.pgxpDepthBuffer, tr("PGXP Depth Buffer"), tr("Unchecked"),
    356     tr("Attempts to reduce polygon Z-fighting by testing pixels against the depth values from PGXP. Low compatibility, "
    357        "but can work well in some games. Other games may need a threshold adjustment."));
    358   dialog->registerWidgetHelp(
    359     m_ui.force43For24Bit, tr("Force 4:3 For FMVs"), tr("Unchecked"),
    360     tr("Switches back to 4:3 display aspect ratio when displaying 24-bit content, usually FMVs."));
    361   dialog->registerWidgetHelp(m_ui.chromaSmoothingFor24Bit, tr("FMV Chroma Smoothing"), tr("Unchecked"),
    362                              tr("Smooths out blockyness between colour transitions in 24-bit content, usually FMVs."));
    363   dialog->registerWidgetHelp(
    364     m_ui.disableInterlacing, tr("Disable Interlacing"), tr("Checked"),
    365     tr(
    366       "Forces the rendering and display of frames to progressive mode. <br>This removes the \"combing\" effect seen in "
    367       "480i games by rendering them in 480p. Usually safe to enable.<br><b><u>May not be compatible with all "
    368       "games.</u></b>"));
    369   dialog->registerWidgetHelp(
    370     m_ui.forceNTSCTimings, tr("Force NTSC Timings"), tr("Unchecked"),
    371     tr("Uses NTSC frame timings when the console is in PAL mode, forcing PAL games to run at 60hz. <br>For most games "
    372        "which have a speed tied to the framerate, this will result in the game running approximately 17% faster. "
    373        "<br>For variable frame rate games, it may not affect the speed."));
    374 
    375   // Advanced Tab
    376 
    377   dialog->registerWidgetHelp(m_ui.fullscreenMode, tr("Fullscreen Mode"), tr("Borderless Fullscreen"),
    378                              tr("Chooses the fullscreen resolution and frequency."));
    379   dialog->registerWidgetHelp(m_ui.exclusiveFullscreenControl, tr("Exclusive Fullscreen Control"), tr("Automatic"),
    380                              tr("Controls whether exclusive fullscreen can be utilized by Vulkan drivers."));
    381   dialog->registerWidgetHelp(
    382     m_ui.displayAlignment, tr("Position"),
    383     QString::fromUtf8(Settings::GetDisplayAlignmentDisplayName(Settings::DEFAULT_DISPLAY_ALIGNMENT)),
    384     tr("Determines the position on the screen when black borders must be added."));
    385   dialog->registerWidgetHelp(m_ui.gpuThread, tr("Threaded Rendering"), tr("Checked"),
    386                              tr("Uses a second thread for drawing graphics. Currently only available for the software "
    387                                 "renderer, but can provide a significant speed improvement, and is safe to use."));
    388   dialog->registerWidgetHelp(m_ui.threadedPresentation, tr("Threaded Presentation"), tr("Checked"),
    389                              tr("Presents frames on a background thread when fast forwarding or vsync is disabled. "
    390                                 "This can measurably improve performance in the Vulkan renderer."));
    391   dialog->registerWidgetHelp(
    392     m_ui.disableMailboxPresentation, tr("Disable Mailbox Presentation"), tr("Unchecked"),
    393     tr("Forces the use of FIFO over Mailbox presentation, i.e. double buffering instead of triple buffering. "
    394        "Usually results in worse frame pacing."));
    395   dialog->registerWidgetHelp(
    396     m_ui.stretchDisplayVertically, tr("Stretch Vertically"), tr("Unchecked"),
    397     tr("Prefers stretching the display vertically instead of horizontally, when applying the display aspect ratio."));
    398 #ifdef _WIN32
    399   dialog->registerWidgetHelp(m_ui.blitSwapChain, tr("Use Blit Swap Chain"), tr("Unchecked"),
    400                              tr("Uses a blit presentation model instead of flipping when using the Direct3D 11 "
    401                                 "renderer. This usually results in slower performance, but may be required for some "
    402                                 "streaming applications, or to uncap framerates on some systems."));
    403 #endif
    404 
    405   dialog->registerWidgetHelp(m_ui.gpuLineDetectMode, tr("Line Detection"),
    406                              QString::fromUtf8(Settings::GetLineDetectModeName(Settings::DEFAULT_GPU_LINE_DETECT_MODE)),
    407                              tr("Attempts to detect one pixel high/wide lines that rely on non-upscaled rasterization "
    408                                 "behavior, filling in gaps introduced by upscaling."));
    409   dialog->registerWidgetHelp(
    410     m_ui.msaaMode, tr("Multi-Sampling"), tr("Disabled"),
    411     tr("Uses multi-sampled anti-aliasing when rendering 3D polygons. Can improve visuals with a lower performance "
    412        "requirement compared to upscaling, <strong>but often introduces rendering errors.</strong>"));
    413   dialog->registerWidgetHelp(
    414     m_ui.debanding, tr("True Color Debanding"), tr("Unchecked"),
    415     tr("Applies modern dithering techniques to further smooth out gradients when true color is enabled. "
    416        "This debanding is performed during rendering (as opposed to a post-processing step), which allows it to be "
    417        "fast while preserving detail. "
    418        "Debanding increases the file size of screenshots due to the subtle dithering pattern present in screenshots."));
    419   dialog->registerWidgetHelp(
    420     m_ui.scaledDithering, tr("Scaled Dithering"), tr("Checked"),
    421     tr("Scales the dither pattern to the resolution scale of the emulated GPU. This makes the dither pattern much less "
    422        "obvious at higher resolutions. Usually safe to enable."));
    423   dialog->registerWidgetHelp(
    424     m_ui.useSoftwareRendererForReadbacks, tr("Software Renderer Readbacks"), tr("Unchecked"),
    425     tr("Runs the software renderer in parallel for VRAM readbacks. On some systems, this may result in greater "
    426        "performance when using graphical enhancements with the hardware renderer."));
    427   dialog->registerWidgetHelp(
    428     m_ui.forceRoundedTexcoords, tr("Round Upscaled Texture Coordinates"), tr("Unchecked"),
    429     tr("Rounds texture coordinates instead of flooring when upscaling. Can fix misaligned textures in some games, but "
    430        "break others, and is incompatible with texture filtering."));
    431   dialog->registerWidgetHelp(
    432     m_ui.accurateBlending, tr("Accurate Blending"), tr("Unchecked"),
    433     tr("Forces blending to be done in the shader at 16-bit precision, when not using true color. Very few games "
    434        "actually require this, and there is a <strong>non-trivial</strong> performance cost."));
    435 
    436   // PGXP Tab
    437 
    438   dialog->registerWidgetHelp(
    439     m_ui.pgxpGeometryTolerance, tr("Geometry Tolerance"), tr("-1.00px (Disabled)"),
    440     tr("Discards precise geometry when it is found to be offset past the specified threshold. This can help with games "
    441        "that have vertices significantly moved by PGXP, but is still a hack/workaround."));
    442   dialog->registerWidgetHelp(m_ui.pgxpDepthClearThreshold, tr("Depth Clear Threshold"),
    443                              QStringLiteral("%1").arg(Settings::DEFAULT_GPU_PGXP_DEPTH_THRESHOLD),
    444                              tr("Determines the increase in depth that will result in the depth buffer being cleared. "
    445                                 "Can help with depth issues in some games, but is still a hack/workaround."));
    446   dialog->registerWidgetHelp(m_ui.pgxpTextureCorrection, tr("Perspective Correct Textures"), tr("Checked"),
    447                              tr("Uses perspective-correct interpolation for texture coordinates, straightening out "
    448                                 "warped textures. Requires geometry correction enabled."));
    449   dialog->registerWidgetHelp(
    450     m_ui.pgxpColorCorrection, tr("Perspective Correct Colors"), tr("Unchecked"),
    451     tr("Uses perspective-correct interpolation for vertex colors, which can improve visuals in some games, but cause "
    452        "rendering errors in others. Requires geometry correction enabled."));
    453   dialog->registerWidgetHelp(m_ui.pgxpCulling, tr("Culling Correction"), tr("Checked"),
    454                              tr("Increases the precision of polygon culling, reducing the number of holes in geometry. "
    455                                 "Requires geometry correction enabled."));
    456   dialog->registerWidgetHelp(
    457     m_ui.pgxpPreserveProjPrecision, tr("Preserve Projection Precision"), tr("Unchecked"),
    458     tr("Adds additional precision to PGXP data post-projection. May improve visuals in some games."));
    459   dialog->registerWidgetHelp(m_ui.pgxpCPU, tr("CPU Mode"), tr("Unchecked"),
    460                              tr("Uses PGXP for all instructions, not just memory operations. Required for PGXP to "
    461                                 "correct wobble in some games, but has a high performance cost."));
    462   dialog->registerWidgetHelp(
    463     m_ui.pgxpVertexCache, tr("Vertex Cache"), tr("Unchecked"),
    464     tr("Uses screen-space vertex positions to obtain precise positions, instead of tracking memory accesses. Can "
    465        "provide PGXP compatibility for some games, but <strong>generally provides no benefit.</strong>"));
    466   dialog->registerWidgetHelp(m_ui.pgxpDisableOn2DPolygons, tr("Disable on 2D Polygons"), tr("Unchecked"),
    467                              tr("Uses native resolution coordinates for 2D polygons, instead of precise coordinates. "
    468                                 "Can fix misaligned UI in some games, but otherwise should be left disabled. The game "
    469                                 "database will enable this automatically when needed."));
    470 
    471   // OSD Tab
    472 
    473   dialog->registerWidgetHelp(
    474     m_ui.osdScale, tr("OSD Scale"), tr("100%"),
    475     tr("Changes the size at which on-screen elements, including status and messages are displayed."));
    476   dialog->registerWidgetHelp(m_ui.showOSDMessages, tr("Show OSD Messages"), tr("Checked"),
    477                              tr("Shows on-screen-display messages when events occur such as save states being "
    478                                 "created/loaded, screenshots being taken, etc."));
    479   dialog->registerWidgetHelp(m_ui.showResolution, tr("Show Resolution"), tr("Unchecked"),
    480                              tr("Shows the resolution of the game in the top-right corner of the display."));
    481   dialog->registerWidgetHelp(
    482     m_ui.showSpeed, tr("Show Emulation Speed"), tr("Unchecked"),
    483     tr("Shows the current emulation speed of the system in the top-right corner of the display as a percentage."));
    484   dialog->registerWidgetHelp(m_ui.showFPS, tr("Show FPS"), tr("Unchecked"),
    485                              tr("Shows the internal frame rate of the game in the top-right corner of the display."));
    486   dialog->registerWidgetHelp(
    487     m_ui.showCPU, tr("Show CPU Usage"), tr("Unchecked"),
    488     tr("Shows the host's CPU usage based on threads in the top-right corner of the display. This does not display the "
    489        "emulated system CPU's usage. If a value close to 100% is being displayed, this means your host's CPU is likely "
    490        "the bottleneck. In this case, you should reduce enhancement-related settings such as overclocking."));
    491   dialog->registerWidgetHelp(m_ui.showGPU, tr("Show GPU Usage"), tr("Unchecked"),
    492                              tr("Shows the host's GPU usage in the top-right corner of the display."));
    493   dialog->registerWidgetHelp(m_ui.showGPUStatistics, tr("Show GPU Statistics"), tr("Unchecked"),
    494                              tr("Shows information about the emulated GPU in the top-right corner of the display."));
    495   dialog->registerWidgetHelp(
    496     m_ui.showLatencyStatistics, tr("Show Latency Statistics"), tr("Unchecked"),
    497     tr("Shows information about input and audio latency in the top-right corner of the display."));
    498   dialog->registerWidgetHelp(
    499     m_ui.showFrameTimes, tr("Show Frame Times"), tr("Unchecked"),
    500     tr("Shows the history of frame rendering times as a graph in the top-right corner of the display."));
    501   dialog->registerWidgetHelp(
    502     m_ui.showInput, tr("Show Controller Input"), tr("Unchecked"),
    503     tr("Shows the current controller state of the system in the bottom-left corner of the display."));
    504   dialog->registerWidgetHelp(m_ui.showSettings, tr("Show Settings"), tr("Unchecked"),
    505                              tr("Shows a summary of current settings in the bottom-right corner of the display."));
    506   dialog->registerWidgetHelp(m_ui.showStatusIndicators, tr("Show Status Indicators"), tr("Checked"),
    507                              tr("Shows indicators on screen when the system is not running in its \"normal\" state. "
    508                                 "For example, fast forwarding, or being paused."));
    509 
    510   // Capture Tab
    511 
    512   dialog->registerWidgetHelp(m_ui.screenshotSize, tr("Screenshot Size"), tr("Screen Resolution"),
    513                              tr("Determines the resolution at which screenshots will be saved. Internal resolutions "
    514                                 "preserve more detail at the cost of file size."));
    515   dialog->registerWidgetHelp(
    516     m_ui.screenshotFormat, tr("Screenshot Format"), tr("PNG"),
    517     tr("Selects the format which will be used to save screenshots. JPEG produces smaller files, but loses detail."));
    518   dialog->registerWidgetHelp(m_ui.screenshotQuality, tr("Screenshot Quality"),
    519                              QStringLiteral("%1%").arg(Settings::DEFAULT_DISPLAY_SCREENSHOT_QUALITY),
    520                              tr("Selects the quality at which screenshots will be compressed. Higher values preserve "
    521                                 "more detail for JPEG, and reduce file size for PNG."));
    522   dialog->registerWidgetHelp(
    523     m_ui.mediaCaptureBackend, tr("Backend"),
    524     QString::fromUtf8(MediaCapture::GetBackendDisplayName(Settings::DEFAULT_MEDIA_CAPTURE_BACKEND)),
    525     tr("Selects the framework that is used to encode video/audio."));
    526   dialog->registerWidgetHelp(m_ui.captureContainer, tr("Container"), tr("MP4"),
    527                              tr("Determines the file format used to contain the captured audio/video"));
    528   dialog->registerWidgetHelp(
    529     m_ui.videoCaptureCodec, tr("Video Codec"), tr("Default"),
    530     tr("Selects which Video Codec to be used for Video Capture. <b>If unsure, leave it on default.<b>"));
    531   dialog->registerWidgetHelp(m_ui.videoCaptureBitrate, tr("Video Bitrate"), tr("6000 kbps"),
    532                              tr("Sets the video bitrate to be used. Larger bitrate generally yields better video "
    533                                 "quality at the cost of larger resulting file size."));
    534   dialog->registerWidgetHelp(
    535     m_ui.videoCaptureResolutionAuto, tr("Automatic Resolution"), tr("Unchecked"),
    536     tr("When checked, the video capture resolution will follows the internal resolution of the running "
    537        "game. <b>Be careful when using this setting especially when you are upscaling, as higher internal "
    538        "resolutions (above 4x) can cause system slowdown.</b>"));
    539   dialog->registerWidgetHelp(m_ui.enableVideoCaptureArguments, tr("Enable Extra Video Arguments"), tr("Unchecked"),
    540                              tr("Allows you to pass arguments to the selected video codec."));
    541   dialog->registerWidgetHelp(
    542     m_ui.videoCaptureArguments, tr("Extra Video Arguments"), tr("Empty"),
    543     tr("Parameters passed to the selected video codec.<br><b>You must use '=' to separate key from value and ':' to "
    544        "separate two pairs from each other.</b><br>For example: \"crf = 21 : preset = veryfast\""));
    545   dialog->registerWidgetHelp(
    546     m_ui.audioCaptureCodec, tr("Audio Codec"), tr("Default"),
    547     tr("Selects which Audio Codec to be used for Video Capture. <b>If unsure, leave it on default.<b>"));
    548   dialog->registerWidgetHelp(m_ui.audioCaptureBitrate, tr("Audio Bitrate"), tr("160 kbps"),
    549                              tr("Sets the audio bitrate to be used."));
    550   dialog->registerWidgetHelp(m_ui.enableAudioCaptureArguments, tr("Enable Extra Audio Arguments"), tr("Unchecked"),
    551                              tr("Allows you to pass arguments to the selected audio codec."));
    552   dialog->registerWidgetHelp(
    553     m_ui.audioCaptureArguments, tr("Extra Audio Arguments"), tr("Empty"),
    554     tr("Parameters passed to the selected audio codec.<br><b>You must use '=' to separate key from value and ':' to "
    555        "separate two pairs from each other.</b><br>For example: \"compression_level = 4 : joint_stereo = 1\""));
    556 
    557   // Texture Replacements Tab
    558 
    559   dialog->registerWidgetHelp(m_ui.vramWriteReplacement, tr("Enable VRAM Write Replacement"), tr("Unchecked"),
    560                              tr("Enables the replacement of background textures in supported games. <strong>This is "
    561                                 "not general texture replacement.</strong>"));
    562   dialog->registerWidgetHelp(m_ui.preloadTextureReplacements, tr("Preload Texture Replacements"), tr("Unchecked"),
    563                              tr("Loads all replacement texture to RAM, reducing stuttering at runtime."));
    564   dialog->registerWidgetHelp(m_ui.useOldMDECRoutines, tr("Use Old MDEC Routines"), tr("Unchecked"),
    565                              tr("Enables the older, less accurate MDEC decoding routines. May be required for old "
    566                                 "replacement backgrounds to match/load."));
    567   dialog->registerWidgetHelp(m_ui.setVRAMWriteAlphaChannel, tr("Set Alpha Channel"), tr("Checked"),
    568                              tr("Clears the mask/transparency bit in VRAM write dumps."));
    569   dialog->registerWidgetHelp(m_ui.vramWriteDumping, tr("Enable VRAM Write Dumping"), tr("Unchecked"),
    570                              tr("Writes backgrounds that can be replaced to the dump directory."));
    571   dialog->registerWidgetHelp(m_ui.minDumpedVRAMWriteWidth, tr("Dump Size Threshold"), tr("128px"),
    572                              tr("Determines the threshold that triggers a VRAM write to be dumped."));
    573   dialog->registerWidgetHelp(m_ui.minDumpedVRAMWriteHeight, tr("Dump Size Threshold"), tr("128px"),
    574                              tr("Determines the threshold that triggers a VRAM write to be dumped."));
    575 
    576   // Debugging Tab
    577 
    578   dialog->registerWidgetHelp(m_ui.gpuWireframeMode, tr("Wireframe Mode"), tr("Disabled"),
    579                              tr("Draws a wireframe outline of the triangles rendered by the console's GPU, either as a "
    580                                 "replacement or an overlay."));
    581 
    582   dialog->registerWidgetHelp(
    583     m_ui.useDebugDevice, tr("Use Debug Device"), tr("Unchecked"),
    584     tr("Enable debugging when supported by the host's renderer API. <strong>Only for developer use.</strong>"));
    585   dialog->registerWidgetHelp(
    586     m_ui.disableShaderCache, tr("Disable Shader Cache"), tr("Unchecked"),
    587     tr("Forces shaders to be compiled for every run of the program. <strong>Only for developer use.</strong>"));
    588   dialog->registerWidgetHelp(m_ui.disableDualSource, tr("Disable Dual-Source Blending"), tr("Unchecked"),
    589                              tr("Prevents dual-source blending from being used. Useful for testing broken graphics "
    590                                 "drivers. <strong>Only for developer use.</strong>"));
    591   dialog->registerWidgetHelp(m_ui.disableFramebufferFetch, tr("Disable Framebuffer Fetch"), tr("Unchecked"),
    592                              tr("Prevents the framebuffer fetch extensions from being used. Useful for testing broken "
    593                                 "graphics drivers. <strong>Only for developer use.</strong>"));
    594   dialog->registerWidgetHelp(
    595     m_ui.disableTextureBuffers, tr("Disable Texture Buffers"), tr("Unchecked"),
    596     tr("Forces VRAM updates through texture updates, instead of texture buffers and draws. Useful for testing broken "
    597        "graphics drivers. <strong>Only for developer use.</strong>"));
    598   dialog->registerWidgetHelp(m_ui.disableTextureCopyToSelf, tr("Disable Texture Copies To Self"), tr("Unchecked"),
    599                              tr("Disables the use of self-copy updates for the VRAM texture. Useful for testing broken "
    600                                 "graphics drivers. <strong>Only for developer use.</strong>"));
    601   dialog->registerWidgetHelp(m_ui.disableMemoryImport, tr("Disable Memory Import"), tr("Unchecked"),
    602                              tr("Disables the use of host memory importing. Useful for testing broken graphics "
    603                                 "drivers. <strong>Only for developer use.</strong>"));
    604   dialog->registerWidgetHelp(m_ui.disableRasterOrderViews, tr("Disable Rasterizer Order Views"), tr("Unchecked"),
    605                              tr("Disables the use of rasterizer order views. Useful for testing broken graphics "
    606                                 "drivers. <strong>Only for developer use.</strong>"));
    607 }
    608 
    609 GraphicsSettingsWidget::~GraphicsSettingsWidget() = default;
    610 
    611 void GraphicsSettingsWidget::setupAdditionalUi()
    612 {
    613   // Rendering Tab
    614 
    615   for (u32 i = 0; i < static_cast<u32>(GPURenderer::Count); i++)
    616   {
    617     m_ui.renderer->addItem(QString::fromUtf8(Settings::GetRendererDisplayName(static_cast<GPURenderer>(i))));
    618   }
    619 
    620   for (u32 i = 0; i < static_cast<u32>(GPUTextureFilter::Count); i++)
    621   {
    622     m_ui.textureFiltering->addItem(
    623       QString::fromUtf8(Settings::GetTextureFilterDisplayName(static_cast<GPUTextureFilter>(i))));
    624     m_ui.spriteTextureFiltering->addItem(
    625       QString::fromUtf8(Settings::GetTextureFilterDisplayName(static_cast<GPUTextureFilter>(i))));
    626   }
    627 
    628   for (u32 i = 0; i < static_cast<u32>(GPUDownsampleMode::Count); i++)
    629   {
    630     m_ui.gpuDownsampleMode->addItem(
    631       QString::fromUtf8(Settings::GetDownsampleModeDisplayName(static_cast<GPUDownsampleMode>(i))));
    632   }
    633 
    634   for (u32 i = 0; i < static_cast<u32>(DisplayAspectRatio::Count); i++)
    635   {
    636     m_ui.displayAspectRatio->addItem(
    637       QString::fromUtf8(Settings::GetDisplayAspectRatioDisplayName(static_cast<DisplayAspectRatio>(i))));
    638   }
    639 
    640   for (u32 i = 0; i < static_cast<u32>(DisplayDeinterlacingMode::Count); i++)
    641   {
    642     m_ui.displayDeinterlacing->addItem(
    643       QString::fromUtf8(Settings::GetDisplayDeinterlacingModeDisplayName(static_cast<DisplayDeinterlacingMode>(i))));
    644   }
    645 
    646   for (u32 i = 0; i < static_cast<u32>(DisplayCropMode::Count); i++)
    647   {
    648     m_ui.displayCropMode->addItem(
    649       QString::fromUtf8(Settings::GetDisplayCropModeDisplayName(static_cast<DisplayCropMode>(i))));
    650   }
    651 
    652   for (u32 i = 0; i < static_cast<u32>(DisplayScalingMode::Count); i++)
    653   {
    654     m_ui.displayScaling->addItem(
    655       QString::fromUtf8(Settings::GetDisplayScalingDisplayName(static_cast<DisplayScalingMode>(i))));
    656   }
    657 
    658   // Advanced Tab
    659 
    660   for (u32 i = 0; i < static_cast<u32>(DisplayExclusiveFullscreenControl::Count); i++)
    661   {
    662     m_ui.exclusiveFullscreenControl->addItem(QString::fromUtf8(
    663       Settings::GetDisplayExclusiveFullscreenControlDisplayName(static_cast<DisplayExclusiveFullscreenControl>(i))));
    664   }
    665 
    666   for (u32 i = 0; i < static_cast<u32>(DisplayAlignment::Count); i++)
    667   {
    668     m_ui.displayAlignment->addItem(
    669       QString::fromUtf8(Settings::GetDisplayAlignmentDisplayName(static_cast<DisplayAlignment>(i))));
    670   }
    671 
    672   for (u32 i = 0; i < static_cast<u32>(DisplayRotation::Count); i++)
    673   {
    674     m_ui.displayRotation->addItem(
    675       QString::fromUtf8(Settings::GetDisplayRotationDisplayName(static_cast<DisplayRotation>(i))));
    676   }
    677 
    678   for (u32 i = 0; i < static_cast<u32>(GPULineDetectMode::Count); i++)
    679   {
    680     m_ui.gpuLineDetectMode->addItem(
    681       QString::fromUtf8(Settings::GetLineDetectModeDisplayName(static_cast<GPULineDetectMode>(i))));
    682   }
    683 
    684   // Capture Tab
    685 
    686   for (u32 i = 0; i < static_cast<u32>(DisplayScreenshotMode::Count); i++)
    687   {
    688     m_ui.screenshotSize->addItem(
    689       QString::fromUtf8(Settings::GetDisplayScreenshotModeDisplayName(static_cast<DisplayScreenshotMode>(i))));
    690   }
    691 
    692   for (u32 i = 0; i < static_cast<u32>(DisplayScreenshotFormat::Count); i++)
    693   {
    694     m_ui.screenshotFormat->addItem(
    695       QString::fromUtf8(Settings::GetDisplayScreenshotFormatDisplayName(static_cast<DisplayScreenshotFormat>(i))));
    696   }
    697 
    698   for (u32 i = 0; i < static_cast<u32>(MediaCaptureBackend::MaxCount); i++)
    699   {
    700     m_ui.mediaCaptureBackend->addItem(
    701       QString::fromUtf8(MediaCapture::GetBackendDisplayName(static_cast<MediaCaptureBackend>(i))));
    702   }
    703 
    704   // Debugging Tab
    705 
    706   for (u32 i = 0; i < static_cast<u32>(GPUWireframeMode::Count); i++)
    707   {
    708     m_ui.gpuWireframeMode->addItem(
    709       QString::fromUtf8(Settings::GetGPUWireframeModeDisplayName(static_cast<GPUWireframeMode>(i))));
    710   }
    711 }
    712 
    713 void GraphicsSettingsWidget::removePlatformSpecificUi()
    714 {
    715 #ifndef _WIN32
    716   m_ui.advancedDisplayOptionsLayout->removeWidget(m_ui.blitSwapChain);
    717   delete m_ui.blitSwapChain;
    718   m_ui.blitSwapChain = nullptr;
    719 #endif
    720 }
    721 
    722 GPURenderer GraphicsSettingsWidget::getEffectiveRenderer() const
    723 {
    724   return Settings::ParseRendererName(
    725            m_dialog
    726              ->getEffectiveStringValue("GPU", "Renderer", Settings::GetRendererName(Settings::DEFAULT_GPU_RENDERER))
    727              .c_str())
    728     .value_or(Settings::DEFAULT_GPU_RENDERER);
    729 }
    730 
    731 bool GraphicsSettingsWidget::effectiveRendererIsHardware() const
    732 {
    733   return (getEffectiveRenderer() != GPURenderer::Software);
    734 }
    735 
    736 void GraphicsSettingsWidget::onShowDebugSettingsChanged(bool enabled)
    737 {
    738   m_ui.tabs->setTabVisible(TAB_INDEX_DEBUGGING, enabled);
    739 }
    740 
    741 void GraphicsSettingsWidget::updateRendererDependentOptions()
    742 {
    743   const GPURenderer renderer = getEffectiveRenderer();
    744   const RenderAPI render_api = Settings::GetRenderAPIForRenderer(renderer);
    745   const bool is_hardware = (renderer != GPURenderer::Software);
    746 
    747   m_ui.resolutionScale->setEnabled(is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::DisableUpscaling));
    748   m_ui.resolutionScaleLabel->setEnabled(is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::DisableUpscaling));
    749   m_ui.msaaMode->setEnabled(is_hardware);
    750   m_ui.msaaModeLabel->setEnabled(is_hardware);
    751   m_ui.textureFiltering->setEnabled(is_hardware &&
    752                                     !m_dialog->hasGameTrait(GameDatabase::Trait::DisableTextureFiltering));
    753   m_ui.textureFilteringLabel->setEnabled(is_hardware &&
    754                                          !m_dialog->hasGameTrait(GameDatabase::Trait::DisableTextureFiltering));
    755   m_ui.gpuDownsampleLabel->setEnabled(is_hardware);
    756   m_ui.gpuDownsampleMode->setEnabled(is_hardware);
    757   m_ui.gpuDownsampleScale->setEnabled(is_hardware);
    758   m_ui.trueColor->setEnabled(is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::DisableTrueColor));
    759   m_ui.pgxpEnable->setEnabled(is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXP));
    760 
    761   m_ui.gpuLineDetectMode->setEnabled(is_hardware);
    762   m_ui.gpuLineDetectModeLabel->setEnabled(is_hardware);
    763   m_ui.gpuWireframeMode->setEnabled(is_hardware);
    764   m_ui.gpuWireframeModeLabel->setEnabled(is_hardware);
    765   m_ui.debanding->setEnabled(is_hardware);
    766   m_ui.scaledDithering->setEnabled(is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::DisableScaledDithering));
    767   m_ui.useSoftwareRendererForReadbacks->setEnabled(is_hardware);
    768   m_ui.forceRoundedTexcoords->setEnabled(is_hardware);
    769   m_ui.accurateBlending->setEnabled(is_hardware);
    770 
    771   m_ui.tabs->setTabEnabled(TAB_INDEX_TEXTURE_REPLACEMENTS, is_hardware);
    772 
    773 #ifdef _WIN32
    774   m_ui.blitSwapChain->setEnabled(render_api == RenderAPI::D3D11);
    775 #endif
    776 
    777   m_ui.gpuThread->setEnabled(!is_hardware);
    778   m_ui.threadedPresentation->setEnabled(render_api == RenderAPI::Vulkan);
    779 
    780   m_ui.exclusiveFullscreenLabel->setEnabled(render_api == RenderAPI::D3D11 || render_api == RenderAPI::D3D12 ||
    781                                             render_api == RenderAPI::Vulkan);
    782   m_ui.exclusiveFullscreenControl->setEnabled(render_api == RenderAPI::Vulkan);
    783 
    784   populateGPUAdaptersAndResolutions(render_api);
    785   updatePGXPSettingsEnabled();
    786 }
    787 
    788 void GraphicsSettingsWidget::populateGPUAdaptersAndResolutions(RenderAPI render_api)
    789 {
    790   // Don't re-query, it's expensive.
    791   if (m_adapters_render_api != render_api)
    792   {
    793     m_adapters_render_api = render_api;
    794     m_adapters = GPUDevice::GetAdapterListForAPI(render_api);
    795   }
    796 
    797   const GPUDevice::AdapterInfo* current_adapter = nullptr;
    798   SettingsInterface* const sif = m_dialog->getSettingsInterface();
    799 
    800   {
    801     m_ui.adapter->disconnect();
    802     m_ui.adapter->clear();
    803     m_ui.adapter->addItem(tr("(Default)"), QVariant(QString()));
    804 
    805     const std::string current_adapter_name = m_dialog->getEffectiveStringValue("GPU", "Adapter", "");
    806     for (const GPUDevice::AdapterInfo& adapter : m_adapters)
    807     {
    808       const QString qadaptername = QString::fromStdString(adapter.name);
    809       m_ui.adapter->addItem(qadaptername, QVariant(qadaptername));
    810       if (adapter.name == current_adapter_name)
    811         current_adapter = &adapter;
    812     }
    813 
    814     // default adapter
    815     if (!m_adapters.empty() && current_adapter_name.empty())
    816       current_adapter = &m_adapters.front();
    817 
    818     // disable it if we don't have a choice
    819     m_ui.adapter->setEnabled(!m_adapters.empty());
    820     SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.adapter, "GPU", "Adapter");
    821     connect(m_ui.adapter, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
    822             &GraphicsSettingsWidget::updateRendererDependentOptions);
    823   }
    824 
    825   {
    826     m_ui.fullscreenMode->disconnect();
    827     m_ui.fullscreenMode->clear();
    828 
    829     m_ui.fullscreenMode->addItem(tr("Borderless Fullscreen"), QVariant(QString()));
    830     if (current_adapter)
    831     {
    832       for (const std::string& mode_name : current_adapter->fullscreen_modes)
    833       {
    834         const QString qmodename = QString::fromStdString(mode_name);
    835         m_ui.fullscreenMode->addItem(qmodename, QVariant(qmodename));
    836       }
    837     }
    838 
    839     // disable it if we don't have a choice
    840     m_ui.fullscreenMode->setEnabled(current_adapter && !current_adapter->fullscreen_modes.empty());
    841     SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.fullscreenMode, "GPU", "FullscreenMode");
    842   }
    843 
    844   if (!m_dialog->hasGameTrait(GameDatabase::Trait::DisableUpscaling))
    845   {
    846     m_ui.resolutionScale->disconnect();
    847     m_ui.resolutionScale->clear();
    848 
    849     static constexpr const std::pair<int, const char*> templates[] = {
    850       {0, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Automatic (Based on Window Size)")},
    851       {1, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "1x Native (Default)")},
    852       {3, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "3x Native (for 720p)")},
    853       {5, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "5x Native (for 1080p)")},
    854       {6, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "6x Native (for 1440p)")},
    855       {9, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "9x Native (for 4K)")},
    856     };
    857 
    858     const int max_scale =
    859       static_cast<int>(current_adapter ? std::max<u32>(current_adapter->max_texture_size / 1024, 1) : 16);
    860     for (int scale = 0; scale <= max_scale; scale++)
    861     {
    862       const auto it = std::find_if(std::begin(templates), std::end(templates),
    863                                    [&scale](const std::pair<int, const char*>& it) { return scale == it.first; });
    864       m_ui.resolutionScale->addItem((it != std::end(templates)) ?
    865                                       qApp->translate("GraphicsSettingsWidget", it->second) :
    866                                       qApp->translate("GraphicsSettingsWidget", "%1x Native").arg(scale));
    867     }
    868 
    869     SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.resolutionScale, "GPU", "ResolutionScale", 1);
    870     connect(m_ui.resolutionScale, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
    871             &GraphicsSettingsWidget::updateResolutionDependentOptions);
    872   }
    873 
    874   {
    875     m_ui.msaaMode->disconnect();
    876     m_ui.msaaMode->clear();
    877 
    878     if (m_dialog->isPerGameSettings())
    879       m_ui.msaaMode->addItem(tr("Use Global Setting"));
    880 
    881     const u32 max_multisamples = current_adapter ? current_adapter->max_multisamples : 8;
    882     m_ui.msaaMode->addItem(tr("Disabled"), GetMSAAModeValue(1, false));
    883     for (uint i = 2; i <= max_multisamples; i *= 2)
    884       m_ui.msaaMode->addItem(tr("%1x MSAA").arg(i), GetMSAAModeValue(i, false));
    885     for (uint i = 2; i <= max_multisamples; i *= 2)
    886       m_ui.msaaMode->addItem(tr("%1x SSAA").arg(i), GetMSAAModeValue(i, true));
    887 
    888     if (!m_dialog->isPerGameSettings() || (m_dialog->containsSettingValue("GPU", "Multisamples") ||
    889                                            m_dialog->containsSettingValue("GPU", "PerSampleShading")))
    890     {
    891       const QVariant current_msaa_mode(
    892         GetMSAAModeValue(static_cast<uint>(m_dialog->getEffectiveIntValue("GPU", "Multisamples", 1)),
    893                          m_dialog->getEffectiveBoolValue("GPU", "PerSampleShading", false)));
    894       const int current_msaa_index = m_ui.msaaMode->findData(current_msaa_mode);
    895       if (current_msaa_index >= 0)
    896         m_ui.msaaMode->setCurrentIndex(current_msaa_index);
    897     }
    898     else
    899     {
    900       m_ui.msaaMode->setCurrentIndex(0);
    901     }
    902     connect(m_ui.msaaMode, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this]() {
    903       const int index = m_ui.msaaMode->currentIndex();
    904       if (m_dialog->isPerGameSettings() && index == 0)
    905       {
    906         m_dialog->removeSettingValue("GPU", "Multisamples");
    907         m_dialog->removeSettingValue("GPU", "PerSampleShading");
    908       }
    909       else
    910       {
    911         uint multisamples;
    912         bool ssaa;
    913         DecodeMSAAModeValue(m_ui.msaaMode->itemData(index), &multisamples, &ssaa);
    914         m_dialog->setIntSettingValue("GPU", "Multisamples", static_cast<int>(multisamples));
    915         m_dialog->setBoolSettingValue("GPU", "PerSampleShading", ssaa);
    916       }
    917     });
    918   }
    919 }
    920 
    921 void GraphicsSettingsWidget::updatePGXPSettingsEnabled()
    922 {
    923   const bool enabled = (effectiveRendererIsHardware() && m_dialog->getEffectiveBoolValue("GPU", "PGXPEnable", false) &&
    924                         !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXP));
    925   const bool tc_enabled = (enabled && m_dialog->getEffectiveBoolValue("GPU", "PGXPTextureCorrection", true));
    926   const bool depth_enabled = (enabled && m_dialog->getEffectiveBoolValue("GPU", "PGXPDepthBuffer", false));
    927   m_ui.tabs->setTabEnabled(TAB_INDEX_PGXP, enabled);
    928   m_ui.pgxpTab->setEnabled(enabled);
    929   m_ui.pgxpCulling->setEnabled(enabled && !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPCulling));
    930   m_ui.pgxpTextureCorrection->setEnabled(enabled &&
    931                                          !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPTextureCorrection));
    932   m_ui.pgxpColorCorrection->setEnabled(tc_enabled &&
    933                                        !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPColorCorrection));
    934   m_ui.pgxpDepthBuffer->setEnabled(enabled && !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPDepthBuffer));
    935   m_ui.pgxpPreserveProjPrecision->setEnabled(enabled &&
    936                                              !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPPreserveProjFP));
    937   m_ui.pgxpCPU->setEnabled(enabled);
    938   m_ui.pgxpVertexCache->setEnabled(enabled);
    939   m_ui.pgxpGeometryTolerance->setEnabled(enabled);
    940   m_ui.pgxpGeometryToleranceLabel->setEnabled(enabled);
    941   m_ui.pgxpDepthClearThreshold->setEnabled(depth_enabled);
    942   m_ui.pgxpDepthClearThresholdLabel->setEnabled(depth_enabled);
    943 }
    944 
    945 void GraphicsSettingsWidget::onAspectRatioChanged()
    946 {
    947   const DisplayAspectRatio ratio =
    948     Settings::ParseDisplayAspectRatio(
    949       m_dialog
    950         ->getEffectiveStringValue("Display", "AspectRatio",
    951                                   Settings::GetDisplayAspectRatioName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO))
    952         .c_str())
    953       .value_or(Settings::DEFAULT_DISPLAY_ASPECT_RATIO);
    954 
    955   const bool is_custom = (ratio == DisplayAspectRatio::Custom);
    956 
    957   m_ui.customAspectRatioNumerator->setVisible(is_custom);
    958   m_ui.customAspectRatioDenominator->setVisible(is_custom);
    959   m_ui.customAspectRatioSeparator->setVisible(is_custom);
    960 }
    961 
    962 void GraphicsSettingsWidget::updateResolutionDependentOptions()
    963 {
    964   const int scale = m_dialog->getEffectiveIntValue("GPU", "ResolutionScale", 1);
    965   const GPUTextureFilter texture_filtering =
    966     Settings::ParseTextureFilterName(
    967       m_dialog
    968         ->getEffectiveStringValue("GPU", "TextureFilter",
    969                                   Settings::GetTextureFilterName(Settings::DEFAULT_GPU_TEXTURE_FILTER))
    970         .c_str())
    971       .value_or(Settings::DEFAULT_GPU_TEXTURE_FILTER);
    972   m_ui.forceRoundedTexcoords->setEnabled(scale > 1 && texture_filtering == GPUTextureFilter::Nearest);
    973   onTrueColorChanged();
    974 }
    975 
    976 void GraphicsSettingsWidget::onTrueColorChanged()
    977 {
    978   const int resolution_scale = m_dialog->getEffectiveIntValue("GPU", "ResolutionScale", 1);
    979   const bool true_color = m_dialog->getEffectiveBoolValue("GPU", "TrueColor", false);
    980   const bool allow_scaled_dithering =
    981     (resolution_scale != 1 && !true_color && !m_dialog->hasGameTrait(GameDatabase::Trait::DisableScaledDithering));
    982   m_ui.scaledDithering->setEnabled(allow_scaled_dithering);
    983   m_ui.debanding->setEnabled(true_color);
    984   m_ui.accurateBlending->setEnabled(!true_color);
    985 }
    986 
    987 void GraphicsSettingsWidget::onDownsampleModeChanged()
    988 {
    989   const GPUDownsampleMode mode =
    990     Settings::ParseDownsampleModeName(
    991       m_dialog
    992         ->getEffectiveStringValue("GPU", "DownsampleMode",
    993                                   Settings::GetDownsampleModeName(Settings::DEFAULT_GPU_DOWNSAMPLE_MODE))
    994         .c_str())
    995       .value_or(Settings::DEFAULT_GPU_DOWNSAMPLE_MODE);
    996 
    997   const bool visible = (mode == GPUDownsampleMode::Box);
    998   if (visible && m_ui.gpuDownsampleLayout->indexOf(m_ui.gpuDownsampleScale) < 0)
    999   {
   1000     m_ui.gpuDownsampleScale->setVisible(true);
   1001     m_ui.gpuDownsampleLayout->addWidget(m_ui.gpuDownsampleScale, 0);
   1002   }
   1003   else if (!visible && m_ui.gpuDownsampleLayout->indexOf(m_ui.gpuDownsampleScale) >= 0)
   1004   {
   1005     m_ui.gpuDownsampleScale->setVisible(false);
   1006     m_ui.gpuDownsampleLayout->removeWidget(m_ui.gpuDownsampleScale);
   1007   }
   1008 }
   1009 
   1010 void GraphicsSettingsWidget::onMediaCaptureBackendChanged()
   1011 {
   1012   SettingsInterface* const sif = m_dialog->getSettingsInterface();
   1013   const MediaCaptureBackend backend =
   1014     MediaCapture::ParseBackendName(
   1015       m_dialog
   1016         ->getEffectiveStringValue("MediaCapture", "Backend",
   1017                                   MediaCapture::GetBackendName(Settings::DEFAULT_MEDIA_CAPTURE_BACKEND))
   1018         .c_str())
   1019       .value_or(Settings::DEFAULT_MEDIA_CAPTURE_BACKEND);
   1020 
   1021   {
   1022     m_ui.captureContainer->disconnect();
   1023     m_ui.captureContainer->clear();
   1024 
   1025     for (const auto& [name, display_name] : MediaCapture::GetContainerList(backend))
   1026     {
   1027       const QString qname = QString::fromStdString(name);
   1028       m_ui.captureContainer->addItem(tr("%1 (%2)").arg(QString::fromStdString(display_name)).arg(qname), qname);
   1029     }
   1030 
   1031     SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.captureContainer, "MediaCapture", "Container",
   1032                                                    Settings::DEFAULT_MEDIA_CAPTURE_CONTAINER);
   1033     connect(m_ui.captureContainer, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
   1034             &GraphicsSettingsWidget::onMediaCaptureContainerChanged);
   1035   }
   1036 
   1037   onMediaCaptureContainerChanged();
   1038 }
   1039 
   1040 void GraphicsSettingsWidget::onMediaCaptureContainerChanged()
   1041 {
   1042   SettingsInterface* const sif = m_dialog->getSettingsInterface();
   1043   const MediaCaptureBackend backend =
   1044     MediaCapture::ParseBackendName(
   1045       m_dialog
   1046         ->getEffectiveStringValue("MediaCapture", "Backend",
   1047                                   MediaCapture::GetBackendName(Settings::DEFAULT_MEDIA_CAPTURE_BACKEND))
   1048         .c_str())
   1049       .value_or(Settings::DEFAULT_MEDIA_CAPTURE_BACKEND);
   1050   const std::string container = m_dialog->getEffectiveStringValue("MediaCapture", "Container", "mp4");
   1051 
   1052   {
   1053     m_ui.videoCaptureCodec->disconnect();
   1054     m_ui.videoCaptureCodec->clear();
   1055     m_ui.videoCaptureCodec->addItem(tr("Default"), QVariant(QString()));
   1056 
   1057     for (const auto& [name, display_name] : MediaCapture::GetVideoCodecList(backend, container.c_str()))
   1058     {
   1059       const QString qname = QString::fromStdString(name);
   1060       m_ui.videoCaptureCodec->addItem(tr("%1 (%2)").arg(QString::fromStdString(display_name)).arg(qname), qname);
   1061     }
   1062 
   1063     SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.videoCaptureCodec, "MediaCapture", "VideoCodec");
   1064   }
   1065 
   1066   {
   1067     m_ui.audioCaptureCodec->disconnect();
   1068     m_ui.audioCaptureCodec->clear();
   1069     m_ui.audioCaptureCodec->addItem(tr("Default"), QVariant(QString()));
   1070 
   1071     for (const auto& [name, display_name] : MediaCapture::GetAudioCodecList(backend, container.c_str()))
   1072     {
   1073       const QString qname = QString::fromStdString(name);
   1074       m_ui.audioCaptureCodec->addItem(tr("%1 (%2)").arg(QString::fromStdString(display_name)).arg(qname), qname);
   1075     }
   1076 
   1077     SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.audioCaptureCodec, "MediaCapture", "AudioCodec");
   1078   }
   1079 }
   1080 
   1081 void GraphicsSettingsWidget::onMediaCaptureVideoEnabledChanged()
   1082 {
   1083   const bool enabled = m_dialog->getEffectiveBoolValue("MediaCapture", "VideoCapture", true);
   1084   m_ui.videoCaptureCodecLabel->setEnabled(enabled);
   1085   m_ui.videoCaptureCodec->setEnabled(enabled);
   1086   m_ui.videoCaptureBitrateLabel->setEnabled(enabled);
   1087   m_ui.videoCaptureBitrate->setEnabled(enabled);
   1088   m_ui.videoCaptureResolutionLabel->setEnabled(enabled);
   1089   m_ui.videoCaptureResolutionAuto->setEnabled(enabled);
   1090   m_ui.enableVideoCaptureArguments->setEnabled(enabled);
   1091   m_ui.videoCaptureArguments->setEnabled(enabled);
   1092   onMediaCaptureVideoAutoResolutionChanged();
   1093 }
   1094 
   1095 void GraphicsSettingsWidget::onMediaCaptureVideoAutoResolutionChanged()
   1096 {
   1097   const bool enabled = m_dialog->getEffectiveBoolValue("MediaCapture", "VideoCapture", true);
   1098   const bool auto_enabled = m_dialog->getEffectiveBoolValue("MediaCapture", "VideoAutoSize", false);
   1099   m_ui.videoCaptureWidth->setEnabled(enabled && !auto_enabled);
   1100   m_ui.xLabel->setEnabled(enabled && !auto_enabled);
   1101   m_ui.videoCaptureHeight->setEnabled(enabled && !auto_enabled);
   1102 }
   1103 
   1104 void GraphicsSettingsWidget::onMediaCaptureAudioEnabledChanged()
   1105 {
   1106   const bool enabled = m_dialog->getEffectiveBoolValue("MediaCapture", "AudioCapture", true);
   1107   m_ui.audioCaptureCodecLabel->setEnabled(enabled);
   1108   m_ui.audioCaptureCodec->setEnabled(enabled);
   1109   m_ui.audioCaptureBitrateLabel->setEnabled(enabled);
   1110   m_ui.audioCaptureBitrate->setEnabled(enabled);
   1111   m_ui.enableAudioCaptureArguments->setEnabled(enabled);
   1112   m_ui.audioCaptureArguments->setEnabled(enabled);
   1113 }
   1114 
   1115 void GraphicsSettingsWidget::onEnableAnyTextureReplacementsChanged()
   1116 {
   1117   const bool any_replacements_enabled =
   1118     m_dialog->getEffectiveBoolValue("TextureReplacements", "EnableVRAMWriteReplacements", false);
   1119   m_ui.preloadTextureReplacements->setEnabled(any_replacements_enabled);
   1120 }
   1121 
   1122 void GraphicsSettingsWidget::onEnableVRAMWriteDumpingChanged()
   1123 {
   1124   const bool enabled = m_dialog->getEffectiveBoolValue("TextureReplacements", "DumpVRAMWrites", false);
   1125   m_ui.setVRAMWriteAlphaChannel->setEnabled(enabled);
   1126   m_ui.minDumpedVRAMWriteWidth->setEnabled(enabled);
   1127   m_ui.minDumpedVRAMWriteHeight->setEnabled(enabled);
   1128   m_ui.vramWriteDumpThresholdLabel->setEnabled(enabled);
   1129   m_ui.vramWriteDumpThresholdSeparator->setEnabled(enabled);
   1130 }