keywords: UE4, UE5, Unreal Engine, Scalability Settings, Graphics Quality, Scalability Group

Scalability Command

Run follows in console command or execute UKismetSystemLibrary::ExecuteConsoleCommand() to modify graphics quality at run-time:

r.PostProcessAAQuality 4
sg.PostProcessQuality 3
sg.ShadowQuality 3
sg.TextureQuality 3
sg.EffectsQuality 3
foliage.DensityScale 1.0
grass.DensityScale 1.0

Blueprint function Execute Console Command (C++ interface is UKismetSystemLibrary::ExecuteConsoleCommand()) also works in shipping package.

Reference:
Scalability Reference
https://docs.unrealengine.com/en-us/Engine/Performance/Scalability/ScalabilityReference Unreal Engine 4 Save and Load Graphics Settings using Blueprints Tutorial
https://www.youtube.com/watch?v=5AXA2CrPwTo
Change Graphics Settings In-Game - Unreal Engine 4 Tutorial
https://www.youtube.com/watch?v=T90vqA9x5so

Q: Is there any way to set all quality groups to the same level?
A: Scalability::SetFromSingleQualityLevel(3); (in Engine\Source\Runtime\Engine\Public\Scalability.h)

// Sets all other settings based on an overall value
// @param Value 0:low, 1:medium, 2:high, 3:epic, 4:cinematic (gets clamped if needed)
ENGINE_API void SetFromSingleQualityLevel(int32 Value);

// Sets all other settings based on an overall value, but relative to the maximum.
// @param Value 0: maximum level, 1: maximumlevel -1, etc
ENGINE_API void SetFromSingleQualityLevelRelativeToMax(int32 Value);
Scalability on mobile

Take Android device as example, graphics quality configuration on mobile:
Example in \Engine\Config\Android\AndroidScalability.ini

; ------------------------------------------------------------------------------
; Foliage settings
; ------------------------------------------------------------------------------

[FoliageQuality@0]
foliage.DensityScale=0.5
grass.DensityScale=0.5

[FoliageQuality@1]
grass.densityScale=0.8
grass.CullDistanceScale=0.8
foliage.DensityScale=0.8

Take high-end Android device an example, the default configuration for different device type:
Example in \Engine\Config\BaseDeviceProfiles.ini

[Android_High DeviceProfile]
DeviceType=Android
BaseProfileName=Android
+CVars=sg.ViewDistanceQuality=2
+CVars=sg.AntiAliasingQuality=2
+CVars=sg.ShadowQuality=2
+CVars=sg.PostProcessQuality=2
+CVars=sg.TextureQuality=2
+CVars=sg.EffectsQuality=2
+CVars=sg.FoliageQuality=2
+CVars=r.MobileContentScaleFactor=1.0

Customizing Device Profiles and Scalability for Android - Official Docs

Scalability on PC & Console

Directory in PIE:
[UnrealEngine]/Engine/Config/BaseScalability.ini

Directory in shipped package:

  • Shipping Client: %localappdata%/[ProjectName]/Saved/Config/WindowsNoEditor/Engine.ini
  • Others: [ProjectName]/Saved/Config/WindowsNoEditor/Engine.ini

Example content in Engine\Config\BaseScalability.ini:

[ShadowQuality@0]
r.LightFunctionQuality=0
r.ShadowQuality=0
r.Shadow.CSM.MaxCascades=1
r.Shadow.MaxResolution=512
r.Shadow.MaxCSMResolution=512
r.Shadow.RadiusThreshold=0.06
r.Shadow.DistanceScale=0.6
r.Shadow.CSM.TransitionScale=0
r.Shadow.PreShadowResolutionFactor=0.5
r.DistanceFieldShadowing=0
r.DistanceFieldAO=0
r.VolumetricFog=0
r.LightMaxDrawDistanceScale=0
r.CapsuleShadows=0

Content in AndroidScalability.ini will be merged into Engine.ini automatically and override settings of Engine\Config\BaseScalability.ini while building package.

If want to change Scalability group, tweak it in [MyProj]\Saved\Config\Windows\GameUserSettings.ini, example in UE5:

[ScalabilityGroups]
sg.ResolutionQuality=0
sg.ViewDistanceQuality=3
sg.AntiAliasingQuality=3
sg.ShadowQuality=3
sg.GlobalIlluminationQuality=3
sg.ReflectionQuality=3
sg.PostProcessQuality=3
sg.TextureQuality=3
sg.EffectsQuality=3
sg.FoliageQuality=3
sg.ShadingQuality=3

[/Script/Engine.GameUserSettings]
bUseVSync=False
bUseDynamicResolution=False
ResolutionSizeX=1920
ResolutionSizeY=1200
LastUserConfirmedResolutionSizeX=1920
LastUserConfirmedResolutionSizeY=1200
WindowPosX=-1
WindowPosY=-1
FullscreenMode=1
LastConfirmedFullscreenMode=1
PreferredFullscreenMode=1
Version=5
AudioQualityLevel=0
LastConfirmedAudioQualityLevel=0
FrameRateLimit=0.000000
DesiredScreenWidth=1280
bUseDesiredScreenHeight=False
DesiredScreenHeight=720
LastUserConfirmedDesiredScreenWidth=1280
LastUserConfirmedDesiredScreenHeight=720
LastRecommendedScreenWidth=-1.000000
LastRecommendedScreenHeight=-1.000000
LastCPUBenchmarkResult=-1.000000
LastGPUBenchmarkResult=-1.000000
LastGPUBenchmarkMultiplier=1.000000
bUseHDRDisplayOutput=False
HDRDisplayOutputNits=1000

sg.ResolutionQuality isn’t using the usual 0/1/2/3 quality levels, the value directly maps to r.ScreenPercentage.
sg.ResolutionQuality=0 (by default) means to apply screen percentage as default value: UE5 is 66.662 = (1440.0f / 2160.0f - 0.1f / 2160.0f) , while UE4 is 100.
For more detail see BaseScalability.ini.

Screen Resolution on Mobile

iOS:

  • r.MobileContentScaleFactor 0.0:Native resolution.
  • r.MobileContentScaleFactor 1.0:Use non-Retina resolution on Retina devices.
  • r.MobileContentScaleFactor 2.0:Native resolution of iPhone 4 ~ iPhone 6.
  • r.MobileContentScaleFactor 3.0:Native resolution of iPhone 6+.

Android:

  • r.MobileContentScaleFactor 0.0:Native resolution.
  • r.MobileContentScaleFactor 1.0:720p (1280 x 720).
  • r.MobileContentScaleFactor 2.0:Scale of 1.0 (720p).

Origin:
https://www.jianshu.com/p/7ee6b5020767

Checking Scalability

The entry of setting scalability automatically on application startup:
in Engine\Source\Runtime\Engine\Private\DeviceProfiles\DeviceProfileManager.cpp

/**
 * Startup and select the active device profile
 * Then Init the CVars from this profile and it's Device profile parent tree.
 */
static void InitializeCVarsForActiveDeviceProfile(bool bPushSettings=false, bool bForceDeviceProfilePriority = false);

Gather device scalability detail and set graphics quality automatically:
Console command:

scalability auto

This command will also output details in log file.

C++ API:

if(UGameUserSettings* GameUserSettings = UGameUserSettings::GetGameUserSettings())
{
    GameUserSettings->RunHardwareBenchmark();
    GameUserSettings->ApplyHardwareBenchmarkResults();
}

Gather CPU and GPU benchmark data:

uint32 WorkScale = 10.f;
float CPUMultiplier = 1.f;
float GPUMultiplier = 1.f;
const Scalability::FQualityLevels State = Scalability::BenchmarkQualityLevels(uint32 WorkScale, float CPUMultiplier, float GPUMultiplier);
const float CPUIndex = State.CPUBenchmarkResults;
const float GPUIndex = State.GPUBenchmarkResults;

The return value corresponds to the configuration in Engine\Config\BaseScalability.ini

[ScalabilitySettings]
; PerfIndexThresholds define the thresholds that determine what the autodetected quality should be for each group.
; When you auto detect performance, both a CPUIndex and GPUIndex are calculated on the machine.
; Use the console command "scalability auto" to print these values for a machine.
; The type of perfindex used to determine the quality for a group is either the GPU, CPU or Min.
; GPU means the quality is based on the speed of the graphics card. CPU means the quality is based on the processor, and Min means the group quality is based on the slower of either the CPU or GPU.
; Each group has a type followed by three numbers.
; The first number is the perfindex threshold that changes quality from 0 to 1. The second is the threshold from 1 to 2, the third is the threshold from 2 to 3.
PerfIndexThresholds_ResolutionQuality="GPU 18 42 115"
PerfIndexThresholds_ViewDistanceQuality="Min 18 42 105"
PerfIndexThresholds_AntiAliasingQuality="GPU 18 42 115"
PerfIndexThresholds_ShadowQuality="Min 18 42 105"
PerfIndexThresholds_PostProcessQuality="GPU 18 42 115"
PerfIndexThresholds_TextureQuality="GPU 18 42 115"
PerfIndexThresholds_EffectsQuality="Min 18 42 105"
PerfIndexThresholds_FoliageQuality="GPU 18 42 115"
PerfIndexThresholds_ShadingQuality="GPU 18 42 115"

; This is the screen percentage for the resolution quality, corresponding to 25% pixels, 50% pixels, 75% pixels, and 100% pixels
PerfIndexValues_ResolutionQuality="50 71 87 100 100"

Output log:

Cmd: scalability auto
LogSynthBenchmark: Display: FSynthBenchmark (V0.95):  requested WorkScale=10.00
LogSynthBenchmark: Display: ===============
LogSynthBenchmark: Display: Main Processor:
LogSynthBenchmark: Display:          ... 0.049066 s/Run 'RayIntersect'
LogSynthBenchmark: Display:          ... 0.030856 s/Run 'Fractal'
LogSynthBenchmark: Display: 
LogSynthBenchmark: Display:   CompiledTarget_x_Bits: 64
LogSynthBenchmark: Display:   UE_BUILD_SHIPPING: 0
LogSynthBenchmark: Display:   UE_BUILD_TEST: 0
LogSynthBenchmark: Display:   UE_BUILD_DEBUG: 0
LogSynthBenchmark: Display:   TotalPhysicalGBRam: 32
LogSynthBenchmark: Display:   NumberOfCores (physical): 8
LogSynthBenchmark: Display:   NumberOfCores (logical): 8
LogSynthBenchmark: Display:   CPU Perf Index 0: 52.2 (weight 1.00)
LogSynthBenchmark: Display:   CPU Perf Index 1: 92.7 (weight 1.50)
LogSynthBenchmark: Display:  
LogSynthBenchmark: Display: Graphics:
LogSynthBenchmark: Display:   Adapter Name: 'Radeon RX 470 Series'
LogSynthBenchmark: Display:   (On Optimus the name might be wrong, memory should be ok)
LogSynthBenchmark: Display:   Vendor Id: 0x1002
LogSynthBenchmark: Display:   Device Id: 0x67DF
LogSynthBenchmark: Display:   Device Revision: 0xFF
LogSynthBenchmark: Display:   GPU Memory: 4076/0/16351 MB
LogSynthBenchmark: Display:   GPU first test: 0.05s
LogSynthBenchmark: Display:          ... 7.508 s/GigaPix, Confidence=89% 'ALUHeavyNoise' (likely to be very inaccurate)
LogSynthBenchmark: Display:          ... 5.685 s/GigaPix, Confidence=89% 'TexHeavy' (likely to be very inaccurate)
LogSynthBenchmark: Display:          ... 5.209 s/GigaPix, Confidence=89% 'DepTexHeavy' (likely to be very inaccurate)
LogSynthBenchmark: Display:          ... 15.020 s/GigaPix, Confidence=40% 'FillOnly' (likely to be very inaccurate)
LogSynthBenchmark: Display:          ... 0.629 s/GigaPix, Confidence=100% 'Bandwidth' (likely to be very inaccurate)
LogSynthBenchmark: Display:          ... 2.354 s/GigaVert, Confidence=51% 'VertThroughPut1' (likely to be very inaccurate)
LogSynthBenchmark: Display:          ... 4.441 s/GigaVert, Confidence=100% 'VertThroughPut2' (likely to be very inaccurate)
LogSynthBenchmark: Display:   GPU second test: 0.29s
LogSynthBenchmark: Display:          ... 11.496 s/GigaPix, Confidence=70% 'ALUHeavyNoise' (likely to be inaccurate)
LogSynthBenchmark: Display:          ... 8.107 s/GigaPix, Confidence=89% 'TexHeavy' (likely to be inaccurate)
LogSynthBenchmark: Display:          ... 7.910 s/GigaPix, Confidence=100% 'DepTexHeavy' (likely to be inaccurate)
LogSynthBenchmark: Display:          ... 32.663 s/GigaPix, Confidence=100% 'FillOnly' (likely to be inaccurate)
LogSynthBenchmark: Display:          ... 0.672 s/GigaPix, Confidence=100% 'Bandwidth' (likely to be inaccurate)
LogSynthBenchmark: Display:          ... 2.340 s/GigaVert, Confidence=100% 'VertThroughPut1' (likely to be inaccurate)
LogSynthBenchmark: Display:          ... 4.447 s/GigaVert, Confidence=100% 'VertThroughPut2' (likely to be inaccurate)
LogSynthBenchmark: Display:   GPU Final Results:
LogSynthBenchmark: Display:          ... 11.496 s/GigaPix, Confidence=70% 'ALUHeavyNoise'
LogSynthBenchmark: Display:          ... 8.107 s/GigaPix, Confidence=89% 'TexHeavy'
LogSynthBenchmark: Display:          ... 7.910 s/GigaPix, Confidence=100% 'DepTexHeavy'
LogSynthBenchmark: Display:          ... 32.663 s/GigaPix, Confidence=100% 'FillOnly'
LogSynthBenchmark: Display:          ... 0.672 s/GigaPix, Confidence=100% 'Bandwidth'
LogSynthBenchmark: Display:          ... 2.340 s/GigaVert, Confidence=100% 'VertThroughPut1'
LogSynthBenchmark: Display:          ... 4.447 s/GigaVert, Confidence=100% 'VertThroughPut2'
LogSynthBenchmark: Display: 
LogSynthBenchmark: Display:   GPU Perf Index 0: 249.9 (weight 1.00)
LogSynthBenchmark: Display:   GPU Perf Index 1: 108.9 (weight 0.10)
LogSynthBenchmark: Display:   GPU Perf Index 2: 205.6 (weight 0.10)
LogSynthBenchmark: Display:   GPU Perf Index 3: 128.3 (weight 3.00)
LogSynthBenchmark: Display:   GPU Perf Index 4: 62.7 (weight 1.00)
LogSynthBenchmark: Display:   GPU Perf Index 5: 152.2 (weight 0.00)
LogSynthBenchmark: Display:   GPU Perf Index 6: 251.7 (weight 0.00)
LogSynthBenchmark: Display:   GPUIndex: 140.2
LogSynthBenchmark: Display:   CPUIndex: 76.5
LogSynthBenchmark: Display: 
LogSynthBenchmark: Display:          ... Total Time: 2.228119 sec

Auto-detect Optimal Graphics Settings for Unreal
https://www.tomlooman.com/auto-detect-graphics-settings-ue4/

GameUserSettings->RunHardwareBenchmark(); and Scalability::BenchmarkQualityLevels(); only work on PC and console. The mobile device fluctuates inaccurately, and may be greatly affected by temperature.

By default GTX 970 will be identified as Epic group (yes, even UE5), but for your game, probably the Epic group should be at least RTX 3070, to address this, you can tweak GPUMultiplier, e.g. 0.39.

How to read config file (.ini)

config file path: [Project]/Config/DefaultMyGameSettings.ini

[/Script/MyGame.MyGameSettings]
+AssetList=/Game/Assets/Textures/T_Diffuse
+AssetList=/Game/Assets/Textures/T_Normal

Reading example:

TArray<FString> AssetList;
FString Path = FPaths::SourceConfigDir() + TEXT("DefaultMyGameSettings.ini");
GConfig->GetArray(TEXT("/Script/MyGame.MyGameSettings"), TEXT("+AssetList"), AssetList, Path);

Example from Engine\Source\Runtime\Engine\Private\Scalability.cpp:

const FString ArrayKey = FString(TEXT("PerfIndexThresholds_")) + GroupName;
TArray<FString> PerfIndexThresholds;
GConfig->GetSingleLineArray(TEXT("ScalabilitySettings"), *ArrayKey, PerfIndexThresholds, GScalabilityIni);
How to get device profile name
FString DefaultDeviceProfileName = FPlatformMisc::GetDefaultDeviceProfileName();
UE_LOG(LogTemp, Display, TEXT("DeviceProfile+++++ %s"), *DefaultDeviceProfileName);

UDeviceProfile* DeviceProfile = UDeviceProfileManager::Get().GetActiveProfile();
FString ProfileName = "";
if (DeviceProfile != nullptr)
{
    if (DeviceProfile->BaseProfileName.IsEmpty())
    {
        ProfileName = DeviceProfile->GetName();
    }
    else
    {
        ProfileName = DeviceProfile->BaseProfileName;
    }
}
How to get scalability value using C++
int32 PostProcessQuality = UKismetSystemLibrary::GetConsoleVariableIntValue(TEXT("sg.PostProcessQuality"))

Or

int Value = 0;
IConsoleVariable* Variable = IConsoleManager::Get().FindConsoleVariable(TEXT("sg.PostProcessQuality"));
if (Variable)
{
    Value = Variable->GetInt();
}
How to change RHI (DirectX 11, DirectX 12)

Engine.ini

[/Script/WindowsTargetPlatform.WindowsTargetSettings]
DefaultGraphicsRHI=DefaultGraphicsRHI_DX11
How to run game in fixed frame rate

Append follows in Engine.ini.
Fixed frame rate:

[/Script/Engine.Engine]
bUseFixedFrameRate=True
FixedFrameRate=60.000000

Unlimited frame rate:

[/Script/Engine.Engine]
bUseFixedFrameRate=False

You need to disable VSync if want to play with fixed frame rate: Engine.ini

[SystemSettings]
r.VSync=0

Or alter GameUserSettings.ini

[/Script/Engine.GameUserSettings]
bUseVSync=False
How to start game in fullscreen mode

Add follows in MyProject/Config/DefaultGameUserSettings.ini

[/Script/Engine.GameUserSettings]
//ResolutionSizeX=1920
//ResolutionSizeY=1080
//LastUserConfirmedResolutionSizeX=1920
//LastUserConfirmedResolutionSizeY=1080
WindowPosX=-1
WindowPosY=-1
bUseDesktopResolutionForFullscreen=True
FullscreenMode=0
LastConfirmedFullscreenMode=0

The mode type explanation in GameUserSettings.h:

/**
 * Game window fullscreen mode
 *  0 = Fullscreen
 *  1 = Windowed fullscreen
 *  2 = Windowed
 */
UPROPERTY(config)
int32 FullscreenMode;

Origin:
https://answers.unrealengine.com/questions/206504/how-to-default-fullscreen-in-shipping-package.html

How to get CPU model and GPU model (brand information)
FString FWindowsPlatformMisc::GetPrimaryGPUBrand()
FString FWindowsPlatformMisc::GetCPUBrand()
Callstack: from UKismetSystemLibrary::ExecuteConsoleCommand to FConsoleVariableBase::CanChange

Callstack in UE5 (but still works for UE4):

FConsoleVariableBase::CanChange(EConsoleVariableFlags SetBy) Line 144
    at D:\UE5\Engine\Source\Runtime\Core\Private\HAL\ConsoleManager.cpp(144)
FConsoleVariable<int>::Set(const wchar_t * InValue, EConsoleVariableFlags SetBy) Line 568
    at D:\UE5\Engine\Source\Runtime\Core\Private\HAL\ConsoleManager.cpp(568)
OnSetCVarFromIniEntry(const wchar_t * IniFile, const wchar_t * Key, const wchar_t * Value, unsigned int SetBy, bool bAllowCheating) Line 5094
    at D:\UE5\Engine\Source\Runtime\Core\Private\Misc\ConfigCacheIni.cpp(5094)
ApplyCVarSettingsFromIni(const wchar_t * InSectionName, const wchar_t * InIniFilename, unsigned int SetBy, bool bAllowCheating) Line 5138
    at D:\UE5\Engine\Source\Runtime\Core\Private\Misc\ConfigCacheIni.cpp(5138)
Scalability::SetGroupQualityLevel(const wchar_t * InGroupName, int InQualityLevel, int InNumLevels) Line 402
    at D:\UE5\Engine\Source\Runtime\Engine\Private\Scalability.cpp(402)
Invoke(void(*)(IConsoleVariable *) &) Line 47
    at D:\UE5\Engine\Source\Runtime\Core\Public\Templates\Invoke.h(47)
UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int>>::ApplyAfter(void(*)(IConsoleVariable *) &) Line 324
    at D:\UE5\Engine\Source\Runtime\Core\Public\Templates\Tuple.h(324)
TBaseStaticDelegateInstance<void __cdecl(IConsoleVariable *),FDefaultDelegateUserPolicy>::ExecuteIfSafe(IConsoleVariable * <Params_0>) Line 731
    at D:\UE5\Engine\Source\Runtime\Core\Public\Delegates\DelegateInstancesImpl.h(731)
TMulticastDelegate<void __cdecl(IConsoleVariable *),FDefaultDelegateUserPolicy>::Broadcast(IConsoleVariable *) Line 967
    at D:\UE5\Engine\Source\Runtime\Core\Public\Delegates\DelegateSignatureImpl.inl(967)
FConsoleVariableBase::OnChanged(EConsoleVariableFlags SetBy) Line 196
    at D:\UE5\Engine\Source\Runtime\Core\Private\HAL\ConsoleManager.cpp(196)
FConsoleVariable<int>::OnChanged(EConsoleVariableFlags) Line 605
    at D:\UE5\Engine\Source\Runtime\Core\Private\HAL\ConsoleManager.cpp(605)
FConsoleVariable<int>::Set(const wchar_t * InValue, EConsoleVariableFlags SetBy) Line 571
    at D:\UE5\Engine\Source\Runtime\Core\Private\HAL\ConsoleManager.cpp(571)
FConsoleManager::ProcessUserConsoleInput(const wchar_t * InInput, FOutputDevice & Ar, UWorld * InWorld) Line 1798
    at D:\UE5\Engine\Source\Runtime\Core\Private\HAL\ConsoleManager.cpp(1798)
UEngine::Exec(UWorld * InWorld, const wchar_t * Cmd, FOutputDevice & Ar) Line 4871
    at D:\UE5\Engine\Source\Runtime\Engine\Private\UnrealEngine.cpp(4871)
UEditorEngine::Exec(UWorld * InWorld, const wchar_t * Stream, FOutputDevice & Ar) Line 5914
    at D:\UE5\Engine\Source\Editor\UnrealEd\Private\EditorServer.cpp(5914)
UUnrealEdEngine::Exec(UWorld * InWorld, const wchar_t * Stream, FOutputDevice & Ar) Line 659
    at D:\UE5\Engine\Source\Editor\UnrealEd\Private\UnrealEdSrv.cpp(659)
UGameViewportClient::Exec(UWorld * InWorld, const wchar_t * Cmd, FOutputDevice & Ar) Line 3014
    at D:\UE5\Engine\Source\Runtime\Engine\Private\GameViewportClient.cpp(3014)
ULocalPlayer::Exec(UWorld * InWorld, const wchar_t * Cmd, FOutputDevice & Ar) Line 1499
    at D:\UE5\Engine\Source\Runtime\Engine\Private\LocalPlayer.cpp(1499)
UPlayer::ConsoleCommand(const FString & Cmd, bool bWriteToLog) Line 51
    at D:\UE5\Engine\Source\Runtime\Engine\Private\Player.cpp(51)
APlayerController::ConsoleCommand(const FString & Cmd, bool bWriteToLog) Line 411
    at D:\UE5\Engine\Source\Runtime\Engine\Private\PlayerController.cpp(411)
UKismetSystemLibrary::ExecuteConsoleCommand(const UObject * WorldContextObject, const FString & Command, APlayerController * Player) Line 396
    at D:\UE5\Engine\Source\Runtime\Engine\Private\KismetSystemLibrary.cpp(396)
Issue: Console Priority

Error when execute console command:

LogConsoleManager: Warning: |ConsoleManager.cpp:161|Setting the console variable 'r.ViewDistanceScale' with 'SetByScalability' was ignored as it is lower priority than the previous 'SetByConsole'. Value remains '1'

Solution:

IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(TEXT("r.ViewDistanceScale"));
Var->Set(TEXT("0.8"), ECVF_SetByConsole);

Origin: UE4控制台命令
https://www.cnblogs.com/kekec/p/11102365.html

References

Lyra Scalability and Device Profiles - UE5 Official Docs


A man who cannot tolerate small misfortunes can never accomplish great things. -Chinese Proverbs