mirror of https://github.com/stenzek/duckstation
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1223 lines
70 KiB
C++
1223 lines
70 KiB
C++
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
|
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
|
|
|
#include "graphicssettingswidget.h"
|
|
#include "qthost.h"
|
|
#include "qtutils.h"
|
|
#include "qtwindowinfo.h"
|
|
#include "settingswindow.h"
|
|
#include "settingwidgetbinder.h"
|
|
|
|
#include "core/core.h"
|
|
#include "core/fullscreenui_widgets.h"
|
|
#include "core/game_database.h"
|
|
#include "core/gpu.h"
|
|
#include "core/settings.h"
|
|
|
|
#include "util/imgui_manager.h"
|
|
#include "util/ini_settings_interface.h"
|
|
|
|
#include "common/error.h"
|
|
#include "common/log.h"
|
|
|
|
#include <QtCore/QDir>
|
|
#include <QtWidgets/QDialog>
|
|
#include <QtWidgets/QFileDialog>
|
|
#include <QtWidgets/QInputDialog>
|
|
#include <algorithm>
|
|
|
|
#include "moc_graphicssettingswidget.cpp"
|
|
|
|
LOG_CHANNEL(Host);
|
|
|
|
static QVariant GetMSAAModeValue(uint multisamples, bool ssaa)
|
|
{
|
|
const uint userdata = (multisamples & 0x7FFFFFFFu) | (static_cast<uint>(ssaa) << 31);
|
|
return QVariant(userdata);
|
|
}
|
|
|
|
static void DecodeMSAAModeValue(const QVariant& userdata, uint* multisamples, bool* ssaa)
|
|
{
|
|
bool ok;
|
|
const uint value = userdata.toUInt(&ok);
|
|
if (!ok || value == 0)
|
|
{
|
|
*multisamples = 1;
|
|
*ssaa = false;
|
|
return;
|
|
}
|
|
|
|
*multisamples = value & 0x7FFFFFFFu;
|
|
*ssaa = (value & (1u << 31)) != 0u;
|
|
}
|
|
|
|
GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* parent)
|
|
: QWidget(parent), m_dialog(dialog)
|
|
{
|
|
SettingsInterface* sif = dialog->getSettingsInterface();
|
|
|
|
m_ui.setupUi(this);
|
|
removePlatformSpecificUi();
|
|
|
|
// Rendering Tab
|
|
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.renderer, "GPU", "Renderer", &Settings::ParseRendererName,
|
|
&Settings::GetRendererName, &Settings::GetRendererDisplayName,
|
|
Settings::DEFAULT_GPU_RENDERER, GPURenderer::Count);
|
|
if (!m_dialog->hasGameTrait(GameDatabase::Trait::DisableUpscaling))
|
|
populateAndConnectUpscalingModes();
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.textureFiltering, "GPU", "TextureFilter",
|
|
&Settings::ParseTextureFilterName, &Settings::GetTextureFilterName,
|
|
&Settings::GetTextureFilterDisplayName,
|
|
Settings::DEFAULT_GPU_TEXTURE_FILTER, GPUTextureFilter::Count);
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.spriteTextureFiltering, "GPU", "SpriteTextureFilter",
|
|
&Settings::ParseTextureFilterName, &Settings::GetTextureFilterName,
|
|
&Settings::GetTextureFilterDisplayName,
|
|
Settings::DEFAULT_GPU_TEXTURE_FILTER, GPUTextureFilter::Count);
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.gpuDitheringMode, "GPU", "DitheringMode",
|
|
&Settings::ParseGPUDitheringModeName, &Settings::GetGPUDitheringModeName,
|
|
&Settings::GetGPUDitheringModeDisplayName,
|
|
Settings::DEFAULT_GPU_DITHERING_MODE, GPUDitheringMode::MaxCount);
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.gpuDownsampleMode, "GPU", "DownsampleMode",
|
|
&Settings::ParseDownsampleModeName, &Settings::GetDownsampleModeName,
|
|
&Settings::GetDownsampleModeDisplayName,
|
|
Settings::DEFAULT_GPU_DOWNSAMPLE_MODE, GPUDownsampleMode::Count);
|
|
createAspectRatioSetting(m_ui.displayAspectRatio, m_ui.customAspectRatioNumerator, m_ui.customAspectRatioSeparator,
|
|
m_ui.customAspectRatioDenominator, sif);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.widescreenHack, "GPU", "WidescreenHack", false);
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(
|
|
sif, m_ui.displayDeinterlacing, "GPU", "DeinterlacingMode", &Settings::ParseDisplayDeinterlacingMode,
|
|
&Settings::GetDisplayDeinterlacingModeName, &Settings::GetDisplayDeinterlacingModeDisplayName,
|
|
Settings::DEFAULT_DISPLAY_DEINTERLACING_MODE, DisplayDeinterlacingMode::Count);
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayCropMode, "Display", "CropMode",
|
|
&Settings::ParseDisplayCropMode, &Settings::GetDisplayCropModeName,
|
|
&Settings::GetDisplayCropModeDisplayName,
|
|
Settings::DEFAULT_DISPLAY_CROP_MODE, DisplayCropMode::MaxCount);
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(
|
|
sif, m_ui.displayScaling, "Display", "Scaling", &Settings::ParseDisplayScaling, &Settings::GetDisplayScalingName,
|
|
&Settings::GetDisplayScalingDisplayName, Settings::DEFAULT_DISPLAY_SCALING, DisplayScalingMode::Count);
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayScaling24Bit, "Display", "Scaling24Bit",
|
|
&Settings::ParseDisplayScaling, &Settings::GetDisplayScalingName,
|
|
&Settings::GetDisplayScalingDisplayName,
|
|
Settings::DEFAULT_DISPLAY_SCALING, DisplayScalingMode::Count);
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.gpuDownsampleScale, "GPU", "DownsampleScale", 1);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpEnable, "GPU", "PGXPEnable", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpDepthBuffer, "GPU", "PGXPDepthBuffer", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.force43For24Bit, "Display", "Force4_3For24Bit", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.chromaSmoothingFor24Bit, "GPU", "ChromaSmoothing24Bit", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.forceRoundedTexcoords, "GPU", "ForceRoundTextureCoordinates",
|
|
false);
|
|
|
|
connect(m_ui.renderer, &QComboBox::currentIndexChanged, this,
|
|
&GraphicsSettingsWidget::updateRendererDependentOptions);
|
|
connect(m_ui.textureFiltering, &QComboBox::currentIndexChanged, this,
|
|
&GraphicsSettingsWidget::updateResolutionDependentOptions);
|
|
connect(m_ui.spriteTextureFiltering, &QComboBox::currentIndexChanged, this,
|
|
&GraphicsSettingsWidget::updateResolutionDependentOptions);
|
|
connect(m_ui.gpuDownsampleMode, &QComboBox::currentIndexChanged, this,
|
|
&GraphicsSettingsWidget::onDownsampleModeChanged);
|
|
connect(m_ui.pgxpEnable, &QCheckBox::checkStateChanged, this, &GraphicsSettingsWidget::updatePGXPSettingsEnabled);
|
|
|
|
SettingWidgetBinder::SetAvailability(m_ui.renderer,
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::ForceSoftwareRenderer));
|
|
SettingWidgetBinder::SetAvailability(m_ui.resolutionScale,
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableUpscaling));
|
|
SettingWidgetBinder::SetAvailability(m_ui.textureFiltering,
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableTextureFiltering));
|
|
SettingWidgetBinder::SetAvailability(m_ui.spriteTextureFiltering,
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableTextureFiltering) ||
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableSpriteTextureFiltering));
|
|
SettingWidgetBinder::SetAvailability(m_ui.pgxpEnable, !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXP));
|
|
SettingWidgetBinder::SetAvailability(m_ui.pgxpDepthBuffer,
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPDepthBuffer));
|
|
SettingWidgetBinder::SetAvailability(m_ui.widescreenHack,
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableWidescreen));
|
|
|
|
// Advanced Tab
|
|
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(
|
|
sif, m_ui.exclusiveFullscreenControl, "Display", "ExclusiveFullscreenControl",
|
|
&Settings::ParseDisplayExclusiveFullscreenControl, &Settings::GetDisplayExclusiveFullscreenControlName,
|
|
&Settings::GetDisplayExclusiveFullscreenControlDisplayName, Settings::DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL,
|
|
DisplayExclusiveFullscreenControl::Count);
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayAlignment, "Display", "Alignment",
|
|
&Settings::ParseDisplayAlignment, &Settings::GetDisplayAlignmentName,
|
|
&Settings::GetDisplayAlignmentDisplayName,
|
|
Settings::DEFAULT_DISPLAY_ALIGNMENT, DisplayAlignment::Count);
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayRotation, "Display", "Rotation",
|
|
&Settings::ParseDisplayRotation, &Settings::GetDisplayRotationName,
|
|
&Settings::GetDisplayRotationDisplayName,
|
|
Settings::DEFAULT_DISPLAY_ROTATION, DisplayRotation::Count);
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(
|
|
sif, m_ui.displayFineCropMode, "Display", "FineCropMode", &Settings::ParseDisplayFineCropMode,
|
|
&Settings::GetDisplayFineCropModeName, &Settings::GetDisplayFineCropModeDisplayName,
|
|
Settings::DEFAULT_DISPLAY_FINE_CROP_MODE, DisplayFineCropMode::MaxCount);
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayFineCropLeft, "Display", "FineCropLeft", 0);
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayFineCropTop, "Display", "FineCropTop", 0);
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayFineCropRight, "Display", "FineCropRight", 0);
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayFineCropBottom, "Display", "FineCropBottom", 0);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableMailboxPresentation, "Display",
|
|
"DisableMailboxPresentation", false);
|
|
#ifdef _WIN32
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.blitSwapChain, "Display", "UseBlitSwapChain", false);
|
|
#endif
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.gpuLineDetectMode, "GPU", "LineDetectMode",
|
|
&Settings::ParseLineDetectModeName, &Settings::GetLineDetectModeName,
|
|
&Settings::GetLineDetectModeDisplayName,
|
|
Settings::DEFAULT_GPU_LINE_DETECT_MODE, GPULineDetectMode::Count);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gpuThread, "GPU", "UseThread", true);
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.maxQueuedFrames, "GPU", "MaxQueuedFrames",
|
|
Settings::DEFAULT_GPU_MAX_QUEUED_FRAMES);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.modulationCrop, "GPU", "EnableModulationCrop", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.scaledInterlacing, "GPU", "ScaledInterlacing", true);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useSoftwareRendererForReadbacks, "GPU",
|
|
"UseSoftwareRendererForReadbacks", false);
|
|
|
|
connect(m_ui.displayFineCropMode, &QComboBox::currentIndexChanged, this,
|
|
&GraphicsSettingsWidget::onFineCropModeChanged);
|
|
connect(m_ui.displayFineCropReset, &QPushButton::clicked, this, &GraphicsSettingsWidget::onFineCropResetClicked);
|
|
connect(m_ui.gpuThread, &QCheckBox::checkStateChanged, this, &GraphicsSettingsWidget::onGPUThreadChanged);
|
|
|
|
SettingWidgetBinder::SetAvailability(m_ui.scaledInterlacing,
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableScaledInterlacing));
|
|
SettingWidgetBinder::SetForceEnabled(m_ui.useSoftwareRendererForReadbacks,
|
|
m_dialog->hasGameTrait(GameDatabase::Trait::ForceSoftwareRendererForReadbacks));
|
|
SettingWidgetBinder::SetForceEnabled(
|
|
m_ui.forceRoundedTexcoords, m_dialog->hasGameTrait(GameDatabase::Trait::ForceRoundUpscaledTextureCoordinates));
|
|
|
|
// PGXP Tab
|
|
|
|
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.pgxpGeometryTolerance, "GPU", "PGXPTolerance", -1.0f);
|
|
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.pgxpDepthClearThreshold, "GPU", "PGXPDepthThreshold",
|
|
Settings::DEFAULT_GPU_PGXP_DEPTH_THRESHOLD);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpTextureCorrection, "GPU", "PGXPTextureCorrection", true);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpColorCorrection, "GPU", "PGXPColorCorrection", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpCulling, "GPU", "PGXPCulling", true);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpPreserveProjPrecision, "GPU", "PGXPPreserveProjFP", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpCPU, "GPU", "PGXPCPU", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpVertexCache, "GPU", "PGXPVertexCache", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpDisableOn2DPolygons, "GPU", "PGXPDisableOn2DPolygons",
|
|
false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pgxpTransparentDepthTest, "GPU", "PGXPTransparentDepthTest",
|
|
false);
|
|
|
|
connect(m_ui.pgxpTextureCorrection, &QCheckBox::checkStateChanged, this,
|
|
&GraphicsSettingsWidget::updatePGXPSettingsEnabled);
|
|
connect(m_ui.pgxpDepthBuffer, &QCheckBox::checkStateChanged, this,
|
|
&GraphicsSettingsWidget::updatePGXPSettingsEnabled);
|
|
|
|
SettingWidgetBinder::SetAvailability(m_ui.pgxpTextureCorrection,
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPTextureCorrection));
|
|
SettingWidgetBinder::SetAvailability(m_ui.pgxpColorCorrection,
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPColorCorrection));
|
|
SettingWidgetBinder::SetAvailability(m_ui.pgxpCulling,
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPCulling));
|
|
SettingWidgetBinder::SetForceEnabled(m_ui.pgxpCPU, m_dialog->hasGameTrait(GameDatabase::Trait::ForcePGXPCPUMode));
|
|
SettingWidgetBinder::SetForceEnabled(m_ui.pgxpVertexCache,
|
|
m_dialog->hasGameTrait(GameDatabase::Trait::ForcePGXPVertexCache));
|
|
SettingWidgetBinder::SetForceEnabled(m_ui.pgxpDisableOn2DPolygons,
|
|
m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPOn2DPolygons));
|
|
if (const GameDatabase::Entry* dbentry = m_dialog->getDatabaseEntry();
|
|
dbentry && dbentry->gpu_pgxp_preserve_proj_fp.has_value())
|
|
{
|
|
if (dbentry->gpu_pgxp_preserve_proj_fp.value())
|
|
SettingWidgetBinder::SetForceEnabled(m_ui.pgxpPreserveProjPrecision, true);
|
|
else
|
|
SettingWidgetBinder::SetAvailability(m_ui.pgxpPreserveProjPrecision, false);
|
|
}
|
|
|
|
// Texture Replacements Tab
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableTextureCache, "GPU", "EnableTextureCache", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preloadTextureReplacements, "TextureReplacements",
|
|
"PreloadTextures", false);
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableTextureReplacements, "TextureReplacements",
|
|
"EnableTextureReplacements", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.alwaysTrackUploads, "TextureReplacements",
|
|
"AlwaysTrackUploads", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableTextureDumping, "TextureReplacements", "DumpTextures",
|
|
false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.dumpReplacedTextures, "TextureReplacements",
|
|
"DumpReplacedTextures", true);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vramWriteReplacement, "TextureReplacements",
|
|
"EnableVRAMWriteReplacements", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vramWriteDumping, "TextureReplacements", "DumpVRAMWrites",
|
|
false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useOldMDECRoutines, "Hacks", "UseOldMDECRoutines", false);
|
|
|
|
if (!m_dialog->isPerGameSettings())
|
|
{
|
|
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.texturesDirectory, m_ui.texturesDirectoryBrowse,
|
|
tr("Select Textures Directory"), m_ui.texturesDirectoryOpen,
|
|
m_ui.texturesDirectoryReset, "Folders", "Textures",
|
|
Path::Combine(EmuFolders::DataRoot, "textures"));
|
|
}
|
|
else
|
|
{
|
|
m_ui.tabTextureReplacementsLayout->removeWidget(m_ui.texturesDirectoryGroup);
|
|
delete m_ui.texturesDirectoryGroup;
|
|
m_ui.texturesDirectoryGroup = nullptr;
|
|
m_ui.texturesDirectoryBrowse = nullptr;
|
|
m_ui.texturesDirectoryOpen = nullptr;
|
|
m_ui.texturesDirectoryReset = nullptr;
|
|
m_ui.texturesDirectoryLabel = nullptr;
|
|
m_ui.texturesDirectory = nullptr;
|
|
}
|
|
|
|
connect(m_ui.enableTextureCache, &QCheckBox::checkStateChanged, this,
|
|
&GraphicsSettingsWidget::onEnableTextureCacheChanged);
|
|
connect(m_ui.enableTextureReplacements, &QCheckBox::checkStateChanged, this,
|
|
&GraphicsSettingsWidget::onEnableAnyTextureReplacementsChanged);
|
|
connect(m_ui.enableTextureDumping, &QCheckBox::checkStateChanged, this,
|
|
&GraphicsSettingsWidget::onEnableAnyTextureDumpingChanged);
|
|
connect(m_ui.vramWriteReplacement, &QCheckBox::checkStateChanged, this,
|
|
&GraphicsSettingsWidget::onEnableAnyTextureReplacementsChanged);
|
|
connect(m_ui.vramWriteDumping, &QCheckBox::checkStateChanged, this,
|
|
&GraphicsSettingsWidget::onEnableAnyTextureDumpingChanged);
|
|
connect(m_ui.textureReplacementOptions, &QPushButton::clicked, this,
|
|
&GraphicsSettingsWidget::onTextureReplacementOptionsClicked);
|
|
|
|
// Debugging Tab
|
|
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.gpuWireframeMode, "GPU", "WireframeMode",
|
|
Settings::ParseGPUWireframeMode, Settings::GetGPUWireframeModeName,
|
|
&Settings::GetGPUWireframeModeDisplayName,
|
|
Settings::DEFAULT_GPU_WIREFRAME_MODE, GPUWireframeMode::Count);
|
|
|
|
SettingWidgetBinder::BindWidgetToEnumSetting(
|
|
sif, m_ui.gpuDumpCompressionMode, "GPU", "DumpCompressionMode", &Settings::ParseGPUDumpCompressionMode,
|
|
&Settings::GetGPUDumpCompressionModeName, &Settings::GetGPUDumpCompressionModeDisplayName,
|
|
Settings::DEFAULT_GPU_DUMP_COMPRESSION_MODE, GPUDumpCompressionMode::MaxCount);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gpuDumpFastReplayMode, "GPU", "DumpFastReplayMode", false);
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useDebugDevice, "GPU", "UseDebugDevice", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useGPUBasedValidation, "GPU", "UseGPUBasedValidation", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preferGLESContext, "GPU", "PreferGLESContext",
|
|
Settings::DEFAULT_GPU_PREFER_GLES_CONTEXT);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableShaderCache, "GPU", "DisableShaderCache", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDualSource, "GPU", "DisableDualSourceBlend", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableFramebufferFetch, "GPU", "DisableFramebufferFetch",
|
|
false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableTextureBuffers, "GPU", "DisableTextureBuffers", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableTextureCopyToSelf, "GPU", "DisableTextureCopyToSelf",
|
|
false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableMemoryImport, "GPU", "DisableMemoryImport", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableRasterOrderViews, "GPU", "DisableRasterOrderViews",
|
|
false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableComputeShaders, "GPU", "DisableComputeShaders", false);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableCompressedTextures, "GPU", "DisableCompressedTextures",
|
|
false);
|
|
|
|
// Init all dependent options.
|
|
updateRendererDependentOptions();
|
|
onDownsampleModeChanged();
|
|
onFineCropModeChanged();
|
|
onEnableTextureCacheChanged();
|
|
onEnableAnyTextureReplacementsChanged();
|
|
onGPUThreadChanged();
|
|
onShowDebugSettingsChanged(QtHost::ShouldShowDebugOptions());
|
|
|
|
// Rendering Tab
|
|
|
|
dialog->registerWidgetHelp(
|
|
m_ui.renderer, tr("Renderer"), QString::fromUtf8(Settings::GetRendererDisplayName(Settings::DEFAULT_GPU_RENDERER)),
|
|
tr("Selects the backend to use for rendering the console/game visuals. <br>Depending on your system and hardware, "
|
|
"Direct3D 11 and OpenGL hardware backends may be available. <br>The software renderer offers the best "
|
|
"compatibility, but is the slowest and does not offer any enhancements."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.adapter, tr("Adapter"), tr("(Default)"),
|
|
tr("If your system contains multiple GPUs or adapters, you can select which GPU you wish to use for the hardware "
|
|
"renderers. <br>This option is only supported in Direct3D and Vulkan. OpenGL will always use the default "
|
|
"device."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.resolutionScale, tr("Internal Resolution"), "1x",
|
|
tr("Setting this beyond 1x will enhance the resolution of rendered 3D polygons and lines. Only applies "
|
|
"to the hardware backends. <br>This option is usually safe, with most games looking fine at "
|
|
"higher resolutions. Higher resolutions require a more powerful GPU."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.gpuDownsampleMode, tr("Down-Sampling"), tr("Disabled"),
|
|
tr("Downsamples the rendered image prior to displaying it. Can improve overall image quality in mixed 2D/3D games, "
|
|
"but should be disabled for pure 3D games."));
|
|
dialog->registerWidgetHelp(m_ui.gpuDownsampleScale, tr("Down-Sampling Display Scale"), tr("1x"),
|
|
tr("Selects the resolution scale that will be applied to the final image. 1x will "
|
|
"downsample to the original console resolution."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.textureFiltering, tr("Texture Filtering"),
|
|
QString::fromUtf8(Settings::GetTextureFilterDisplayName(Settings::DEFAULT_GPU_TEXTURE_FILTER)),
|
|
tr("Smooths out the blockiness of magnified textures on 3D objects by using filtering. <br>Will have a "
|
|
"greater effect on higher resolution scales."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.spriteTextureFiltering, tr("Sprite Texture Filtering"),
|
|
QString::fromUtf8(Settings::GetTextureFilterDisplayName(Settings::DEFAULT_GPU_TEXTURE_FILTER)),
|
|
tr("Smooths out the blockiness of magnified textures on 2D objects by using filtering. This filter only applies to "
|
|
"sprites and other 2D elements, such as the HUD."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.gpuDitheringMode, tr("Dithering"),
|
|
QString::fromUtf8(Settings::GetGPUDitheringModeDisplayName(Settings::DEFAULT_GPU_DITHERING_MODE)),
|
|
tr("Controls how dithering is applied in the emulated GPU. True Color disables dithering and produces the nicest "
|
|
"looking gradients. Scaled options make the dither pattern less noticeable at higher resolutions. Shader "
|
|
"Blending options perform blending in software, and are more accurate but have a <strong>significant</strong> "
|
|
"performance penalty."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.displayAspectRatio, tr("Aspect Ratio"),
|
|
QtUtils::StringViewToQString(Settings::GetDisplayAspectRatioDisplayName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO)),
|
|
tr("Changes the aspect ratio used to display the console's output to the screen. The default is Auto (Game Native) "
|
|
"which automatically adjusts the aspect ratio to match how a game would be shown on a typical TV of the era."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.displayDeinterlacing, tr("Deinterlacing"),
|
|
QString::fromUtf8(Settings::GetDisplayDeinterlacingModeDisplayName(Settings::DEFAULT_DISPLAY_DEINTERLACING_MODE)),
|
|
tr("Determines which algorithm is used to convert interlaced frames to progressive for display on your system. "
|
|
"Using progressive rendering provides the best quality output, but some games require interlaced rendering."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.displayCropMode, tr("Crop"),
|
|
QString::fromUtf8(Settings::GetDisplayCropModeDisplayName(Settings::DEFAULT_DISPLAY_CROP_MODE)),
|
|
tr("Determines how much of the area typically not visible on a consumer TV set to crop/hide. Some games display "
|
|
"content in the overscan area, or use it for screen effects. May not display correctly with the \"All Borders\" "
|
|
"setting. \"Only Overscan\" offers a good compromise between stability and hiding black borders."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.displayScaling, tr("Scaling"), tr("Bilinear (Smooth)"),
|
|
tr("Determines how the emulated console's output is upscaled or downscaled to your monitor's resolution."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.displayScaling24Bit, tr("FMV Scaling"), tr("Bilinear (Smooth)"),
|
|
tr("Determines the scaling algorithm used when 24-bit content is active, typically FMVs."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.widescreenHack, tr("Widescreen Rendering"), tr("Unchecked"),
|
|
tr("Scales vertex positions in screen-space to a widescreen aspect ratio, essentially "
|
|
"increasing the field of view from 4:3 to the chosen display aspect ratio in 3D games. <b><u>May not be "
|
|
"compatible with all games.</u></b>"));
|
|
dialog->registerWidgetHelp(m_ui.pgxpEnable, tr("PGXP Geometry Correction"), tr("Unchecked"),
|
|
tr("Reduces \"wobbly\" polygons and \"warping\" textures that are common in PS1 games. "
|
|
"<strong>May not be compatible with all games.</strong>"));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.pgxpDepthBuffer, tr("PGXP Depth Buffer"), tr("Unchecked"),
|
|
tr("Attempts to reduce polygon Z-fighting by testing pixels against the depth values from PGXP. Low compatibility, "
|
|
"but can work well in some games. Other games may need a threshold adjustment."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.force43For24Bit, tr("Force 4:3 For FMVs"), tr("Unchecked"),
|
|
tr("Switches back to 4:3 display aspect ratio when displaying 24-bit content, usually FMVs."));
|
|
dialog->registerWidgetHelp(m_ui.chromaSmoothingFor24Bit, tr("FMV Chroma Smoothing"), tr("Unchecked"),
|
|
tr("Smooths out blockyness between colour transitions in 24-bit content, usually FMVs."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.forceRoundedTexcoords, tr("Round Upscaled Texture Coordinates"), tr("Unchecked"),
|
|
tr("Rounds texture coordinates instead of flooring when upscaling. Can fix misaligned textures in some games, but "
|
|
"break others, and is incompatible with texture filtering."));
|
|
|
|
// Advanced Tab
|
|
|
|
dialog->registerWidgetHelp(m_ui.fullscreenMode, tr("Fullscreen Mode"), tr("Borderless Fullscreen"),
|
|
tr("Chooses the fullscreen resolution and frequency."));
|
|
dialog->registerWidgetHelp(m_ui.exclusiveFullscreenControl, tr("Exclusive Fullscreen Control"), tr("Automatic"),
|
|
tr("Controls whether exclusive fullscreen can be utilized by Vulkan drivers."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.displayAlignment, tr("Position"),
|
|
QString::fromUtf8(Settings::GetDisplayAlignmentDisplayName(Settings::DEFAULT_DISPLAY_ALIGNMENT)),
|
|
tr("Determines the position on the screen when black borders must be added."));
|
|
dialog->registerWidgetHelp(m_ui.displayFineCropMode, tr("Fine Crop Mode"), tr("None"),
|
|
tr("Enables manual fine cropping of the display area, while preserving the aspect ratio "
|
|
"of the image. Useful for removing black borders in certain games."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.disableMailboxPresentation, tr("Disable Mailbox Presentation"), tr("Unchecked"),
|
|
tr("Forces the use of FIFO over Mailbox presentation, i.e. double buffering instead of triple buffering. "
|
|
"Usually results in worse frame pacing."));
|
|
#ifdef _WIN32
|
|
dialog->registerWidgetHelp(m_ui.blitSwapChain, tr("Use Blit Swap Chain"), tr("Unchecked"),
|
|
tr("Uses a blit presentation model instead of flipping when using the Direct3D 11 "
|
|
"renderer. This usually results in slower performance, but may be required for some "
|
|
"streaming applications, or to uncap framerates on some systems."));
|
|
#endif
|
|
|
|
dialog->registerWidgetHelp(
|
|
m_ui.gpuLineDetectMode, tr("Line Detection"),
|
|
QString::fromUtf8(Settings::GetLineDetectModeDisplayName(Settings::DEFAULT_GPU_LINE_DETECT_MODE)),
|
|
tr("Attempts to detect one pixel high/wide lines that rely on non-upscaled rasterization "
|
|
"behavior, filling in gaps introduced by upscaling."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.msaaMode, tr("Multi-Sampling"), tr("Disabled"),
|
|
tr("Uses multi-sampled anti-aliasing when rendering 3D polygons. Can improve visuals with a lower performance "
|
|
"requirement compared to upscaling, <strong>but often introduces rendering errors.</strong>"));
|
|
dialog->registerWidgetHelp(m_ui.gpuThread, tr("Threaded Rendering"), tr("Checked"),
|
|
tr("Uses a second thread for drawing graphics. Provides a significant speed improvement "
|
|
"particularly with the software renderer, and is safe to use."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.modulationCrop, tr("Texture Modulation Cropping (\"Old/v0\" GPU)"), tr("Unchecked"),
|
|
tr("Crops vertex colours to 5:5:5 before modulating with the texture colour, which typically results in more "
|
|
"visible banding. This is a characteristic of the \"old\" GPUs found in early model consoles."));
|
|
dialog->registerWidgetHelp(m_ui.scaledInterlacing, tr("Scaled Interlacing"), tr("Checked"),
|
|
tr("Scales line skipping in interlaced rendering to the internal resolution. This makes "
|
|
"the combing less obvious at higher resolutions. Usually safe to enable."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.useSoftwareRendererForReadbacks, tr("Software Renderer Readbacks"), tr("Unchecked"),
|
|
tr("Runs the software renderer in parallel for VRAM readbacks. On some systems, this may result in greater "
|
|
"performance when using graphical enhancements with the hardware renderer."));
|
|
|
|
// PGXP Tab
|
|
|
|
dialog->registerWidgetHelp(
|
|
m_ui.pgxpGeometryTolerance, tr("Geometry Tolerance"), tr("-1.00px (Disabled)"),
|
|
tr("Discards precise geometry when it is found to be offset past the specified threshold. This can help with games "
|
|
"that have vertices significantly moved by PGXP, but is still a hack/workaround."));
|
|
dialog->registerWidgetHelp(m_ui.pgxpDepthClearThreshold, tr("Depth Clear Threshold"),
|
|
QStringLiteral("%1").arg(Settings::DEFAULT_GPU_PGXP_DEPTH_THRESHOLD),
|
|
tr("Determines the increase in depth that will result in the depth buffer being cleared. "
|
|
"Can help with depth issues in some games, but is still a hack/workaround."));
|
|
dialog->registerWidgetHelp(m_ui.pgxpTextureCorrection, tr("Perspective Correct Textures"), tr("Checked"),
|
|
tr("Uses perspective-correct interpolation for texture coordinates, straightening out "
|
|
"warped textures. Requires geometry correction enabled."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.pgxpColorCorrection, tr("Perspective Correct Colors"), tr("Unchecked"),
|
|
tr("Uses perspective-correct interpolation for vertex colors, which can improve visuals in some games, but cause "
|
|
"rendering errors in others. Requires geometry correction enabled."));
|
|
dialog->registerWidgetHelp(m_ui.pgxpCulling, tr("Culling Correction"), tr("Checked"),
|
|
tr("Increases the precision of polygon culling, reducing the number of holes in geometry. "
|
|
"Requires geometry correction enabled."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.pgxpPreserveProjPrecision, tr("Preserve Projection Precision"), tr("Unchecked"),
|
|
tr("Adds additional precision to PGXP data post-projection. May improve visuals in some games."));
|
|
dialog->registerWidgetHelp(m_ui.pgxpCPU, tr("CPU Mode"), tr("Unchecked"),
|
|
tr("Uses PGXP for all instructions, not just memory operations. Required for PGXP to "
|
|
"correct wobble in some games, but has a high performance cost."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.pgxpVertexCache, tr("Vertex Cache"), tr("Unchecked"),
|
|
tr("Uses screen-space vertex positions to obtain precise positions, instead of tracking memory accesses. Can "
|
|
"provide PGXP compatibility for some games, but <strong>generally provides no benefit.</strong>"));
|
|
dialog->registerWidgetHelp(m_ui.pgxpDisableOn2DPolygons, tr("Disable on 2D Polygons"), tr("Unchecked"),
|
|
tr("Uses native resolution coordinates for 2D polygons, instead of precise coordinates. "
|
|
"Can fix misaligned UI in some games, but otherwise should be left disabled. The game "
|
|
"database will enable this automatically when needed."));
|
|
dialog->registerWidgetHelp(m_ui.pgxpTransparentDepthTest, tr("Depth Test Transparent Polygons"), tr("Unchecked"),
|
|
tr("Enables depth testing for semi-transparent polygons. Usually these include shadows, "
|
|
"and tend to clip through the ground when depth testing is enabled. Depth writes for "
|
|
"semi-transparent polygons are disabled regardless of this setting."));
|
|
|
|
// Texture Replacements Tab
|
|
dialog->registerWidgetHelp(
|
|
m_ui.enableTextureCache, tr("Enable Texture Cache"), tr("Unchecked"),
|
|
tr("Enables caching of guest textures, required for texture replacement. <strong>The texture cache is currently "
|
|
"experimental, and may cause rendering errors in some games.</strong>"));
|
|
dialog->registerWidgetHelp(m_ui.preloadTextureReplacements, tr("Preload Texture Replacements"), tr("Unchecked"),
|
|
tr("Loads all replacement texture to RAM, reducing stuttering at runtime."));
|
|
|
|
dialog->registerWidgetHelp(m_ui.enableTextureReplacements, tr("Enable Texture Replacements"), tr("Unchecked"),
|
|
tr("Enables loading of replacement textures. Not compatible with all games."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.alwaysTrackUploads, tr("Always Track Uploads"), tr("Unchecked"),
|
|
tr("Forces texture upload tracking to be enabled regardless of whether it is needed. Reduces performance, but "
|
|
"allows toggling replacements on and off. <strong>Not required for replacements to load, </strong>normally "
|
|
"tracking is automatically enabled when needed."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.enableTextureDumping, tr("Enable Texture Dumping"), tr("Unchecked"),
|
|
tr("Enables dumping of textures to image files, which can be replaced. Not compatible with all games."));
|
|
dialog->registerWidgetHelp(m_ui.dumpReplacedTextures, tr("Dump Replaced Textures"), tr("Unchecked"),
|
|
tr("Dumps textures that have replacements already loaded."));
|
|
|
|
dialog->registerWidgetHelp(m_ui.vramWriteReplacement, tr("Enable VRAM Write Replacement"), tr("Unchecked"),
|
|
tr("Enables the replacement of background textures in supported games."));
|
|
dialog->registerWidgetHelp(m_ui.vramWriteDumping, tr("Enable VRAM Write Dumping"), tr("Unchecked"),
|
|
tr("Writes backgrounds that can be replaced to the dump directory."));
|
|
dialog->registerWidgetHelp(m_ui.useOldMDECRoutines, tr("Use Old MDEC Routines"), tr("Unchecked"),
|
|
tr("Enables the older, less accurate MDEC decoding routines. May be required for old "
|
|
"replacement backgrounds to match/load."));
|
|
|
|
// Debugging Tab
|
|
|
|
dialog->registerWidgetHelp(m_ui.gpuWireframeMode, tr("Wireframe Mode"), tr("Disabled"),
|
|
tr("Draws a wireframe outline of the triangles rendered by the console's GPU, either as a "
|
|
"replacement or an overlay."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.useDebugDevice, tr("Use Debug Device"), tr("Unchecked"),
|
|
tr("Enable debugging when supported by the host's renderer API. <strong>Only for developer use.</strong>"));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.useGPUBasedValidation, tr("Use GPU-Based Validation"), tr("Unchecked"),
|
|
tr("Enable GPU-based validation when supported by the renderer API. <strong>Only for developer use.</strong>"));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.preferGLESContext, tr("Prefer OpenGL ES Context"), tr("Unchecked"),
|
|
tr("Uses OpenGL ES even when desktop OpenGL is supported. May improve performance on some SBC drivers."));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.disableShaderCache, tr("Disable Shader Cache"), tr("Unchecked"),
|
|
tr("Forces shaders to be compiled for every run of the program. <strong>Only for developer use.</strong>"));
|
|
dialog->registerWidgetHelp(m_ui.disableDualSource, tr("Disable Dual-Source Blending"), tr("Unchecked"),
|
|
tr("Prevents dual-source blending from being used. Useful for testing broken graphics "
|
|
"drivers. <strong>Only for developer use.</strong>"));
|
|
dialog->registerWidgetHelp(m_ui.disableFramebufferFetch, tr("Disable Framebuffer Fetch"), tr("Unchecked"),
|
|
tr("Prevents the framebuffer fetch extensions from being used. Useful for testing broken "
|
|
"graphics drivers. <strong>Only for developer use.</strong>"));
|
|
dialog->registerWidgetHelp(
|
|
m_ui.disableTextureBuffers, tr("Disable Texture Buffers"), tr("Unchecked"),
|
|
tr("Forces VRAM updates through texture updates, instead of texture buffers and draws. Useful for testing broken "
|
|
"graphics drivers. <strong>Only for developer use.</strong>"));
|
|
dialog->registerWidgetHelp(m_ui.disableTextureCopyToSelf, tr("Disable Texture Copies To Self"), tr("Unchecked"),
|
|
tr("Disables the use of self-copy updates for the VRAM texture. Useful for testing broken "
|
|
"graphics drivers. <strong>Only for developer use.</strong>"));
|
|
dialog->registerWidgetHelp(m_ui.disableMemoryImport, tr("Disable Memory Import"), tr("Unchecked"),
|
|
tr("Disables the use of host memory importing. Useful for testing broken graphics "
|
|
"drivers. <strong>Only for developer use.</strong>"));
|
|
dialog->registerWidgetHelp(m_ui.disableRasterOrderViews, tr("Disable Rasterizer Order Views"), tr("Unchecked"),
|
|
tr("Disables the use of rasterizer order views. Useful for testing broken graphics "
|
|
"drivers. <strong>Only for developer use.</strong>"));
|
|
dialog->registerWidgetHelp(m_ui.disableComputeShaders, tr("Disable Compute Shaders"), tr("Unchecked"),
|
|
tr("Disables the use of compute shaders. Useful for testing broken graphics drivers. "
|
|
"<strong>Only for developer use.</strong>"));
|
|
dialog->registerWidgetHelp(m_ui.disableCompressedTextures, tr("Disable Compressed Textures"), tr("Unchecked"),
|
|
tr("Disables the use of compressed textures. Useful for testing broken graphics drivers. "
|
|
"<strong>Only for developer use.</strong>"));
|
|
}
|
|
|
|
GraphicsSettingsWidget::~GraphicsSettingsWidget() = default;
|
|
|
|
void GraphicsSettingsWidget::removePlatformSpecificUi()
|
|
{
|
|
#ifndef _WIN32
|
|
m_ui.advancedDisplayOptionsLayout->removeWidget(m_ui.blitSwapChain);
|
|
delete m_ui.blitSwapChain;
|
|
m_ui.blitSwapChain = nullptr;
|
|
#endif
|
|
}
|
|
|
|
GPURenderer GraphicsSettingsWidget::getEffectiveRenderer() const
|
|
{
|
|
return Settings::ParseRendererName(
|
|
m_dialog
|
|
->getEffectiveStringValue("GPU", "Renderer", Settings::GetRendererName(Settings::DEFAULT_GPU_RENDERER))
|
|
.c_str())
|
|
.value_or(Settings::DEFAULT_GPU_RENDERER);
|
|
}
|
|
|
|
bool GraphicsSettingsWidget::effectiveRendererIsHardware() const
|
|
{
|
|
return (getEffectiveRenderer() != GPURenderer::Software);
|
|
}
|
|
|
|
void GraphicsSettingsWidget::onShowDebugSettingsChanged(bool enabled)
|
|
{
|
|
m_ui.tabs->setTabVisible(TAB_INDEX_DEBUGGING, enabled);
|
|
}
|
|
|
|
void GraphicsSettingsWidget::updateRendererDependentOptions()
|
|
{
|
|
const GPURenderer renderer = getEffectiveRenderer();
|
|
const RenderAPI render_api = Settings::GetRenderAPIForRenderer(renderer);
|
|
const bool is_hardware = (renderer != GPURenderer::Software);
|
|
|
|
m_ui.resolutionScale->setEnabled(is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::DisableUpscaling));
|
|
m_ui.resolutionScaleLabel->setEnabled(is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::DisableUpscaling));
|
|
m_ui.msaaMode->setEnabled(is_hardware);
|
|
m_ui.msaaModeLabel->setEnabled(is_hardware);
|
|
m_ui.textureFiltering->setEnabled(is_hardware &&
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableTextureFiltering));
|
|
m_ui.textureFilteringLabel->setEnabled(is_hardware &&
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableTextureFiltering));
|
|
m_ui.spriteTextureFiltering->setEnabled(is_hardware &&
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableTextureFiltering) &&
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableSpriteTextureFiltering));
|
|
m_ui.spriteTextureFilteringLabel->setEnabled(
|
|
is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::DisableTextureFiltering) &&
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableSpriteTextureFiltering));
|
|
m_ui.gpuDownsampleLabel->setEnabled(is_hardware);
|
|
m_ui.gpuDownsampleMode->setEnabled(is_hardware);
|
|
m_ui.gpuDownsampleScale->setEnabled(is_hardware);
|
|
m_ui.gpuDitheringModeLabel->setEnabled(is_hardware);
|
|
m_ui.gpuDitheringMode->setEnabled(is_hardware);
|
|
m_ui.pgxpEnable->setEnabled(is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXP));
|
|
|
|
m_ui.gpuLineDetectMode->setEnabled(is_hardware);
|
|
m_ui.gpuLineDetectModeLabel->setEnabled(is_hardware);
|
|
m_ui.gpuWireframeMode->setEnabled(is_hardware);
|
|
m_ui.gpuWireframeModeLabel->setEnabled(is_hardware);
|
|
m_ui.scaledInterlacing->setEnabled(is_hardware &&
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisableScaledInterlacing));
|
|
m_ui.useSoftwareRendererForReadbacks->setEnabled(
|
|
is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::ForceSoftwareRendererForReadbacks));
|
|
m_ui.forceRoundedTexcoords->setEnabled(
|
|
is_hardware && !m_dialog->hasGameTrait(GameDatabase::Trait::ForceRoundUpscaledTextureCoordinates));
|
|
|
|
m_ui.tabs->setTabEnabled(TAB_INDEX_TEXTURE_REPLACEMENTS, is_hardware);
|
|
|
|
#ifdef _WIN32
|
|
m_ui.blitSwapChain->setVisible(render_api == RenderAPI::D3D11);
|
|
#endif
|
|
|
|
populateGPUAdaptersAndResolutions(render_api);
|
|
updatePGXPSettingsEnabled();
|
|
updateResolutionDependentOptions();
|
|
}
|
|
|
|
void GraphicsSettingsWidget::populateGPUAdaptersAndResolutions(RenderAPI render_api)
|
|
{
|
|
// Don't re-query, it's expensive.
|
|
if (m_adapters_render_api != render_api)
|
|
{
|
|
m_adapters_render_api = render_api;
|
|
|
|
QtAsyncTask::create(this, [this, render_api]() {
|
|
Error error;
|
|
std::optional<GPUDevice::AdapterInfoList> adapters =
|
|
GPUDevice::GetAdapterListForAPI(render_api, QtUtils::GetWindowInfoType(), &error);
|
|
if (!adapters.has_value())
|
|
{
|
|
ERROR_LOG("Failed to get adapter list for {} API: {}", GPUDevice::RenderAPIToString(render_api),
|
|
error.GetDescription());
|
|
adapters.emplace();
|
|
}
|
|
|
|
return [this, adapters = std::move(adapters.value()), render_api]() mutable {
|
|
if (m_adapters_render_api != render_api)
|
|
return;
|
|
|
|
m_adapters = std::move(adapters);
|
|
populateGPUAdaptersAndResolutions(render_api);
|
|
};
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
const GPUDevice::AdapterInfo* current_adapter = nullptr;
|
|
SettingsInterface* const sif = m_dialog->getSettingsInterface();
|
|
|
|
{
|
|
SettingWidgetBinder::DisconnectWidget(m_ui.adapter);
|
|
m_ui.adapter->clear();
|
|
m_ui.adapter->addItem(tr("Default"), QVariant(QString()));
|
|
|
|
const std::string current_adapter_name = m_dialog->getEffectiveStringValue("GPU", "Adapter", "");
|
|
for (const GPUDevice::AdapterInfo& adapter : m_adapters)
|
|
{
|
|
if (adapter.name.empty())
|
|
continue;
|
|
|
|
const QString qadaptername = QString::fromStdString(adapter.name);
|
|
m_ui.adapter->addItem(qadaptername, QVariant(qadaptername));
|
|
if (adapter.name == current_adapter_name)
|
|
current_adapter = &adapter;
|
|
}
|
|
|
|
if (!m_adapters.empty())
|
|
{
|
|
if (current_adapter_name.empty())
|
|
{
|
|
// default adapter
|
|
current_adapter = &m_adapters.front();
|
|
}
|
|
else if (!current_adapter)
|
|
{
|
|
// if the adapter is not available, ensure it's in the list anyway, otherwise select the default
|
|
const QString qadaptername = QString::fromStdString(current_adapter_name);
|
|
m_ui.adapter->addItem(qadaptername, QVariant(qadaptername));
|
|
}
|
|
}
|
|
|
|
// disable it if we don't have a choice
|
|
m_ui.adapter->setEnabled(!m_adapters.empty());
|
|
m_ui.adapterLabel->setEnabled(!m_adapters.empty());
|
|
SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.adapter, "GPU", "Adapter");
|
|
connect(m_ui.adapter, &QComboBox::currentIndexChanged, this,
|
|
&GraphicsSettingsWidget::updateRendererDependentOptions);
|
|
}
|
|
|
|
{
|
|
SettingWidgetBinder::DisconnectWidget(m_ui.fullscreenMode);
|
|
m_ui.fullscreenMode->clear();
|
|
|
|
m_ui.fullscreenMode->addItem(tr("Borderless Fullscreen"), QVariant(QString()));
|
|
|
|
const std::string current_fullscreen_mode = m_dialog->getEffectiveStringValue("GPU", "FullscreenMode", "");
|
|
bool current_fullscreen_mode_found = false;
|
|
if (current_adapter)
|
|
{
|
|
for (const GPUDevice::ExclusiveFullscreenMode& mode : current_adapter->fullscreen_modes)
|
|
{
|
|
const TinyString mode_str = mode.ToString();
|
|
current_fullscreen_mode_found = current_fullscreen_mode_found || (current_fullscreen_mode == mode_str.view());
|
|
|
|
const QString qmodename = QtUtils::StringViewToQString(mode_str);
|
|
m_ui.fullscreenMode->addItem(qmodename, QVariant(qmodename));
|
|
}
|
|
}
|
|
|
|
// if the current mode is not valid (e.g. adapter change), ensure it's in the list so the user isn't confused
|
|
if (!current_fullscreen_mode_found && !current_fullscreen_mode.empty())
|
|
{
|
|
const QString qmodename = QtUtils::StringViewToQString(current_fullscreen_mode);
|
|
m_ui.fullscreenMode->addItem(qmodename, QVariant(qmodename));
|
|
}
|
|
|
|
// disable it if we don't have a choice
|
|
const bool has_fullscreen_modes = (current_adapter && !current_adapter->fullscreen_modes.empty());
|
|
const bool has_exclusive_fullscreen_control = (render_api == RenderAPI::Vulkan);
|
|
m_ui.fullscreenMode->setVisible(has_fullscreen_modes);
|
|
if (has_fullscreen_modes)
|
|
SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.fullscreenMode, "GPU", "FullscreenMode");
|
|
m_ui.exclusiveFullscreenControl->setVisible(has_exclusive_fullscreen_control);
|
|
m_ui.exclusiveFullscreenLabel->setVisible(has_fullscreen_modes || has_exclusive_fullscreen_control);
|
|
}
|
|
|
|
if (!m_dialog->hasGameTrait(GameDatabase::Trait::DisableUpscaling))
|
|
{
|
|
const int max_scale =
|
|
static_cast<int>(current_adapter ? std::clamp<u32>(current_adapter->max_texture_size / 1024u, 1u, 32u) : 16);
|
|
populateAndConnectUpscalingModes(max_scale);
|
|
}
|
|
|
|
{
|
|
SettingWidgetBinder::DisconnectWidget(m_ui.msaaMode);
|
|
m_ui.msaaMode->clear();
|
|
|
|
if (m_dialog->isPerGameSettings())
|
|
m_ui.msaaMode->addItem(tr("Use Global Setting"));
|
|
|
|
const u32 max_multisamples = current_adapter ? current_adapter->max_multisamples : 8;
|
|
m_ui.msaaMode->addItem(tr("Disabled"), GetMSAAModeValue(1, false));
|
|
for (uint i = 2; i <= max_multisamples; i *= 2)
|
|
m_ui.msaaMode->addItem(tr("%1x MSAA").arg(i), GetMSAAModeValue(i, false));
|
|
for (uint i = 2; i <= max_multisamples; i *= 2)
|
|
m_ui.msaaMode->addItem(tr("%1x SSAA").arg(i), GetMSAAModeValue(i, true));
|
|
|
|
if (!m_dialog->isPerGameSettings() || (m_dialog->containsSettingValue("GPU", "Multisamples") ||
|
|
m_dialog->containsSettingValue("GPU", "PerSampleShading")))
|
|
{
|
|
const QVariant current_msaa_mode(
|
|
GetMSAAModeValue(static_cast<uint>(m_dialog->getEffectiveIntValue("GPU", "Multisamples", 1)),
|
|
m_dialog->getEffectiveBoolValue("GPU", "PerSampleShading", false)));
|
|
const int current_msaa_index = m_ui.msaaMode->findData(current_msaa_mode);
|
|
if (current_msaa_index >= 0)
|
|
m_ui.msaaMode->setCurrentIndex(current_msaa_index);
|
|
}
|
|
else
|
|
{
|
|
m_ui.msaaMode->setCurrentIndex(0);
|
|
}
|
|
connect(m_ui.msaaMode, &QComboBox::currentIndexChanged, this, [this]() {
|
|
const int index = m_ui.msaaMode->currentIndex();
|
|
if (m_dialog->isPerGameSettings() && index == 0)
|
|
{
|
|
m_dialog->removeSettingValue("GPU", "Multisamples");
|
|
m_dialog->removeSettingValue("GPU", "PerSampleShading");
|
|
}
|
|
else
|
|
{
|
|
uint multisamples;
|
|
bool ssaa;
|
|
DecodeMSAAModeValue(m_ui.msaaMode->itemData(index), &multisamples, &ssaa);
|
|
m_dialog->setIntSettingValue("GPU", "Multisamples", static_cast<int>(multisamples));
|
|
m_dialog->setBoolSettingValue("GPU", "PerSampleShading", ssaa);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void GraphicsSettingsWidget::populateAndConnectUpscalingModes(int max_scale)
|
|
{
|
|
SettingWidgetBinder::DisconnectWidget(m_ui.resolutionScale);
|
|
m_ui.resolutionScale->clear();
|
|
|
|
populateUpscalingModes(m_ui.resolutionScale, max_scale);
|
|
|
|
SettingWidgetBinder::BindWidgetToIntSetting(m_dialog->getSettingsInterface(), m_ui.resolutionScale, "GPU",
|
|
"ResolutionScale", 1);
|
|
connect(m_ui.resolutionScale, &QComboBox::currentIndexChanged, this,
|
|
&GraphicsSettingsWidget::updateResolutionDependentOptions);
|
|
}
|
|
|
|
void GraphicsSettingsWidget::populateUpscalingModes(QComboBox* const cb, int max_scale)
|
|
{
|
|
static constexpr const std::pair<int, const char*> templates[] = {
|
|
{0, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Automatic (Based on Window Size)")},
|
|
{1, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "1x Native (Default)")},
|
|
{3, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "3x Native (for 720p)")},
|
|
{5, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "5x Native (for 1080p)")},
|
|
{6, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "6x Native (for 1440p)")},
|
|
{9, QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "9x Native (for 4K)")},
|
|
};
|
|
for (int scale = 0; scale <= max_scale; scale++)
|
|
{
|
|
const auto it = std::find_if(std::begin(templates), std::end(templates),
|
|
[&scale](const std::pair<int, const char*>& it) { return scale == it.first; });
|
|
cb->addItem((it != std::end(templates)) ? qApp->translate("GraphicsSettingsWidget", it->second) :
|
|
qApp->translate("GraphicsSettingsWidget", "%1x Native").arg(scale));
|
|
}
|
|
}
|
|
|
|
QVariant GraphicsSettingsWidget::packAspectRatio(DisplayAspectRatio ar)
|
|
{
|
|
return QVariant(static_cast<uint>(ar.numerator) << 16 | static_cast<uint>(ar.denominator));
|
|
}
|
|
|
|
DisplayAspectRatio GraphicsSettingsWidget::unpackAspectRatio(const QVariant& var)
|
|
{
|
|
const uint packed = var.toUInt();
|
|
return DisplayAspectRatio{static_cast<s16>(packed >> 16), static_cast<s16>(packed & 0xFFFFu)};
|
|
}
|
|
|
|
void GraphicsSettingsWidget::createAspectRatioSetting(QComboBox* const cb, QSpinBox* const numerator,
|
|
QLabel* const separator, QSpinBox* const denominator,
|
|
SettingsInterface* const sif)
|
|
{
|
|
static constexpr const char* CONFIG_SECTION = "Display";
|
|
static constexpr const char* CONFIG_KEY = "AspectRatio";
|
|
|
|
// AR requires special handling because of the custom option.
|
|
if (sif)
|
|
{
|
|
cb->addItem(qApp->translate("SettingsDialog", "Use Global Setting [%1]")
|
|
.arg(QtUtils::StringViewToQString(Settings::GetDisplayAspectRatioDisplayName(
|
|
Settings::ParseDisplayAspectRatio(Core::GetBaseStringSettingValue(CONFIG_SECTION, CONFIG_KEY))
|
|
.value_or(Settings::DEFAULT_DISPLAY_ASPECT_RATIO)))));
|
|
}
|
|
for (const DisplayAspectRatio& ratio : Settings::GetPredefinedDisplayAspectRatios())
|
|
{
|
|
cb->addItem(QtUtils::StringViewToQString(Settings::GetDisplayAspectRatioDisplayName(ratio)),
|
|
packAspectRatio(ratio));
|
|
}
|
|
cb->addItem(tr("Custom"));
|
|
|
|
bool is_custom_ar = false;
|
|
if (sif && !sif->ContainsValue(CONFIG_SECTION, CONFIG_KEY))
|
|
{
|
|
cb->setCurrentIndex(0);
|
|
}
|
|
else
|
|
{
|
|
const DisplayAspectRatio ar =
|
|
Settings::ParseDisplayAspectRatio(sif ? sif->GetStringValue(CONFIG_SECTION, CONFIG_KEY) :
|
|
Core::GetBaseStringSettingValue(CONFIG_SECTION, CONFIG_KEY))
|
|
.value_or(Settings::DEFAULT_DISPLAY_ASPECT_RATIO);
|
|
if ((is_custom_ar = std::ranges::none_of(Settings::GetPredefinedDisplayAspectRatios(),
|
|
[&ar](const auto& it) { return (it == ar); })))
|
|
{
|
|
cb->setCurrentIndex(cb->count() - 1);
|
|
numerator->setValue(ar.numerator);
|
|
denominator->setValue(ar.denominator);
|
|
}
|
|
else
|
|
{
|
|
cb->setCurrentIndex(cb->findData(packAspectRatio(ar)));
|
|
}
|
|
}
|
|
numerator->setVisible(is_custom_ar);
|
|
separator->setVisible(is_custom_ar);
|
|
denominator->setVisible(is_custom_ar);
|
|
|
|
auto value_changed = [cb, numerator, separator, denominator, sif]() {
|
|
std::optional<DisplayAspectRatio> value_to_save;
|
|
const int index = cb->currentIndex();
|
|
bool is_custom = false;
|
|
if (!sif || index > 0)
|
|
{
|
|
if (index == (cb->count() - 1))
|
|
{
|
|
is_custom = true;
|
|
value_to_save.emplace(static_cast<s16>(numerator->value()), static_cast<s16>(denominator->value()));
|
|
}
|
|
else
|
|
{
|
|
value_to_save.emplace(unpackAspectRatio(cb->currentData()));
|
|
}
|
|
}
|
|
|
|
numerator->setVisible(is_custom);
|
|
denominator->setVisible(is_custom);
|
|
separator->setVisible(is_custom);
|
|
|
|
if (sif)
|
|
{
|
|
if (value_to_save.has_value())
|
|
{
|
|
sif->SetStringValue(CONFIG_SECTION, CONFIG_KEY,
|
|
Settings::GetDisplayAspectRatioName(value_to_save.value()).c_str());
|
|
}
|
|
else
|
|
{
|
|
sif->DeleteValue(CONFIG_SECTION, CONFIG_KEY);
|
|
}
|
|
|
|
QtHost::SaveGameSettings(sif, true);
|
|
g_core_thread->reloadGameSettings();
|
|
}
|
|
else
|
|
{
|
|
if (value_to_save.has_value())
|
|
{
|
|
Core::SetBaseStringSettingValue(CONFIG_SECTION, CONFIG_KEY,
|
|
Settings::GetDisplayAspectRatioName(value_to_save.value()).c_str());
|
|
}
|
|
else
|
|
{
|
|
Core::DeleteBaseSettingValue(CONFIG_SECTION, CONFIG_KEY);
|
|
}
|
|
|
|
Host::CommitBaseSettingChanges();
|
|
g_core_thread->applySettings();
|
|
}
|
|
};
|
|
|
|
connect(cb, &QComboBox::currentIndexChanged, cb, value_changed);
|
|
connect(numerator, &QSpinBox::valueChanged, cb, value_changed);
|
|
connect(denominator, &QSpinBox::valueChanged, cb, std::move(value_changed));
|
|
}
|
|
|
|
void GraphicsSettingsWidget::updatePGXPSettingsEnabled()
|
|
{
|
|
const bool enabled = (effectiveRendererIsHardware() && m_dialog->getEffectiveBoolValue("GPU", "PGXPEnable", false) &&
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXP));
|
|
const bool tc_enabled = (enabled && m_dialog->getEffectiveBoolValue("GPU", "PGXPTextureCorrection", true));
|
|
const bool depth_enabled = (enabled && m_dialog->getEffectiveBoolValue("GPU", "PGXPDepthBuffer", false));
|
|
m_ui.tabs->setTabEnabled(TAB_INDEX_PGXP, enabled);
|
|
m_ui.pgxpTab->setEnabled(enabled);
|
|
m_ui.pgxpCulling->setEnabled(enabled && !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPCulling));
|
|
m_ui.pgxpTextureCorrection->setEnabled(enabled &&
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPTextureCorrection));
|
|
m_ui.pgxpColorCorrection->setEnabled(tc_enabled &&
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPColorCorrection));
|
|
m_ui.pgxpDepthBuffer->setEnabled(enabled && !m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPDepthBuffer));
|
|
m_ui.pgxpPreserveProjPrecision->setEnabled(
|
|
enabled && (!m_dialog->hasDatabaseEntry() || !m_dialog->getDatabaseEntry()->gpu_pgxp_preserve_proj_fp.has_value()));
|
|
m_ui.pgxpCPU->setEnabled(enabled && !m_dialog->hasGameTrait(GameDatabase::Trait::ForcePGXPCPUMode));
|
|
m_ui.pgxpVertexCache->setEnabled(enabled && !m_dialog->hasGameTrait(GameDatabase::Trait::ForcePGXPVertexCache));
|
|
m_ui.pgxpGeometryTolerance->setEnabled(enabled);
|
|
m_ui.pgxpGeometryToleranceLabel->setEnabled(enabled);
|
|
m_ui.pgxpDepthClearThreshold->setEnabled(depth_enabled);
|
|
m_ui.pgxpDepthClearThresholdLabel->setEnabled(depth_enabled);
|
|
m_ui.pgxpDisableOn2DPolygons->setEnabled(enabled &&
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::DisablePGXPOn2DPolygons));
|
|
m_ui.pgxpTransparentDepthTest->setEnabled(depth_enabled);
|
|
}
|
|
|
|
void GraphicsSettingsWidget::updateResolutionDependentOptions()
|
|
{
|
|
const bool is_hardware = (getEffectiveRenderer() != GPURenderer::Software);
|
|
const int scale = m_dialog->getEffectiveIntValue("GPU", "ResolutionScale", 1);
|
|
const GPUTextureFilter texture_filtering =
|
|
Settings::ParseTextureFilterName(m_dialog->getEffectiveStringValue("GPU", "TextureFilter").c_str())
|
|
.value_or(Settings::DEFAULT_GPU_TEXTURE_FILTER);
|
|
const GPUTextureFilter sprite_texture_filtering =
|
|
Settings::ParseTextureFilterName(m_dialog->getEffectiveStringValue("GPU", "SpriteTextureFilter").c_str())
|
|
.value_or(texture_filtering);
|
|
m_ui.forceRoundedTexcoords->setEnabled(
|
|
is_hardware && scale != 1 &&
|
|
(texture_filtering == GPUTextureFilter::Nearest || sprite_texture_filtering == GPUTextureFilter::Nearest) &&
|
|
!m_dialog->hasGameTrait(GameDatabase::Trait::ForceRoundUpscaledTextureCoordinates));
|
|
}
|
|
|
|
void GraphicsSettingsWidget::onDownsampleModeChanged()
|
|
{
|
|
const GPUDownsampleMode mode =
|
|
Settings::ParseDownsampleModeName(
|
|
m_dialog
|
|
->getEffectiveStringValue("GPU", "DownsampleMode",
|
|
Settings::GetDownsampleModeName(Settings::DEFAULT_GPU_DOWNSAMPLE_MODE))
|
|
.c_str())
|
|
.value_or(Settings::DEFAULT_GPU_DOWNSAMPLE_MODE);
|
|
|
|
const bool visible = (mode == GPUDownsampleMode::Box);
|
|
if (visible && m_ui.gpuDownsampleLayout->indexOf(m_ui.gpuDownsampleScale) < 0)
|
|
{
|
|
m_ui.gpuDownsampleScale->setVisible(true);
|
|
m_ui.gpuDownsampleLayout->addWidget(m_ui.gpuDownsampleScale, 0);
|
|
}
|
|
else if (!visible && m_ui.gpuDownsampleLayout->indexOf(m_ui.gpuDownsampleScale) >= 0)
|
|
{
|
|
m_ui.gpuDownsampleScale->setVisible(false);
|
|
m_ui.gpuDownsampleLayout->removeWidget(m_ui.gpuDownsampleScale);
|
|
}
|
|
}
|
|
|
|
void GraphicsSettingsWidget::onFineCropModeChanged()
|
|
{
|
|
const DisplayFineCropMode mode =
|
|
Settings::ParseDisplayFineCropMode(m_dialog->getEffectiveStringValue("Display", "FineCropMode", "").c_str())
|
|
.value_or(Settings::DEFAULT_DISPLAY_FINE_CROP_MODE);
|
|
const bool enabled = (mode != DisplayFineCropMode::None);
|
|
m_ui.displayFineCropLabel->setEnabled(enabled);
|
|
m_ui.displayFineCropLeftLabel->setEnabled(enabled);
|
|
m_ui.displayFineCropLeft->setEnabled(enabled);
|
|
m_ui.displayFineCropTopLabel->setEnabled(enabled);
|
|
m_ui.displayFineCropTop->setEnabled(enabled);
|
|
m_ui.displayFineCropRightLabel->setEnabled(enabled);
|
|
m_ui.displayFineCropRight->setEnabled(enabled);
|
|
m_ui.displayFineCropBottomLabel->setEnabled(enabled);
|
|
m_ui.displayFineCropBottom->setEnabled(enabled);
|
|
}
|
|
|
|
void GraphicsSettingsWidget::onFineCropResetClicked()
|
|
{
|
|
if (m_dialog->isPerGameSettings())
|
|
{
|
|
// Super nasty.. need to set the default without the signal firing and writing the global value to the ini...
|
|
const auto reset = [this]<typename T>(T* widget, const char* key) {
|
|
const QSignalBlocker sb(widget);
|
|
if constexpr (std::is_same_v<T, QComboBox>)
|
|
SettingWidgetBinder::SettingAccessor<T>::setNullableStringValue(widget, std::nullopt);
|
|
else
|
|
SettingWidgetBinder::SettingAccessor<QSpinBox>::setNullableIntValue(widget, std::nullopt);
|
|
|
|
m_dialog->removeSettingValue("Display", key);
|
|
};
|
|
|
|
reset(m_ui.displayFineCropMode, "FineCropMode");
|
|
reset(m_ui.displayFineCropLeft, "FineCropLeft");
|
|
reset(m_ui.displayFineCropTop, "FineCropTop");
|
|
reset(m_ui.displayFineCropRight, "FineCropRight");
|
|
reset(m_ui.displayFineCropBottom, "FineCropBottom");
|
|
}
|
|
else
|
|
{
|
|
m_ui.displayFineCropMode->setCurrentIndex(0);
|
|
m_ui.displayFineCropLeft->setValue(0);
|
|
m_ui.displayFineCropTop->setValue(0);
|
|
m_ui.displayFineCropRight->setValue(0);
|
|
m_ui.displayFineCropBottom->setValue(0);
|
|
}
|
|
}
|
|
|
|
void GraphicsSettingsWidget::onEnableTextureCacheChanged()
|
|
{
|
|
const bool tc_enabled = m_dialog->getEffectiveBoolValue("GPU", "EnableTextureCache", false);
|
|
m_ui.enableTextureReplacements->setEnabled(tc_enabled);
|
|
m_ui.enableTextureDumping->setEnabled(tc_enabled);
|
|
m_ui.alwaysTrackUploads->setEnabled(tc_enabled);
|
|
onEnableAnyTextureDumpingChanged();
|
|
onEnableAnyTextureReplacementsChanged();
|
|
}
|
|
|
|
void GraphicsSettingsWidget::onEnableAnyTextureDumpingChanged()
|
|
{
|
|
const bool tc_enabled = m_dialog->getEffectiveBoolValue("GPU", "EnableTextureCache", false);
|
|
const bool dumping_enabled =
|
|
(tc_enabled && m_dialog->getEffectiveBoolValue("TextureReplacements", "DumpTextures", false));
|
|
const bool background_dumping_enabled =
|
|
m_dialog->getEffectiveBoolValue("TextureReplacements", "DumpVRAMWrites", false);
|
|
const bool any_dumping_enabled = (dumping_enabled || background_dumping_enabled);
|
|
m_ui.dumpReplacedTextures->setEnabled(any_dumping_enabled);
|
|
}
|
|
|
|
void GraphicsSettingsWidget::onEnableAnyTextureReplacementsChanged()
|
|
{
|
|
const bool any_replacements_enabled =
|
|
(m_dialog->getEffectiveBoolValue("TextureReplacements", "EnableVRAMWriteReplacements", false) ||
|
|
(m_dialog->getEffectiveBoolValue("GPU", "EnableTextureCache", false) &&
|
|
m_dialog->getEffectiveBoolValue("TextureReplacements", "EnableTextureReplacements", false)));
|
|
m_ui.preloadTextureReplacements->setEnabled(any_replacements_enabled);
|
|
}
|
|
|
|
void GraphicsSettingsWidget::onGPUThreadChanged()
|
|
{
|
|
const bool enabled = m_dialog->getEffectiveBoolValue("GPU", "UseThread", true);
|
|
m_ui.maxQueuedFrames->setEnabled(enabled);
|
|
m_ui.maxQueuedFramesLabel->setEnabled(enabled);
|
|
}
|
|
|
|
TextureReplacementSettingsDialog::TextureReplacementSettingsDialog(SettingsWindow* settings_window, QWidget* parent)
|
|
: QDialog(parent)
|
|
{
|
|
m_ui.setupUi(this);
|
|
m_ui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("image-fill")).pixmap(32));
|
|
|
|
constexpr Settings::TextureReplacementSettings::Configuration default_replacement_config;
|
|
SettingsInterface* const sif = settings_window->getSettingsInterface();
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.dumpTexturePages, "TextureReplacements", "DumpTexturePages",
|
|
default_replacement_config.dump_texture_pages);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.dumpFullTexturePages, "TextureReplacements",
|
|
"DumpFullTexturePages",
|
|
default_replacement_config.dump_full_texture_pages);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.dumpC16Textures, "TextureReplacements", "DumpC16Textures",
|
|
default_replacement_config.dump_c16_textures);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.reducePaletteRange, "TextureReplacements",
|
|
"ReducePaletteRange", default_replacement_config.reduce_palette_range);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.convertCopiesToWrites, "TextureReplacements",
|
|
"ConvertCopiesToWrites",
|
|
default_replacement_config.convert_copies_to_writes);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.replacementScaleLinearFilter, "TextureReplacements",
|
|
"ReplacementScaleLinearFilter",
|
|
default_replacement_config.replacement_scale_linear_filter);
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.maxVRAMWriteSplits, "TextureReplacements", "MaxVRAMWriteSplits",
|
|
default_replacement_config.max_vram_write_splits);
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.maxVRAMWriteCoalesceWidth, "TextureReplacements",
|
|
"MaxVRAMWriteCoalesceWidth",
|
|
default_replacement_config.max_vram_write_coalesce_width);
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.maxVRAMWriteCoalesceHeight, "TextureReplacements",
|
|
"MaxVRAMWriteCoalesceHeight",
|
|
default_replacement_config.max_vram_write_coalesce_height);
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.minDumpedTextureWidth, "TextureReplacements",
|
|
"DumpTextureWidthThreshold",
|
|
default_replacement_config.texture_dump_width_threshold);
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.minDumpedTextureHeight, "TextureReplacements",
|
|
"DumpTextureHeightThreshold",
|
|
default_replacement_config.texture_dump_height_threshold);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.setTextureDumpAlphaChannel, "TextureReplacements",
|
|
"DumpTextureForceAlphaChannel",
|
|
default_replacement_config.dump_texture_force_alpha_channel);
|
|
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.minDumpedVRAMWriteWidth, "TextureReplacements",
|
|
"DumpVRAMWriteWidthThreshold",
|
|
default_replacement_config.vram_write_dump_width_threshold);
|
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.minDumpedVRAMWriteHeight, "TextureReplacements",
|
|
"DumpVRAMWriteHeightThreshold",
|
|
default_replacement_config.vram_write_dump_height_threshold);
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.setVRAMWriteAlphaChannel, "TextureReplacements",
|
|
"DumpVRAMWriteForceAlphaChannel",
|
|
default_replacement_config.dump_vram_write_force_alpha_channel);
|
|
|
|
m_ui.dumpFullTexturePages->setEnabled(
|
|
settings_window->getEffectiveBoolValue("TextureReplacements", "DumpTexturePages", false));
|
|
connect(m_ui.dumpTexturePages, &QCheckBox::checkStateChanged, this, [this, settings_window] {
|
|
m_ui.dumpFullTexturePages->setEnabled(
|
|
settings_window->getEffectiveBoolValue("TextureReplacements", "DumpTexturePages", false));
|
|
});
|
|
connect(m_ui.closeButton, &QAbstractButton::clicked, this, &QDialog::accept);
|
|
connect(m_ui.exportButton, &QAbstractButton::clicked, this, &TextureReplacementSettingsDialog::onExportClicked);
|
|
}
|
|
|
|
void TextureReplacementSettingsDialog::onExportClicked()
|
|
{
|
|
Settings::TextureReplacementSettings::Configuration config;
|
|
|
|
config.dump_texture_pages = m_ui.dumpTexturePages->isChecked();
|
|
config.dump_full_texture_pages = m_ui.dumpFullTexturePages->isChecked();
|
|
config.dump_c16_textures = m_ui.dumpC16Textures->isChecked();
|
|
config.reduce_palette_range = m_ui.reducePaletteRange->isChecked();
|
|
config.convert_copies_to_writes = m_ui.convertCopiesToWrites->isChecked();
|
|
config.replacement_scale_linear_filter = m_ui.replacementScaleLinearFilter->isChecked();
|
|
config.max_vram_write_splits = static_cast<u16>(m_ui.maxVRAMWriteSplits->value());
|
|
config.max_vram_write_coalesce_width = static_cast<u16>(m_ui.maxVRAMWriteCoalesceWidth->value());
|
|
config.max_vram_write_coalesce_height = static_cast<u16>(m_ui.maxVRAMWriteCoalesceHeight->value());
|
|
config.texture_dump_width_threshold = static_cast<u16>(m_ui.minDumpedTextureWidth->value());
|
|
config.texture_dump_height_threshold = static_cast<u16>(m_ui.minDumpedTextureHeight->value());
|
|
config.dump_texture_force_alpha_channel = m_ui.setTextureDumpAlphaChannel->isChecked();
|
|
config.vram_write_dump_width_threshold = static_cast<u16>(m_ui.minDumpedVRAMWriteWidth->value());
|
|
config.vram_write_dump_height_threshold = static_cast<u16>(m_ui.minDumpedVRAMWriteHeight->value());
|
|
config.dump_vram_write_force_alpha_channel = m_ui.setTextureDumpAlphaChannel->isChecked();
|
|
|
|
QInputDialog* const idlg = new QInputDialog(this);
|
|
idlg->resize(600, 400);
|
|
idlg->setWindowTitle(tr("Texture Replacement Configuration"));
|
|
idlg->setInputMode(QInputDialog::TextInput);
|
|
idlg->setOption(QInputDialog::UsePlainTextEditForTextInput);
|
|
idlg->setLabelText(tr("Texture Replacement Configuration (config.yaml)"));
|
|
idlg->setTextValue(QString::fromStdString(config.ExportToYAML(false)));
|
|
idlg->setOkButtonText(tr("Save As..."));
|
|
connect(idlg, &QDialog::accepted, [this, idlg]() {
|
|
const QString path =
|
|
QFileDialog::getSaveFileName(this, tr("Save Configuration"), QString(), tr("Configuration Files (config.yaml)"));
|
|
if (path.isEmpty())
|
|
return;
|
|
|
|
Error error;
|
|
if (!FileSystem::WriteStringToFile(QDir::toNativeSeparators(path).toUtf8().constData(),
|
|
idlg->textValue().toStdString(), &error))
|
|
{
|
|
QtUtils::AsyncMessageBox(this, QMessageBox::Critical, tr("Write Failed"),
|
|
QString::fromStdString(error.GetDescription()));
|
|
}
|
|
});
|
|
idlg->open();
|
|
}
|
|
|
|
void GraphicsSettingsWidget::onTextureReplacementOptionsClicked()
|
|
{
|
|
QDialog* const dlg = new TextureReplacementSettingsDialog(m_dialog, this);
|
|
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
|
dlg->open();
|
|
}
|