Files
jpolo1224 001ca40803 GS: add SGSR upscaling (Qualcomm Snapdragon Game Super Resolution 1)
A third display upscaler beside FSR1, and the first one written for the
hardware this app actually runs on: FSR1's two compute passes are the
expensive outlier on mobile, while SGSR is a single pass Qualcomm designed
for Adreno.

The filter is Qualcomm's, BSD-3-Clause, unchanged in substance. What differs
is the shape around it -- theirs is a fragment shader over a fullscreen
triangle, this is a compute pass, because that is what GSDevice already knows
how to schedule. So the interpolated texcoord becomes a UV computed from the
invocation id and the fragment output becomes an imageStore. The crop
handling and the widened 0..2 sharpness range come from the Eden Emulator
Project's port (GPL-3.0-or-later, compatible with PCSX2's GPL-3.0+); PCSX2
hands the pass a display rectangle inside a larger target, which is the same
problem FsrEasuConOffset solves for FSR1.

Deliberately a strict subset of what FSR1 already requires -- same descriptor
types, same rgba8 storage image, textureGather with a constant component and
no offset, which is core Vulkan 1.0 and needs no optional feature. So any
driver already running FSR1 can run this, Turnip included, and there is no
vendor gate on either. A driver that cannot compile the pipeline clears
Features().sgsr and the renderer falls back to plain bilinear with an OSD
notice, rather than failing.

The Android upscaler control becomes a picker rather than an on/off toggle:
three mutually exclusive upscalers expressed as two toggles that silently
switch each other off is a worse way to say it than one list. FSR1 and SGSR
share the existing sharpness slider -- the number means different things to
each, but it is the same intent, and a second slider would only invite the
two to disagree.

★ The Settings.kt clamp on the persisted enum was still bounded at
UPSCALER_FSR1, and would have silently rewritten any SGSR selection back to
Off. That clamp's own comment warns about exactly this failure; it still had
to be updated by hand. Worth remembering the next time the enum grows.

Suggested by CamilleLaVey, who authored the upstream changes (eden-emu #4293).
2026-08-24 15:44:20 -04:00

1803 lines
54 KiB
C++

// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include "Host/AudioStreamTypes.h"
#include "common/Pcsx2Defs.h"
#include "common/FPControl.h"
#include <array>
#include <string>
#include <optional>
#include <vector>
// Macro used for removing some of the redtape involved in defining bitfield/union helpers.
//
#define BITFIELD32() \
union \
{ \
u32 bitset; \
struct \
{
#define BITFIELD_END \
} \
; \
} \
;
class Error;
class SettingsInterface;
class SettingsWrapper;
enum class CDVD_SourceType : uint8_t;
namespace Pad
{
enum class ControllerType : u8;
}
/// Generic setting information which can be reused in multiple components.
struct SettingInfo
{
using GetOptionsCallback = std::vector<std::pair<std::string, std::string>> (*)();
enum class Type
{
Boolean,
Integer,
IntegerList,
Float,
String,
StringList,
Path,
};
Type type;
const char* name;
const char* display_name;
const char* description;
const char* default_value;
const char* min_value;
const char* max_value;
const char* step_value;
const char* format;
const char* const* options; // For integer lists.
GetOptionsCallback get_options; // For string lists.
float multiplier;
const char* StringDefaultValue() const;
bool BooleanDefaultValue() const;
s32 IntegerDefaultValue() const;
s32 IntegerMinValue() const;
s32 IntegerMaxValue() const;
s32 IntegerStepValue() const;
float FloatDefaultValue() const;
float FloatMinValue() const;
float FloatMaxValue() const;
float FloatStepValue() const;
void SetDefaultValue(SettingsInterface* si, const char* section, const char* key) const;
void CopyValue(SettingsInterface* dest_si, const SettingsInterface& src_si,
const char* section, const char* key) const;
};
enum class GenericInputBinding : u8;
// TODO(Stenzek): Move to InputCommon.h or something?
struct InputBindingInfo
{
enum class Type : u8
{
Unknown,
Button,
Axis,
HalfAxis,
Motor,
Pointer, // Receive relative mouse movement events, bind_index is offset by the axis.
Keyboard, // Receive host key events, bind_index is offset by the key code.
Device, // Used for special-purpose device selection, e.g. force feedback.
Macro,
};
const char* name;
const char* display_name;
const char* icon_name;
Type bind_type;
u16 bind_index;
GenericInputBinding generic_mapping;
};
/// Generic input bindings. These roughly match a DualShock 4 or XBox One controller.
/// They are used for automatic binding to PS2 controller types, and for big picture mode navigation.
enum class GenericInputBinding : u8
{
Unknown,
DPadUp,
DPadRight,
DPadLeft,
DPadDown,
LeftStickUp,
LeftStickRight,
LeftStickDown,
LeftStickLeft,
L3,
RightStickUp,
RightStickRight,
RightStickDown,
RightStickLeft,
R3,
Triangle, // Y on XBox pads.
Circle, // B on XBox pads.
Cross, // A on XBox pads.
Square, // X on XBox pads.
Select, // Share on DS4, View on XBox pads.
Start, // Options on DS4, Menu on XBox pads.
System, // PS button on DS4, Guide button on XBox pads.
L1, // LB on Xbox pads.
L2, // Left trigger on XBox pads.
R1, // RB on XBox pads.
R2, // Right trigger on Xbox pads.
SmallMotor, // High frequency vibration.
LargeMotor, // Low frequency vibration.
Count,
};
enum GamefixId
{
GamefixId_FIRST = 0,
Fix_FpuMultiply = GamefixId_FIRST,
Fix_GoemonTlbMiss,
Fix_SoftwareRendererFMV,
Fix_SkipMpeg,
Fix_OPHFlag,
Fix_EETiming,
Fix_InstantDMA,
Fix_DMABusy,
Fix_GIFFIFO,
Fix_VIFFIFO,
Fix_VIF1Stall,
Fix_VuAddSub,
Fix_Ibit,
Fix_VUSync,
Fix_VUOverflow,
Fix_XGKick,
Fix_BlitInternalFPS,
Fix_FullVU0Sync,
GamefixId_COUNT
};
// TODO - config - not a fan of the excessive use of enums and macros to make them work
// a proper object would likely make more sense (if possible).
enum class SpeedHack
{
MVUFlag,
InstantVU1,
MTVU,
EECycleRate,
MaxCount,
};
enum class DebugAnalysisCondition
{
ALWAYS,
IF_DEBUGGER_IS_OPEN,
NEVER
};
struct DebugSymbolSource
{
std::string Name;
bool ClearDuringAnalysis = false;
friend auto operator<=>(const DebugSymbolSource& lhs, const DebugSymbolSource& rhs) = default;
};
struct DebugExtraSymbolFile
{
std::string Path;
std::string BaseAddress;
std::string Condition;
friend auto operator<=>(const DebugExtraSymbolFile& lhs, const DebugExtraSymbolFile& rhs) = default;
};
enum class DebugFunctionScanMode
{
SCAN_ELF,
SCAN_MEMORY,
SKIP
};
enum class AspectRatioType : u8
{
Stretch, // Stretches to the whole window/display size
RAuto4_3_3_2, // Automatically scales to the target aspect ratio if there's a widescreen patch
R4_3,
R16_9,
R10_7,
// Ultrawide. Only meaningful with a widescreen/ultrawide patch applied -- without one the
// game still renders 4:3 content and this just crops or pillarboxes it. Requested for fold
// and tablet users, DeX, and phones driving a 21:9 display, who otherwise had to use
// Stretch and accept the distortion.
R21_9,
// 20:9 is the real panel ratio of most modern phones, which 21:9 only approximates -- close
// enough to leave thin black bars. Appended rather than slotted in next to R21_9 on purpose:
// these values are persisted as raw ints in the ini and in the Android prefs, so inserting
// mid-enum would silently repoint every saved 21:9 config at a different ratio.
R20_9,
// 19.5:9 — the other common modern phone ratio (many Xiaomi/Samsung panels). Appended, not
// slotted next to R20_9, for the same persisted-raw-int reason documented above.
R19_5_9,
// User-entered ratio (GSOptions::CustomAspectRatio). Last on purpose: it is the catch-all for
// panels none of the fixed entries match, and appending keeps every persisted index stable.
Custom,
MaxCount
};
enum class FMVAspectRatioSwitchType : u8
{
Off, // Falls back on the selected generic aspect ratio type
RAuto4_3_3_2,
R4_3,
R16_9,
R10_7,
// Ultrawide. Only meaningful with a widescreen/ultrawide patch applied -- without one the
// game still renders 4:3 content and this just crops or pillarboxes it. Requested for fold
// and tablet users, DeX, and phones driving a 21:9 display, who otherwise had to use
// Stretch and accept the distortion.
R21_9,
// See AspectRatioType::R20_9 -- appended for the same persisted-index reason.
R20_9,
R19_5_9,
Custom,
MaxCount
};
// Display rotation applied at present time. Useful for handhelds whose panel
// is mounted in one orientation but the user wants the game in another.
// Rotation is applied to the *final* swapchain blit only; internal GS
// coordinates and aspect-ratio math run in the unrotated frame.
enum class DisplayRotation : u8
{
Rot0,
Rot90,
Rot180,
Rot270,
MaxCount
};
enum class MemoryCardType
{
Empty,
File,
Folder,
MaxCount
};
enum class MemoryCardFileType
{
Unknown,
PS2_8MB,
PS2_16MB,
PS2_32MB,
PS2_64MB,
PS1,
MaxCount
};
enum class LimiterModeType : u8
{
Nominal,
Turbo,
Slomo,
Unlimited,
};
enum class GSRendererType : s8
{
Auto = -1,
DX11 = 3,
Null = 11,
OGL = 12,
SW = 13,
VK = 14,
Metal = 17,
DX12 = 15,
};
enum class GSVSyncMode : u8
{
Disabled,
FIFO,
Mailbox,
Count
};
enum class GSInterlaceMode : u8
{
Automatic,
Off,
WeaveTFF,
WeaveBFF,
BobTFF,
BobBFF,
BlendTFF,
BlendBFF,
AdaptiveTFF,
AdaptiveBFF,
Count
};
enum class GSPostBilinearMode : u8
{
Off,
BilinearSmooth,
BilinearSharp,
};
// Ordering was done to keep compatibility with older ini file.
enum class BiFiltering : u8
{
Nearest,
Forced,
PS2,
Forced_But_Sprite,
};
enum class TriFiltering : s8
{
Automatic = -1,
Off,
PS2,
Forced,
};
enum class AccBlendLevel : u8
{
Minimum,
Basic,
Medium,
High,
Full,
Maximum,
MaxCount
};
enum class OsdOverlayPos : u8
{
None,
TopLeft,
TopCenter,
TopRight,
CenterLeft,
Center,
CenterRight,
BottomLeft,
BottomCenter,
BottomRight,
};
enum class TexturePreloadingLevel : u8
{
Off,
Partial,
Full,
};
enum class GSScreenshotSize : u8
{
WindowResolution,
InternalResolution,
InternalResolutionUncorrected,
};
enum class GSScreenshotFormat : u8
{
PNG,
JPEG,
WebP,
Count,
};
enum class GSDumpCompressionMethod : u8
{
Uncompressed,
LZMA,
Zstandard,
};
enum class SavestateCompressionMethod : u8
{
Uncompressed = 0,
Deflate = 1,
Zstandard = 2
};
enum class SavestateCompressionLevel : u8
{
Low = 0,
Medium = 1,
High = 2,
VeryHigh = 3,
};
enum class GSHardwareDownloadMode : u8
{
Enabled,
EnabledForceFull,
NoReadbacks,
Unsynchronized,
Disabled,
// Appended for wire compatibility (HWDownloadMode is stored as a raw int in the ini and
// in GameDB). NOTE: because of that, this enum is NO LONGER ordered by "how much readback
// happens" — never use relational comparisons on it. Use the predicates below instead.
Asynchronous
};
/// True when the mode performs a real GPU->CPU readback of render targets into local memory.
/// (Asynchronous does the same download, just without making the EE thread wait for it.)
constexpr bool IsHardwareDownloadReadbackEnabled(GSHardwareDownloadMode mode)
{
return mode == GSHardwareDownloadMode::Enabled ||
mode == GSHardwareDownloadMode::EnabledForceFull ||
mode == GSHardwareDownloadMode::Asynchronous;
}
/// True when the EE thread services the readback itself instead of waiting on the GS thread.
/// Both modes therefore need the GS thread synchronized around anything that reopens the
/// renderer underneath them. NOTE: this says nothing about *what* gets read — Unsynchronized
/// takes live local memory, Asynchronous takes the mutex-guarded CPU shadow — so it is not the
/// right question to ask about the pipelined front-object split, which cares only about live
/// reads. GS.cpp tests that directly.
constexpr bool IsHardwareDownloadEEThreadRead(GSHardwareDownloadMode mode)
{
return mode == GSHardwareDownloadMode::Unsynchronized ||
mode == GSHardwareDownloadMode::Asynchronous;
}
enum class GSCASMode : u8
{
Disabled,
SharpenOnly,
SharpenAndResize,
};
enum class GSUpscaler : u8
{
Off, ///< Plain bilinear present-time stretch (default).
MetalFXSpatial, ///< Apple MetalFX spatial upscaler (Metal backend, macOS 13+).
// Appended rather than inserted: this enum is persisted as an integer, so renumbering
// MetalFXSpatial would silently re-point every existing config at a different upscaler.
FSR1, ///< AMD FidelityFX Super Resolution 1 (EASU + RCAS compute passes, Vulkan).
SGSR, ///< Qualcomm Snapdragon Game Super Resolution 1 (single compute pass, Vulkan).
};
enum class GSHWAutoFlushLevel : u8
{
Disabled,
SpritesOnly,
Enabled,
};
enum class GSGPUTargetCLUTMode : u8
{
Disabled,
Enabled,
InsideTarget,
};
enum class GSTextureInRtMode : u8
{
Disabled,
InsideTargets,
MergeTargets,
};
enum class GSLimit24BitDepth : u8
{
Disabled,
PrioritizeUpper,
PrioritizeLower,
};
enum class GSBilinearDirtyMode : u8
{
Automatic,
ForceBilinear,
ForceNearest,
MaxCount
};
enum class GSHalfPixelOffset : u8
{
Off,
Normal,
Special,
SpecialAggressive,
Native,
NativeWTexOffset,
MaxCount
};
enum class GSNativeScaling : u8
{
Off,
Normal,
Aggressive,
NormalUpscaled,
AggressiveUpscaled,
MaxCount
};
// A hack the player set on purpose. Normally the GameDB gets the last word on these
// unless manual hacks are on, which is all or nothing: switching one hack off throws
// away every automatic fix the game had. Pinning one keeps the rest.
enum class GSUserHackOverride : u8
{
AlignSprite,
MergeSprite,
RoundSprite,
HalfPixelOffset,
ForceEvenSpritePosition,
NativeScaling,
NativePaletteDraw,
BilinearHack,
TextureOffsetX,
AutoFlush,
TextureInsideRt,
// Appended rather than slotted in next to X, so a mask already written to an INI keeps
// meaning what it meant. Everything below follows the same rule: append only.
TextureOffsetY,
PreloadFrameData,
DisablePartialInvalidation,
GPUPaletteConversion,
DisableDepthSupport,
CPUFBConversion,
ReadTCOnClose,
Limit24BitDepth,
EstimateTextureRegion,
DrawBuffering,
CPUSpriteRenderBW,
CPUSpriteRenderLevel,
CPUCLUTRender,
GPUTargetCLUT,
MaxCount
};
enum class GSDepthFeedbackMode : u8
{
None = 0,
Auto = 1,
Depth = 2,
DepthAsRT = 3,
};
// GV-7 GS front/back split. Off = today's single-threaded path with no record
// round-trip; InlineRecords = build + execute every record on the calling
// thread (the GV7-0 shape — validation / bisect rung); Lockstep = back thread
// runs but the front drains after every record; Pipelined = the real thing.
enum class GSBackThreadMode : u8
{
Off = 0,
InlineRecords = 1,
Lockstep = 2,
Pipelined = 3,
};
enum class AchievementOverlayPosition : u8
{
TopLeft,
TopCenter,
TopRight,
CenterLeft,
Center,
CenterRight,
BottomLeft,
BottomCenter,
BottomRight,
MaxCount
};
// --------------------------------------------------------------------------------------
// TraceLogsEE
// --------------------------------------------------------------------------------------
struct TraceLogsEE
{
// EE
BITFIELD32()
bool
bios : 1,
memory : 1,
giftag : 1,
vifcode : 1,
mskpath3 : 1,
r5900 : 1,
cop0 : 1,
cop1 : 1,
cop2 : 1,
cache : 1,
knownhw : 1,
unknownhw : 1,
dmahw : 1,
ipu : 1,
dmac : 1,
counters : 1,
spr : 1,
vif : 1,
gif : 1;
BITFIELD_END
TraceLogsEE();
bool operator==(const TraceLogsEE& right) const;
bool operator!=(const TraceLogsEE& right) const;
};
// --------------------------------------------------------------------------------------
// TraceLogsIOP
// --------------------------------------------------------------------------------------
struct TraceLogsIOP
{
BITFIELD32()
bool
bios : 1,
memcards : 1,
pad : 1,
r3000a : 1,
cop2 : 1,
memory : 1,
knownhw : 1,
unknownhw : 1,
dmahw : 1,
dmac : 1,
counters : 1,
cdvd : 1,
mdec : 1;
BITFIELD_END
TraceLogsIOP();
bool operator==(const TraceLogsIOP& right) const;
bool operator!=(const TraceLogsIOP& right) const;
};
// --------------------------------------------------------------------------------------
// TraceLogsMISC
// --------------------------------------------------------------------------------------
struct TraceLogsMISC
{
BITFIELD32()
bool
sif : 1;
BITFIELD_END
TraceLogsMISC();
bool operator==(const TraceLogsMISC& right) const;
bool operator!=(const TraceLogsMISC& right) const;
};
// --------------------------------------------------------------------------------------
// TraceLogFilters
// --------------------------------------------------------------------------------------
struct TraceLogFilters
{
bool Enabled;
TraceLogsEE EE;
TraceLogsIOP IOP;
TraceLogsMISC MISC;
TraceLogFilters();
void LoadSave(SettingsWrapper& ini);
// When logging, the tracelogpack is checked, not was in the config.
// Call this to sync the tracelogpack values with the config values.
void SyncToConfig() const;
bool operator==(const TraceLogFilters& right) const;
bool operator!=(const TraceLogFilters& right) const;
};
// --------------------------------------------------------------------------------------
// Pcsx2Config class
// --------------------------------------------------------------------------------------
// This is intended to be a public class library between the core emulator and GUI only.
//
// When GUI code performs modifications of this class, it must be done with strict thread
// safety, since the emu runs on a separate thread. Additionally many components of the
// class require special emu-side resets or state save/recovery to be applied. Please
// use the provided functions to lock the emulation into a safe state and then apply
// chances on the necessary scope (see Core_Pause, Core_ApplySettings, and Core_Resume).
//
struct Pcsx2Config
{
struct ProfilerOptions
{
BITFIELD32()
bool
Enabled : 1, // universal toggle for the profiler.
RecBlocks_EE : 1, // Enables per-block profiling for the EE recompiler [unimplemented]
RecBlocks_IOP : 1, // Enables per-block profiling for the IOP recompiler [unimplemented]
RecBlocks_VU0 : 1, // Enables per-block profiling for the VU0 recompiler [unimplemented]
RecBlocks_VU1 : 1, // Enables per-block profiling for the VU1 recompiler [unimplemented]
EnablePerfDump : 1; // Linux: write JIT blocks to perf jitdump (USE_PERF_JITDUMP build only).
BITFIELD_END
// Default is Disabled, with all recs enabled underneath.
ProfilerOptions();
void LoadSave(SettingsWrapper& wrap);
bool operator==(const ProfilerOptions& right) const;
bool operator!=(const ProfilerOptions& right) const;
};
// ------------------------------------------------------------------------
struct RecompilerOptions
{
BITFIELD32()
bool
EnableEE : 1,
EnableIOP : 1,
EnableVU0 : 1,
EnableVU1 : 1;
bool
vu0Overflow : 1,
vu0ExtraOverflow : 1,
vu0SignOverflow : 1,
vu0Underflow : 1;
bool
vu1Overflow : 1,
vu1ExtraOverflow : 1,
vu1SignOverflow : 1,
vu1Underflow : 1;
bool
fpuOverflow : 1,
fpuExtraOverflow : 1,
fpuFullMode : 1,
fpuExactMode : 1,
fpuGuardedAddSub : 1; // EE FPU add/sub guard-bit emulation (single-precision fast path). ON by default — the PS2-accurate behavior. Opt-OUT globally via INI for EE-FPU-heavy titles verified to render fine without it (each ADD.S/SUB.S then costs one op instead of the guard sequence). Independent of the clamp tiers: Full mode runs the DOUBLE path, which guards unconditionally regardless of this bit.
bool
EnableEECache : 1;
bool
EnableFastmem : 1;
bool
PauseOnTLBMiss : 1;
// Cache compiled VU micro-programs to disk and reload them across
// sessions to cut recompilation stutter on later runs. arm64-only;
// no-op on x86.
bool
EnableVUProgramCache : 1;
BITFIELD_END
RecompilerOptions();
void ApplySanityCheck();
void LoadSave(SettingsWrapper& wrap);
bool operator==(const RecompilerOptions& right) const;
bool operator!=(const RecompilerOptions& right) const;
u32 GetEEClampMode() const;
void SetEEClampMode(u32 value);
u32 GetVUClampMode() const;
};
// ------------------------------------------------------------------------
struct CpuOptions
{
BITFIELD32()
bool
ExtraMemory : 1;
BITFIELD_END
RecompilerOptions Recompiler;
FPControlRegister FPUFPCR;
FPControlRegister FPUDivFPCR;
FPControlRegister VU0FPCR;
FPControlRegister VU1FPCR;
CpuOptions();
void LoadSave(SettingsWrapper& wrap);
void ApplySanityCheck();
bool CpusChanged(const CpuOptions& right) const;
bool operator==(const CpuOptions& right) const;
bool operator!=(const CpuOptions& right) const;
};
// ------------------------------------------------------------------------
struct GSOptions
{
static const char* AspectRatioNames[];
static const char* FMVAspectRatioSwitchNames[];
static const char* DisplayRotationNames[];
static const char* BlendingLevelNames[];
static const char* CaptureContainers[];
static const char* GetRendererName(GSRendererType type);
/// Converts a tri-state option to an optional boolean value.
static std::optional<bool> TriStateToOptionalBoolean(int value);
/// Constants for determining default values.
static constexpr float DEFAULT_FRAME_RATE_NTSC = 59.94f;
static constexpr float DEFAULT_FRAME_RATE_PAL = 50.00f;
static constexpr GSRendererType DEFAULT_HW_RENDERER = GSRendererType::Auto;
static constexpr AspectRatioType DEFAULT_ASPECT_RATIO = AspectRatioType::RAuto4_3_3_2;
static constexpr GSInterlaceMode DEFAULT_INTERLACE_MODE = GSInterlaceMode::Automatic;
static constexpr GSPostBilinearMode DEFAULT_BILINEAR_FILTERING_MODE = GSPostBilinearMode::BilinearSmooth;
static constexpr FMVAspectRatioSwitchType DEFAULT_FMV_ASPECT_RATIO = FMVAspectRatioSwitchType::Off;
static constexpr GSCASMode DEFAULT_CAS_MODE = GSCASMode::Disabled;
static constexpr GSUpscaler DEFAULT_UPSCALER = GSUpscaler::Off;
static constexpr float DEFAULT_UPSCALE_MULTIPLIER = 1.0f;
static constexpr AccBlendLevel DEFAULT_BLENDING_ACCURACY = AccBlendLevel::Basic;
static constexpr BiFiltering DEFAULT_TEXTURE_FILTERING_MODE = BiFiltering::PS2;
static constexpr TriFiltering DEFAULT_TRILINEAR_FILTERING_MODE = TriFiltering::Automatic;
static constexpr float DEFAULT_OSD_SCALE = 100.0f;
static constexpr float DEFAULT_OSD_MARGIN = 10.0f;
static constexpr OsdOverlayPos DEFAULT_OSD_MESSAGE_POS = OsdOverlayPos::TopLeft;
static constexpr OsdOverlayPos DEFAULT_OSD_PERFORMANCE_POS = OsdOverlayPos::TopRight;
static constexpr int DEFAULT_VIDEO_CAPTURE_BITRATE = 6000;
static constexpr int DEFAULT_VIDEO_CAPTURE_WIDTH = 640;
static constexpr int DEFAULT_VIDEO_CAPTURE_HEIGHT = 480;
static constexpr int DEFAULT_AUDIO_CAPTURE_BITRATE = 192;
static const char* DEFAULT_CAPTURE_CONTAINER;
static constexpr int DEFAULT_SHADEBOOST_BRIGHTNESS = 50;
static constexpr int DEFAULT_SHADEBOOST_CONTRAST = 50;
static constexpr int DEFAULT_SHADEBOOST_GAMMA = 50;
static constexpr int DEFAULT_SHADEBOOST_SATURATION = 50;
union
{
u64 bitsets[2];
struct
{
bool
SynchronousMTGS : 1,
VsyncEnable : 1,
DisableMailboxPresentation : 1,
ExtendedUpscalingMultipliers : 1,
PCRTCAntiBlur : 1,
DisableInterlaceOffset : 1,
PCRTCOffsets : 1,
PCRTCOverscan : 1,
IntegerScaling : 1,
UseDebugDevice : 1,
UseDebugBlend : 1,
// Emit per-draw graphics-debugger labels describing the PS2 state.
// Deliberately separate from UseDebugDevice, which also installs the
// validation layer and so makes any capture perf-meaningless.
DebugLabels : 1,
// Record the per-draw ledger (GSDrawLog). Attribution only -- it is
// not free, so never leave it on for one arm of an A/B.
DumpDrawLog : 1,
UseBlitSwapChain : 1,
DisableShaderCache : 1,
DisableFramebufferFetch : 1,
EnableAdrenoFramebufferFetch : 1,
ForceMaliFramebufferFetch : 1,
DisablePS2DepthQuantization : 1,
DisableVertexShaderExpand : 1,
SkipDuplicateFrames : 1,
OsdShowSpeed : 1,
OsdShowFPS : 1,
OsdShowVPS : 1,
OsdShowResolution : 1,
OsdShowGSStats : 1,
OsdShowCPU : 1,
OsdShowGPU : 1,
OsdShowGPUDebug : 1,
OsdShowGPUStats : 1,
OsdShowIndicators : 1,
OsdShowFrameTimes : 1,
OsdShowHardwareInfo : 1,
OsdShowVersion : 1,
OsdShowSettings : 1,
OsdshowPatches : 1,
OsdShowInputs : 1,
OsdShowVideoCapture : 1,
OsdShowInputRec : 1,
OsdShowTextureReplacements : 1,
OsdBoldText : 1,
HWSpinGPUForReadbacks : 1,
HWSpinCPUForReadbacks : 1,
GPUPaletteConversion : 1,
AutoFlushSW : 1,
PreloadFrameWithGSData : 1,
Mipmap : 1,
HWMipmap : 1,
HWAccurateAlphaTest : 1,
HWAA1 : 1,
HWROV : 1,
HWROVLogging : 1,
HWROVBarriersVK : 1,
// Hold hardware draws back so consecutive draws to the same target
// share one render pass (GSPassScheduler). Aimed at tiling GPUs,
// where every pass boundary is a full tile load and store. Hot-
// appliable: turning it off just stops deferring.
CoalesceRenderPasses : 1,
ManualUserHacks : 1,
UserHacks_AlignSpriteX : 1,
UserHacks_CPUFBConversion : 1,
UserHacks_ReadTCOnClose : 1,
UserHacks_DisableDepthSupport : 1,
UserHacks_DisablePartialInvalidation : 1,
UserHacks_DisableSafeFeatures : 1,
UserHacks_DisableRenderFixes : 1,
UserHacks_MergePPSprite : 1,
UserHacks_ForceEvenSpritePosition : 1,
UserHacks_NativePaletteDraw : 1,
UserHacks_EstimateTextureRegion : 1,
UserHacks_DrawBuffering : 1,
FXAA : 1,
ShadeBoost : 1,
DumpGSData : 1,
SaveRT : 1,
SaveFrame : 1,
SaveTexture : 1,
SaveDepth : 1,
SaveAlpha : 1,
SaveInfo : 1,
SaveTransferImages : 1,
SaveDrawStats : 1,
SaveFrameStats : 1,
SaveHWConfig : 1,
DumpReplaceableTextures : 1,
DumpReplaceableMipmaps : 1,
DumpTexturesWithFMVActive : 1,
DumpDirectTextures : 1,
DumpPaletteTextures : 1,
LoadTextureReplacements : 1,
LoadTextureReplacementsAsync : 1,
PrecacheTextureReplacements : 1,
EnableVideoCapture : 1,
EnableVideoCaptureParameters : 1,
VideoCaptureAutoResolution : 1,
EnableAudioCapture : 1,
EnableAudioCaptureParameters : 1,
OrganizeSnapshotsByGame : 1,
OrganizeVideoCaptureByGame : 1;
};
};
int VsyncQueueSize = 2;
float FramerateNTSC = DEFAULT_FRAME_RATE_NTSC;
float FrameratePAL = DEFAULT_FRAME_RATE_PAL;
AspectRatioType AspectRatio = DEFAULT_ASPECT_RATIO;
FMVAspectRatioSwitchType FMVAspectRatioSwitch = DEFAULT_FMV_ASPECT_RATIO;
DisplayRotation Rotation = DisplayRotation::Rot0;
GSInterlaceMode InterlaceMode = DEFAULT_INTERLACE_MODE;
GSPostBilinearMode LinearPresent = DEFAULT_BILINEAR_FILTERING_MODE;
float StretchY = 100.0f;
/// Width/height for AspectRatioType::Custom. Stored as a ratio, not W and H separately, so
/// anything can be expressed (2.1666 for 19.5:9, 1.85 for a film ratio) without a second key.
float CustomAspectRatio = 16.0f / 9.0f;
int Crop[4] = {};
float OsdScale = DEFAULT_OSD_SCALE;
/// OSD text colour as 0xRRGGBB. 0 keeps the classic white, so existing installs and
/// every non-Android frontend are untouched unless the user picks a colour.
u32 OsdColor = 0;
float OsdMargin = DEFAULT_OSD_MARGIN;
std::string OsdFontPath;
OsdOverlayPos OsdMessagesPos = DEFAULT_OSD_MESSAGE_POS;
OsdOverlayPos OsdPerformancePos = DEFAULT_OSD_PERFORMANCE_POS;
GSRendererType Renderer = DEFAULT_HW_RENDERER;
float UpscaleMultiplier = DEFAULT_UPSCALE_MULTIPLIER;
AccBlendLevel AccurateBlendingUnit = DEFAULT_BLENDING_ACCURACY;
BiFiltering TextureFiltering = DEFAULT_TEXTURE_FILTERING_MODE;
TexturePreloadingLevel TexturePreloading = TexturePreloadingLevel::Full;
GSDumpCompressionMethod GSDumpCompression = GSDumpCompressionMethod::Zstandard;
GSHardwareDownloadMode HWDownloadMode = GSHardwareDownloadMode::Enabled;
GSCASMode CASMode = DEFAULT_CAS_MODE;
GSUpscaler Upscaler = DEFAULT_UPSCALER;
u8 Dithering = 2;
u8 MaxAnisotropy = 0;
u8 TVShader = 0;
s16 GetSkipCountFunctionId = -1;
s16 BeforeDrawFunctionId = -1;
s16 MoveHandlerFunctionId = -1;
int SkipDrawStart = 0;
int SkipDrawEnd = 0;
GSHWAutoFlushLevel UserHacks_AutoFlush = GSHWAutoFlushLevel::Disabled;
GSHalfPixelOffset UserHacks_HalfPixelOffset = GSHalfPixelOffset::Off;
s8 UserHacks_RoundSprite = 0;
GSNativeScaling UserHacks_NativeScaling = GSNativeScaling::Off;
s32 UserHacks_TCOffsetX = 0;
s32 UserHacks_TCOffsetY = 0;
u8 UserHacks_CPUSpriteRenderBW = 0;
u8 UserHacks_CPUSpriteRenderLevel = 0;
u8 UserHacks_CPUCLUTRender = 0;
GSGPUTargetCLUTMode UserHacks_GPUTargetCLUTMode = GSGPUTargetCLUTMode::Disabled;
GSTextureInRtMode UserHacks_TextureInsideRt = GSTextureInRtMode::Disabled;
GSLimit24BitDepth UserHacks_Limit24BitDepth = GSLimit24BitDepth::Disabled;
GSBilinearDirtyMode UserHacks_BilinearHack = GSBilinearDirtyMode::Automatic;
TriFiltering TriFilter = DEFAULT_TRILINEAR_FILTERING_MODE;
s8 OverrideTextureBarriers = -1;
GSDepthFeedbackMode DepthFeedbackMode = GSDepthFeedbackMode::Auto;
GSBackThreadMode BackThreadMode = GSBackThreadMode::Off;
// RetroArch (.slangp) shader chain, applied at present after ShadeBoost/FXAA via
// librashader. Disabled or an empty preset skips the chain entirely (zero cost),
// and it's a no-op on renderers/builds without a librashader backend.
bool ShaderChainEnabled = false;
std::string ShaderChainPreset;
// LSFG — Lossless Scaling frame generation, inserted into the Vulkan present path.
// Off unless the user both enables it AND supplies their own Lossless.dll: the
// interpolation shaders are read out of that file at runtime and nothing about them
// ships with ARMSX2. Vulkan + Adreno 7xx and newer only, and compiled out entirely
// in the play flavour, so every one of these is inert in a build without it.
bool LsfgEnabled = false;
u8 LsfgMultiplier = 2; // frames displayed per rendered frame: 2 = one interpolated
std::string LsfgDllPath;
// LSFG 3.1p, a lighter shader family than 3.1. Default on: this runs on a phone GPU
// that is already presenting the game, and the cheaper pipeline is what makes the
// feature pay for itself there. Falls back to 3.1 when the user's DLL predates 3.1p.
bool LsfgPerformance = true;
// Optical-flow resolution, as a percentage of the presented image (25..100). Lower is
// cheaper and blurrier. Handed to the library as a DIVISOR — see GSLsfg.cpp.
u8 LsfgFlowScale = 100;
// Target OUTPUT rate in Hz for the adaptive pacer; 0 holds LsfgMultiplier fixed.
//
// A fixed multiplier is the wrong shape for a game that oscillates between 60 and 30fps
// on a 60Hz panel: at x2 it presents 120 then 60, and every transition is visible as
// judder. Given a target, the pacer varies the generation count instead — two
// interpolated frames while the game runs at 30, one while it runs at 60 — so the
// presented rate stays put while the rendered rate moves underneath it.
//
// u16 because 0..1000 covers every panel; 0 is the default so behaviour is unchanged
// until the user opts in.
u16 LsfgTargetRate = 0;
u8 CAS_Sharpness = 50;
// FSR1's RCAS pass, 0..100. Mapped to AMD's "stops" scale in GSDevice::FSR1Upscale,
// where 0 stops is maximum sharpening - it is not the same curve as CAS_Sharpness.
u8 FSR_Sharpness = 50;
u8 ShadeBoost_Brightness = DEFAULT_SHADEBOOST_BRIGHTNESS;
u8 ShadeBoost_Contrast = DEFAULT_SHADEBOOST_CONTRAST;
u8 ShadeBoost_Saturation = DEFAULT_SHADEBOOST_SATURATION;
u8 ShadeBoost_Gamma = DEFAULT_SHADEBOOST_GAMMA;
u8 PNGCompressionLevel = 1;
u16 SWExtraThreads = 2;
u16 SWExtraThreadsHeight = 4;
int SaveDrawStart = 0;
int SaveDrawCount = 5000;
int SaveDrawBy = 1;
int SaveFrameStart = 0;
int SaveFrameCount = -1;
int SaveFrameBy = 1;
s8 ExclusiveFullscreenControl = -1;
GSScreenshotSize ScreenshotSize = GSScreenshotSize::WindowResolution;
GSScreenshotFormat ScreenshotFormat = GSScreenshotFormat::PNG;
int ScreenshotQuality = 90;
std::string CaptureContainer = DEFAULT_CAPTURE_CONTAINER;
std::string VideoCaptureCodec;
std::string VideoCaptureFormat;
std::string VideoCaptureParameters;
std::string AudioCaptureCodec;
std::string AudioCaptureParameters;
int VideoCaptureBitrate = DEFAULT_VIDEO_CAPTURE_BITRATE;
int VideoCaptureWidth = DEFAULT_VIDEO_CAPTURE_WIDTH;
int VideoCaptureHeight = DEFAULT_VIDEO_CAPTURE_HEIGHT;
int AudioCaptureBitrate = DEFAULT_AUDIO_CAPTURE_BITRATE;
std::string Adapter;
std::string AndroidGpuProfileOverride = "auto";
std::string HWDumpDirectory;
std::string SWDumpDirectory;
/// Hacks the player set deliberately, one bit per GSUserHackOverride. Kept out of
/// the bitfield union above on purpose: that packing is what OptionsAreEqual
/// compares wholesale, and this is not a hack value, it is who owns one.
u32 UserHackOverrides = 0;
GSOptions();
void LoadSave(SettingsWrapper& wrap);
bool IsUserHackPinned(GSUserHackOverride hack) const
{
return (UserHackOverrides & (1u << static_cast<u32>(hack))) != 0;
}
void SetUserHackPinned(GSUserHackOverride hack, bool pinned)
{
const u32 bit = 1u << static_cast<u32>(hack);
UserHackOverrides = pinned ? (UserHackOverrides | bit) : (UserHackOverrides & ~bit);
}
/// Sets user hack values to defaults when user hacks are not enabled. Hacks the
/// player claimed survive, unless the caller is stripping for safety rather than
/// preference, in which case pass false.
void MaskUserHacks(bool respect_claims = true);
/// Sets user hack values to defaults when upscaling is not enabled.
void MaskUpscalingHacks();
/// Returns true if any of the hardware renderers are selected.
bool UseHardwareRenderer() const;
/// Returns false if the compared to the old settings, we need to reopen GS.
/// (i.e. renderer change, swap chain mode change, etc.)
bool RestartOptionsAreEqual(const GSOptions& right) const;
/// Whether changing this INI key forces a GS device teardown, i.e. whether it is
/// one of the fields RestartOptionsAreEqual compares. Lets a caller mutating a
/// setting by name report the cost without diffing two whole configs.
static bool IsRestartOption(const char* ini_key);
/// Returns false if any options need to be applied to the MTGS.
bool OptionsAreEqual(const GSOptions& right) const;
bool operator==(const GSOptions& right) const;
bool operator!=(const GSOptions& right) const;
// Should we dump this draw/frame?
bool ShouldDump(u64 draw, int frame) const;
};
struct SPU2Options
{
enum class SPU2SyncMode : u8
{
Disabled,
TimeStretch,
Count
};
static constexpr s32 MAX_VOLUME = 200;
#ifdef __ANDROID__
static constexpr AudioBackend DEFAULT_BACKEND = AudioBackend::Oboe;
#else
static constexpr AudioBackend DEFAULT_BACKEND = AudioBackend::Cubeb;
#endif
static constexpr SPU2SyncMode DEFAULT_SYNC_MODE = SPU2SyncMode::TimeStretch;
static std::optional<SPU2SyncMode> ParseSyncMode(const char* str);
static const char* GetSyncModeName(SPU2SyncMode backend);
static const char* GetSyncModeDisplayName(SPU2SyncMode backend);
BITFIELD32()
bool
DebugEnabled : 1,
MsgToConsole : 1,
MsgKeyOnOff : 1,
MsgVoiceOff : 1,
MsgDMA : 1,
MsgAutoDMA : 1,
MsgCache : 1,
AccessLog : 1,
DMALog : 1,
WaveLog : 1,
CoresDump : 1,
MemDump : 1,
RegDump : 1,
VisualDebugEnabled : 1;
BITFIELD_END
u32 StandardVolume = 100;
u32 FastForwardVolume = 100;
bool OutputMuted = false;
// Low-end Android lever: skip the SPU2 reverb pipeline in MixCore. Off by default.
bool LightweightMode = false;
AudioBackend Backend = DEFAULT_BACKEND;
SPU2SyncMode SyncMode = DEFAULT_SYNC_MODE;
AudioStreamParameters StreamParameters;
std::string DriverName;
std::string DeviceName;
SPU2Options();
void LoadSave(SettingsWrapper& wrap);
bool IsTimeStretchEnabled() const { return (SyncMode == SPU2SyncMode::TimeStretch); }
bool operator==(const SPU2Options& right) const;
bool operator!=(const SPU2Options& right) const;
};
struct DEV9Options
{
enum struct NetApi : int
{
Unset = 0,
PCAP_Bridged = 1,
PCAP_Switched = 2,
TAP = 3,
Sockets = 4,
LocalLink = 5,
};
static const char* NetApiNames[];
enum struct DnsMode : int
{
Manual = 0,
Auto = 1,
Internal = 2,
};
static const char* DnsModeNames[];
struct HostEntry
{
std::string Url;
std::string Desc;
u8 Address[4]{};
bool Enabled;
bool operator==(const HostEntry& right) const;
bool operator!=(const HostEntry& right) const;
};
bool EthEnable{false};
NetApi EthApi{NetApi::Unset};
std::string EthDevice;
bool EthLogDHCP{false};
bool EthLogDNS{false};
bool LocalLinkHost{false};
std::string LocalLinkAddress;
u32 LocalLinkPort{19072};
u32 LocalLinkPeerId{1};
std::string LocalLinkRoomCode;
bool InterceptDHCP{false};
u8 PS2IP[4]{};
u8 Mask[4]{};
u8 Gateway[4]{};
u8 DNS1[4]{};
u8 DNS2[4]{};
bool AutoMask{true};
bool AutoGateway{true};
DnsMode ModeDNS1{DnsMode::Auto};
DnsMode ModeDNS2{DnsMode::Auto};
std::vector<HostEntry> EthHosts;
bool HddEnable{false};
std::string HddFile;
DEV9Options();
void LoadSave(SettingsWrapper& wrap);
bool operator==(const DEV9Options& right) const;
bool operator!=(const DEV9Options& right) const;
protected:
static void LoadIPHelper(u8* field, const std::string& setting);
static std::string SaveIPHelper(u8* field);
};
// ------------------------------------------------------------------------
// NOTE: The GUI's GameFixes panel is dependent on the order of bits in this structure.
struct GamefixOptions
{
BITFIELD32()
bool
// No reader: eeMulRound (FPU.cpp) and emitDefectiveFmul
// (iFPUd-arm64.cpp) model the multiplier defect this patched one
// product of. The bit stays because its GamefixId indexes
// vu_capture's on-disk gamefix mask.
FpuMulHack : 1, // Tales of Destiny hangs.
GoemonTlbHack : 1, // Gomeon tlb miss hack. The game need to access unmapped virtual address. Instead to handle it as exception, tlb are preloaded at startup
SoftwareRendererFMVHack : 1, // Switches to software renderer for FMVs
SkipMPEGHack : 1, // Skips MPEG videos (Katamari and other games need this)
OPHFlagHack : 1, // Bleach Blade Battlers
EETimingHack : 1, // General purpose timing hack.
InstantDMAHack : 1, // Instantly complete DMA's if possible, good for cache emulation problems.
DMABusyHack : 1, // Denies writes to the DMAC when it's busy. This is correct behaviour but bad timing can cause problems.
GIFFIFOHack : 1, // Enabled the GIF FIFO (more correct but slower)
VIFFIFOHack : 1, // Pretends to fill the non-existant VIF FIFO Buffer.
VIF1StallHack : 1, // Like above, processes FIFO data before the stall is allowed (to make sure data goes over).
VuAddSubHack : 1, // Tri-ace games, they use an encryption algorithm that requires VU ADDI opcode to be bit-accurate.
IbitHack : 1, // I bit hack. Needed to stop constant VU recompilation in some games
VUSyncHack : 1, // Makes microVU run behind the EE to avoid VU register reading/writing sync issues. Useful for M-Bit games
VUOverflowHack : 1, // Tries to simulate overflow flag checks (not really possible on x86 without soft floats)
XgKickHack : 1, // Erementar Gerad, adds more delay to VU XGkick instructions. Corrects the color of some graphics, but breaks Tri-ace games and others.
BlitInternalFPSHack : 1, // Disables privileged register write-based FPS detection.
FullVU0SyncHack : 1; // Forces tight VU0 sync on every COP2 instruction.
BITFIELD_END
GamefixOptions();
void LoadSave(SettingsWrapper& wrap);
GamefixOptions& DisableAll();
static const char* GetGameFixName(GamefixId id);
bool Get(GamefixId id) const;
void Set(GamefixId id, bool enabled = true);
void Clear(GamefixId id) { Set(id, false); }
bool operator==(const GamefixOptions& right) const;
bool operator!=(const GamefixOptions& right) const;
};
// ------------------------------------------------------------------------
struct SpeedhackOptions
{
static constexpr s8 MIN_EE_CYCLE_RATE = -3;
static constexpr s8 MAX_EE_CYCLE_RATE = 3;
static constexpr u8 MAX_EE_CYCLE_SKIP = 3;
BITFIELD32()
bool
fastCDVD : 1, // enables fast CDVD access
IntcStat : 1, // tells Pcsx2 to fast-forward through intc_stat waits.
WaitLoop : 1, // enables constant loop detection and fast-forwarding
vuFlagHack : 1, // microVU specific flag hack
vuThread : 1, // Enable Threaded VU1
vu1Instant : 1; // Enable Instant VU1 (Without MTVU only)
BITFIELD_END
s8 EECycleRate; // EE cycle rate selector (1.0, 1.5, 2.0)
u8 EECycleSkip; // EE Cycle skip factor (0, 1, 2, or 3)
SpeedhackOptions();
void LoadSave(SettingsWrapper& conf);
SpeedhackOptions& DisableAll();
void Set(SpeedHack id, int value);
bool operator==(const SpeedhackOptions& right) const;
bool operator!=(const SpeedhackOptions& right) const;
static const char* GetSpeedHackName(SpeedHack id);
static std::optional<SpeedHack> ParseSpeedHackName(const std::string_view name);
};
// ------------------------------------------------------------------------
struct DebugAnalysisOptions
{
static const char* RunConditionNames[];
static const char* FunctionScanModeNames[];
DebugAnalysisCondition RunCondition = DebugAnalysisCondition::IF_DEBUGGER_IS_OPEN;
bool GenerateSymbolsForIRXExports = true;
bool AutomaticallySelectSymbolsToClear = true;
std::vector<DebugSymbolSource> SymbolSources;
bool ImportSymbolsFromELF = true;
bool ImportSymFileFromDefaultLocation = true;
bool DemangleSymbols = true;
bool DemangleParameters = true;
std::vector<DebugExtraSymbolFile> ExtraSymbolFiles;
DebugFunctionScanMode FunctionScanMode = DebugFunctionScanMode::SCAN_ELF;
bool CustomFunctionScanRange = false;
std::string FunctionScanStartAddress;
std::string FunctionScanEndAddress;
bool GenerateFunctionHashes = true;
void LoadSave(SettingsWrapper& wrap);
friend auto operator<=>(const DebugAnalysisOptions& lhs, const DebugAnalysisOptions& rhs) = default;
};
// ------------------------------------------------------------------------
struct EmulationSpeedOptions
{
BITFIELD32()
bool SyncToHostRefreshRate : 1;
bool UseVSyncForTiming : 1;
BITFIELD_END
float NominalScalar{1.0f};
float TurboScalar{2.0f};
float SlomoScalar{0.5f};
EmulationSpeedOptions();
void LoadSave(SettingsWrapper& wrap);
void SanityCheck();
bool operator==(const EmulationSpeedOptions& right) const;
bool operator!=(const EmulationSpeedOptions& right) const;
};
// ------------------------------------------------------------------------
struct FilenameOptions
{
std::string Bios;
FilenameOptions();
void LoadSave(SettingsWrapper& wrap);
bool operator==(const FilenameOptions& right) const;
bool operator!=(const FilenameOptions& right) const;
};
// ------------------------------------------------------------------------
struct USBOptions
{
static constexpr u32 NUM_PORTS = 2;
struct Port
{
s32 DeviceType;
u32 DeviceSubtype;
bool operator==(const USBOptions::Port& right) const;
bool operator!=(const USBOptions::Port& right) const;
};
std::array<Port, NUM_PORTS> Ports;
USBOptions();
void LoadSave(SettingsWrapper& wrap);
bool operator==(const USBOptions& right) const;
bool operator!=(const USBOptions& right) const;
};
// ------------------------------------------------------------------------
struct PadOptions
{
static constexpr u32 NUM_PORTS = 8;
struct Port
{
Pad::ControllerType Type;
bool operator==(const PadOptions::Port& right) const;
bool operator!=(const PadOptions::Port& right) const;
};
std::array<Port, NUM_PORTS> Ports;
BITFIELD32()
bool
MultitapPort0_Enabled : 1,
MultitapPort1_Enabled;
BITFIELD_END
PadOptions();
void LoadSave(SettingsWrapper& wrap);
bool IsMultitapPortEnabled(u32 port) const
{
return (port == 0) ? MultitapPort0_Enabled : MultitapPort1_Enabled;
}
bool operator==(const PadOptions& right) const;
bool operator!=(const PadOptions& right) const;
};
// ------------------------------------------------------------------------
// Options struct for each memory card.
//
struct McdOptions
{
std::string Filename; // user-configured location of this memory card
bool Enabled; // memory card enabled (if false, memcard will not show up in-game)
MemoryCardType Type; // the memory card implementation that should be used
};
// ------------------------------------------------------------------------
struct AchievementsOptions
{
static constexpr u32 MINIMUM_NOTIFICATION_DURATION = 3;
static constexpr u32 MAXIMUM_NOTIFICATION_DURATION = 30;
static constexpr u32 DEFAULT_NOTIFICATION_DURATION = 5;
static constexpr u32 DEFAULT_LEADERBOARD_DURATION = 10;
static const char* OverlayPositionNames[(size_t)AchievementOverlayPosition::MaxCount + 1];
BITFIELD32()
bool
Enabled : 1,
HardcoreMode : 1,
EncoreMode : 1,
SpectatorMode : 1,
UnofficialTestMode : 1,
Notifications : 1,
LeaderboardNotifications : 1,
SoundEffects : 1,
InfoSound : 1,
UnlockSound : 1,
LBSubmitSound : 1,
Overlays : 1,
LBOverlays : 1;
BITFIELD_END
u32 NotificationsDuration = DEFAULT_NOTIFICATION_DURATION;
u32 LeaderboardsDuration = DEFAULT_LEADERBOARD_DURATION;
AchievementOverlayPosition OverlayPosition = AchievementOverlayPosition::BottomRight;
OsdOverlayPos NotificationPosition = OsdOverlayPos::TopLeft;
std::string InfoSoundName;
std::string UnlockSoundName;
std::string LBSubmitSoundName;
AchievementsOptions();
void LoadSave(SettingsWrapper& wrap);
bool operator==(const AchievementsOptions& right) const;
bool operator!=(const AchievementsOptions& right) const;
};
struct SavestateOptions
{
SavestateOptions();
void LoadSave(SettingsWrapper& wrap);
SavestateCompressionMethod CompressionType = SavestateCompressionMethod::Zstandard;
SavestateCompressionLevel CompressionRatio = SavestateCompressionLevel::Medium;
bool operator==(const SavestateOptions& right) const;
bool operator!=(const SavestateOptions& right) const;
};
// ------------------------------------------------------------------------
BITFIELD32()
bool
CdvdVerboseReads : 1, // enables cdvd read activity verbosely dumped to the console
CdvdDumpBlocks : 1, // enables cdvd block dumping
CdvdPrecache : 1, // enables cdvd precaching of compressed images
EnablePatches : 1, // enables patch detection and application
EnableCheats : 1, // enables cheat detection and application
EnablePINE : 1, // enables inter-process communication
EnableWideScreenPatches : 1,
EnableNoInterlacingPatches : 1,
EnableFastBoot : 1,
EnableFastBootFastForward : 1,
EnableThreadPinning : 1,
// TODO - Vaser - where are these settings exposed in the Qt UI?
EnableRecordingTools : 1,
EnableGameFixes : 1, // enables automatic game fixes
SaveStateOnShutdown : 1, // default value for saving state on shutdown
EnableDiscordPresence : 1, // enables discord rich presence integration
UseSavestateSelector : 1,
InhibitScreensaver : 1,
BackupSavestate : 1,
ManuallySetRealTimeClock : 1, // passes user-set real-time clock information to cdvd at startup
UseSystemLocaleFormat : 1, // presents OS time format instead of yyyy-MM-dd HH:mm:ss for manual RTC
HostFs : 1,
WarnAboutUnsafeSettings : 1;
BITFIELD_END
CpuOptions Cpu;
GSOptions GS;
SpeedhackOptions Speedhacks;
GamefixOptions Gamefixes;
ProfilerOptions Profiler;
DebugAnalysisOptions DebuggerAnalysis;
EmulationSpeedOptions EmulationSpeed;
SavestateOptions Savestate;
SPU2Options SPU2;
DEV9Options DEV9;
USBOptions USB;
PadOptions Pad;
TraceLogFilters Trace;
FilenameOptions BaseFilenames;
AchievementsOptions Achievements;
// Memorycard options - first 2 are default slots, last 6 are multitap 1 and 2
// slots (3 each)
McdOptions Mcd[8];
std::string GzipIsoIndexTemplate; // for quick-access index with gzipped ISO
int PINESlot;
int RtcYear;
int RtcMonth;
int RtcDay;
int RtcHour;
int RtcMinute;
int RtcSecond;
// Set at runtime, not loaded from config.
std::string CurrentBlockdump;
std::string CurrentIRX;
std::string CurrentGameArgs;
std::string CustomDataPath;
AspectRatioType CurrentAspectRatio = AspectRatioType::RAuto4_3_3_2;
// Fall back aspect ratio for games that have patches (when AspectRatioType::RAuto4_3_3_2) is active.
float CurrentCustomAspectRatio = 0.f;
bool IsPortableMode = false;
Pcsx2Config();
void LoadSave(SettingsWrapper& wrap);
void LoadSaveCore(SettingsWrapper& wrap);
void LoadSaveMemcards(SettingsWrapper& wrap);
/// Reloads options affected by patches.
void ReloadPatchAffectingOptions();
std::string FullpathToBios() const;
std::string FullpathToMcd(uint slot) const;
bool operator==(const Pcsx2Config& right) const = delete;
bool operator!=(const Pcsx2Config& right) const = delete;
/// Copies runtime configuration settings (e.g. frame limiter state).
void CopyRuntimeConfig(Pcsx2Config& cfg);
/// Copies configuration from one file to another. Does not copy controller settings.
/// Copies a configuration into a per-game settings file, writing only the values
/// that deviate from what the game would run with anyway — stock defaults, and the
/// game database's own opinion for `game_serial`. Everything else is left absent,
/// because in a per-game file a key that is present is a key the player is taken to
/// have claimed, and the database then stands aside for it.
static void CopyConfiguration(SettingsInterface* dest_si, SettingsInterface& src_si, std::string_view game_serial);
/// Clears all core keys from the specified interface.
static void ClearConfiguration(SettingsInterface* dest_si);
/// Removes keys that are not valid for per-game settings.
static void ClearInvalidPerGameConfiguration(SettingsInterface* si);
};
extern Pcsx2Config EmuConfig;
namespace EmuFolders
{
extern std::string AppRoot;
extern std::string DataRoot;
extern std::string Settings;
extern std::string Bios;
extern std::string Snapshots;
extern std::string Savestates;
extern std::string MemoryCards;
extern std::string Logs;
extern std::string Cheats;
extern std::string Patches;
extern std::string Resources;
extern std::string UserResources;
extern std::string Cache;
extern std::string Covers;
extern std::string GameSettings;
extern std::string Textures;
extern std::string InputProfiles;
extern std::string Videos;
extern std::string DebuggerLayouts;
extern std::string DebuggerSettings;
/// Initializes critical folders (AppRoot, DataRoot, Settings). Call once on startup.
void SetAppRoot();
bool SetResourcesDirectory();
bool SetDataDirectory(Error* error);
// Assumes that AppRoot and DataRoot have been initialized.
void SetDefaults(SettingsInterface& si);
void LoadConfig(SettingsInterface& si);
bool EnsureFoldersExist();
/// Opens the specified log file for writing.
std::FILE* OpenLogFile(std::string_view name, const char* mode);
/// Returns the path to a resource file, allowing the user to override it.
std::string GetOverridableResourcePath(std::string_view name);
} // namespace EmuFolders
/////////////////////////////////////////////////////////////////////////////////////////
// Helper Macros for Reading Emu Configurations.
//
// ------------ CPU / Recompiler Options ---------------
// (ARM64 Phase 7.8) microVU0/1 are now ported, so REC_VU1/THREAD_VU1 track the config
// on both architectures — the old ARM64 hardcoded-false stub would make GetGSPacketSize
// take its `!REC_VU1` path and return the XGKICK packet size with the bit31 EOP flag set,
// which mVU_XGKICK_ then mis-reads as a multi-GB transfer (memcpy crash on the first kick).
#define REC_VU1 (EmuConfig.Cpu.Recompiler.EnableVU1)
#define THREAD_VU1 (REC_VU1 && EmuConfig.Speedhacks.vuThread)
#define INSTANT_VU1 (EmuConfig.Speedhacks.vu1Instant)
#define CHECK_EEREC (EmuConfig.Cpu.Recompiler.EnableEE)
#define CHECK_CACHE (EmuConfig.Cpu.Recompiler.EnableEECache)
#define CHECK_IOPREC (EmuConfig.Cpu.Recompiler.EnableIOP)
#define CHECK_FASTMEM (EmuConfig.Cpu.Recompiler.EnableEE && EmuConfig.Cpu.Recompiler.EnableFastmem)
#define CHECK_EXTRAMEM (memGetExtraMemMode())
//------------ SPECIAL GAME FIXES!!! ---------------
#define CHECK_VUADDSUBHACK (EmuConfig.Gamefixes.VuAddSubHack) // Special Fix for Tri-ace games, they use an encryption algorithm that requires VU addi opcode to be bit-accurate.
#define CHECK_XGKICKHACK (EmuConfig.Gamefixes.XgKickHack) // Special Fix for Erementar Gerad, adds more delay to VU XGkick instructions. Corrects the color of some graphics.
#define CHECK_EETIMINGHACK (EmuConfig.Gamefixes.EETimingHack) // Fix all scheduled events to happen in 1 cycle.
#define CHECK_INSTANTDMAHACK (EmuConfig.Gamefixes.InstantDMAHack) // Attempt to finish DMA's instantly, useful for games which rely on cache emulation.
#define CHECK_SKIPMPEGHACK (EmuConfig.Gamefixes.SkipMPEGHack) // Finds sceMpegIsEnd pattern to tell the game the mpeg is finished (Katamari and a lot of games need this)
#define CHECK_OPHFLAGHACK (EmuConfig.Gamefixes.OPHFlagHack) // Bleach Blade Battlers
#define CHECK_DMABUSYHACK (EmuConfig.Gamefixes.DMABusyHack) // Denies writes to the DMAC when it's busy. This is correct behaviour but bad timing can cause problems.
#define CHECK_VIFFIFOHACK (EmuConfig.Gamefixes.VIFFIFOHack) // Pretends to fill the non-existant VIF FIFO Buffer.
#define CHECK_VIF1STALLHACK (EmuConfig.Gamefixes.VIF1StallHack) // Like above, processes FIFO data before the stall is allowed (to make sure data goes over).
#define CHECK_GIFFIFOHACK (EmuConfig.Gamefixes.GIFFIFOHack) // Enabled the GIF FIFO (more correct but slower)
#define CHECK_VUOVERFLOWHACK (EmuConfig.Gamefixes.VUOverflowHack) // Special Fix for Superman Returns, they check for overflows on PS2 floats which we can't do without soft floats.
#define CHECK_FULLVU0SYNCHACK (EmuConfig.Gamefixes.FullVU0SyncHack)
//------------ Advanced Options!!! ---------------
#define CHECK_VU_OVERFLOW(vunum) (((vunum) == 0) ? EmuConfig.Cpu.Recompiler.vu0Overflow : EmuConfig.Cpu.Recompiler.vu1Overflow)
#define CHECK_VU_EXTRA_OVERFLOW(vunum) (((vunum) == 0) ? EmuConfig.Cpu.Recompiler.vu0ExtraOverflow : EmuConfig.Cpu.Recompiler.vu1ExtraOverflow) // If enabled, Operands are clamped before being used in the VU recs
#define CHECK_VU_SIGN_OVERFLOW(vunum) (((vunum) == 0) ? EmuConfig.Cpu.Recompiler.vu0SignOverflow : EmuConfig.Cpu.Recompiler.vu1SignOverflow)
#define CHECK_VU_UNDERFLOW(vunum) (((vunum) == 0) ? EmuConfig.Cpu.Recompiler.vu0Underflow : EmuConfig.Cpu.Recompiler.vu1Underflow)
#define CHECK_FPU_OVERFLOW (EmuConfig.Cpu.Recompiler.fpuOverflow)
#define CHECK_FPU_EXTRA_OVERFLOW (EmuConfig.Cpu.Recompiler.fpuExtraOverflow) // If enabled, Operands are checked for infinities before being used in the FPU recs
#define CHECK_FPU_EXTRA_FLAGS 1 // Always enabled now // Sets D/I flags on FPU instructions
#define CHECK_FPU_FULL (EmuConfig.Cpu.Recompiler.fpuFullMode) // GameDB eeClampMode >= 3: the EE FPU's arithmetic is iFPUd's, computed in double over a relocated FPR file. Below it the single-precision fast path in iFPU-arm64.cpp runs.
#define CHECK_FPU_EXACT (EmuConfig.Cpu.Recompiler.fpuExactMode) // GameDB eeClampMode 4: mode 3 plus the rest of the EE multiplier's one-ULP deficit, at emitDefectiveFmul (iFPUd-arm64.cpp).
#define CHECK_FPU_GUARDED (EmuConfig.Cpu.Recompiler.fpuGuardedAddSub) // If enabled (default), add/sub emulate the PS2 FPU's missing mantissa guard bits on the single-precision fast path. Disable only for EE-heavy titles confirmed not to need it.
//------------ EE Recompiler defines - Comment to disable a recompiler ---------------
#define SHIFT_RECOMPILE // Speed majorly reduced if disabled
#define BRANCH_RECOMPILE // Speed extremely reduced if disabled - more then shift
// Disabling all the recompilers in this block is interesting, as it still runs at a reasonable rate.
// It also adds a few glitches. Really reminds me of the old Linux 64-bit version. --arcum42
#define ARITHMETICIMM_RECOMPILE
#define ARITHMETIC_RECOMPILE
#define MULTDIV_RECOMPILE
#define JUMP_RECOMPILE
#define LOADSTORE_RECOMPILE
#define MOVE_RECOMPILE
#define MMI_RECOMPILE
#define MMI0_RECOMPILE
#define MMI1_RECOMPILE
#define MMI2_RECOMPILE
#define MMI3_RECOMPILE
#define FPU_RECOMPILE
#define CP0_RECOMPILE
#define CP2_RECOMPILE
// You can't recompile ARITHMETICIMM without ARITHMETIC.
#ifndef ARITHMETIC_RECOMPILE
#undef ARITHMETICIMM_RECOMPILE
#endif
#define EE_CONST_PROP 1 // rec2 - enables constant propagation (faster)
// Change to 1 for console logs of SIF, GPU (PS1 mode) and MDEC (PS1 mode).
// These do spam a lot though!
#define PSX_EXTRALOGS 0
#undef BITFIELD32
#undef BITFIELD_END