RealtimeStyleTransferRuntime/Source/LyraGame/Settings/CustomSettings/LyraSettingValueDiscrete_Re...

446 lines
15 KiB
C++
Raw Normal View History

2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraSettingValueDiscrete_Resolution.h"
#include "GameFramework/GameUserSettings.h"
#include "RHI.h"
#include "Framework/Application/SlateApplication.h"
#include "Engine/Engine.h"
#include "UnrealEngine.h"
#define LOCTEXT_NAMESPACE "LyraSettings"
ULyraSettingValueDiscrete_Resolution::ULyraSettingValueDiscrete_Resolution()
{
}
void ULyraSettingValueDiscrete_Resolution::OnInitialized()
{
Super::OnInitialized();
InitializeResolutions();
}
void ULyraSettingValueDiscrete_Resolution::StoreInitial()
{
// Ignored
}
void ULyraSettingValueDiscrete_Resolution::ResetToDefault()
{
// Ignored
}
void ULyraSettingValueDiscrete_Resolution::RestoreToInitial()
{
// Ignored
}
void ULyraSettingValueDiscrete_Resolution::SetDiscreteOptionByIndex(int32 Index)
{
if (Resolutions.IsValidIndex(Index) && Resolutions[Index].IsValid())
{
GEngine->GetGameUserSettings()->SetScreenResolution(Resolutions[Index]->GetResolution());
NotifySettingChanged(EGameSettingChangeReason::Change);
}
}
int32 ULyraSettingValueDiscrete_Resolution::GetDiscreteOptionIndex() const
{
const UGameUserSettings* UserSettings = CastChecked<const UGameUserSettings>(GEngine->GetGameUserSettings());
return FindIndexOfDisplayResolutionForceValid(UserSettings->GetScreenResolution());
}
TArray<FText> ULyraSettingValueDiscrete_Resolution::GetDiscreteOptions() const
{
TArray<FText> ReturnResolutionTexts;
for (int32 i = 0; i < Resolutions.Num(); ++i)
{
ReturnResolutionTexts.Add(Resolutions[i]->GetDisplayText());
}
return ReturnResolutionTexts;
}
void ULyraSettingValueDiscrete_Resolution::OnDependencyChanged()
{
const FIntPoint CurrentResolution = GEngine->GetGameUserSettings()->GetScreenResolution();
SelectAppropriateResolutions();
SetDiscreteOptionByIndex(FindClosestResolutionIndex(CurrentResolution));
}
void ULyraSettingValueDiscrete_Resolution::InitializeResolutions()
{
Resolutions.Empty();
ResolutionsFullscreen.Empty();
ResolutionsWindowed.Empty();
ResolutionsWindowedFullscreen.Empty();
FDisplayMetrics InitialDisplayMetrics;
FSlateApplication::Get().GetInitialDisplayMetrics(InitialDisplayMetrics);
FScreenResolutionArray ResArray;
RHIGetAvailableResolutions(ResArray, true);
// Determine available windowed modes
{
TArray<FIntPoint> WindowedResolutions;
const FIntPoint MinResolution(1280, 720);
// Use the primary display resolution minus 1 to exclude the primary display resolution from the list.
// This is so you don't make a window so large that part of the game is off screen and you are unable to change resolutions back.
const FIntPoint MaxResolution(InitialDisplayMetrics.PrimaryDisplayWidth - 1, InitialDisplayMetrics.PrimaryDisplayHeight - 1);
// Excluding 4:3 and below
const float MinAspectRatio = 16 / 10.f;
if (MaxResolution.X >= MinResolution.X && MaxResolution.Y >= MinResolution.Y)
{
GetStandardWindowResolutions(MinResolution, MaxResolution, MinAspectRatio, WindowedResolutions);
}
if (GSystemResolution.WindowMode == EWindowMode::Windowed)
{
WindowedResolutions.AddUnique(FIntPoint(GSystemResolution.ResX, GSystemResolution.ResY));
WindowedResolutions.Sort([](const FIntPoint& A, const FIntPoint& B) { return A.X != B.X ? A.X < B.X : A.Y < B.Y; });
}
// If there were no standard resolutions. Add the primary display size, just so one exists.
// This might happen if we are running on a non-standard device.
if (WindowedResolutions.Num() == 0)
{
WindowedResolutions.Add(FIntPoint(InitialDisplayMetrics.PrimaryDisplayWidth, InitialDisplayMetrics.PrimaryDisplayHeight));
}
ResolutionsWindowed.Empty(WindowedResolutions.Num());
for (const FIntPoint& Res : WindowedResolutions)
{
TSharedRef<FScreenResolutionEntry> Entry = MakeShared<FScreenResolutionEntry>();
Entry->Width = Res.X;
Entry->Height = Res.Y;
ResolutionsWindowed.Add(Entry);
}
}
// Determine available windowed full-screen modes
{
FScreenResolutionRHI* RHIInitialResolution = ResArray.FindByPredicate([InitialDisplayMetrics](const FScreenResolutionRHI& ScreenRes) {
return ScreenRes.Width == InitialDisplayMetrics.PrimaryDisplayWidth && ScreenRes.Height == InitialDisplayMetrics.PrimaryDisplayHeight;
});
TSharedRef<FScreenResolutionEntry> Entry = MakeShared<FScreenResolutionEntry>();
if (RHIInitialResolution)
{
// If this is in the official list use that
Entry->Width = RHIInitialResolution->Width;
Entry->Height = RHIInitialResolution->Height;
Entry->RefreshRate = RHIInitialResolution->RefreshRate;
}
else
{
// Custom resolution the RHI doesn't expect
Entry->Width = InitialDisplayMetrics.PrimaryDisplayWidth;
Entry->Height = InitialDisplayMetrics.PrimaryDisplayHeight;
// TODO: Unsure how to calculate refresh rate
Entry->RefreshRate = FPlatformMisc::GetMaxRefreshRate();
}
ResolutionsWindowedFullscreen.Add(Entry);
}
// Determine available full-screen modes
if (ResArray.Num() > 0)
{
// try more strict first then more relaxed, we want at least one resolution to remain
for (int32 FilterThreshold = 0; FilterThreshold < 3; ++FilterThreshold)
{
for (int32 ModeIndex = 0; ModeIndex < ResArray.Num(); ModeIndex++)
{
const FScreenResolutionRHI& ScreenRes = ResArray[ModeIndex];
// first try with struct test, than relaxed test
if (ShouldAllowFullScreenResolution(ScreenRes, FilterThreshold))
{
TSharedRef<FScreenResolutionEntry> Entry = MakeShared<FScreenResolutionEntry>();
Entry->Width = ScreenRes.Width;
Entry->Height = ScreenRes.Height;
Entry->RefreshRate = ScreenRes.RefreshRate;
ResolutionsFullscreen.Add(Entry);
}
}
if (ResolutionsFullscreen.Num())
{
// we found some resolutions, otherwise we try with more relaxed tests
break;
}
}
}
SelectAppropriateResolutions();
}
void ULyraSettingValueDiscrete_Resolution::SelectAppropriateResolutions()
{
EWindowMode::Type const WindowMode = GEngine->GetGameUserSettings()->GetFullscreenMode();
if (LastWindowMode != WindowMode)
{
LastWindowMode = WindowMode;
Resolutions.Empty();
switch (WindowMode)
{
case EWindowMode::Windowed:
Resolutions.Append(ResolutionsWindowed);
break;
case EWindowMode::WindowedFullscreen:
Resolutions.Append(ResolutionsWindowedFullscreen);
break;
case EWindowMode::Fullscreen:
Resolutions.Append(ResolutionsFullscreen);
break;
}
NotifyEditConditionsChanged();
}
}
// To filter out odd resolution so UI and testing has less issues. This is game specific.
// @param ScreenRes resolution and
// @param FilterThreshold 0/1/2 to make sure we get at least some resolutions (might be an issues with UI but at least we get some resolution entries)
bool ULyraSettingValueDiscrete_Resolution::ShouldAllowFullScreenResolution(const FScreenResolutionRHI& SrcScreenRes, int32 FilterThreshold) const
{
FScreenResolutionRHI ScreenRes = SrcScreenRes;
// expected: 4:3=1.333, 16:9=1.777, 16:10=1.6, multi-monitor-wide: >2
bool bIsPortrait = ScreenRes.Width < ScreenRes.Height;
float AspectRatio = (float)ScreenRes.Width / (float)ScreenRes.Height;
// If portrait, flip values back to landscape so we can don't have to special case all the tests below
if (bIsPortrait)
{
AspectRatio = 1.0f / AspectRatio;
ScreenRes.Width = SrcScreenRes.Height;
ScreenRes.Height = SrcScreenRes.Width;
}
// Filter out resolutions that don't match the native aspect ratio of the primary monitor
// TODO: Other games allow the user to choose which monitor the games goes fullscreen on. This would allow
// this filtering to be correct when the users monitors are of different types! ATM, the game can change
// which monitor it uses based on other factors (max window overlap etc.) so we could end up choosing a
// resolution which the target monitor doesn't support.
if (FilterThreshold < 1)
{
FDisplayMetrics DisplayMetrics;
FSlateApplication::Get().GetInitialDisplayMetrics(DisplayMetrics);
// Default display aspect to required aspect in case this platform can't provide the information. Forces acceptance of this resolution.
float DisplayAspect = AspectRatio;
// Some platforms might not be able to detect the native resolution of the display device, so don't filter in that case
for (int32 MonitorIndex = 0; MonitorIndex < DisplayMetrics.MonitorInfo.Num(); ++MonitorIndex)
{
FMonitorInfo& MonitorInfo = DisplayMetrics.MonitorInfo[MonitorIndex];
if (MonitorInfo.bIsPrimary)
{
DisplayAspect = (float)MonitorInfo.NativeWidth / (float)MonitorInfo.NativeHeight;
break;
}
}
// If aspects are not almost exactly equal, reject
if (FMath::Abs(DisplayAspect - AspectRatio) > KINDA_SMALL_NUMBER)
{
return false;
}
}
// more relaxed tests have a larger FilterThreshold
// minimum is 1280x720
if (FilterThreshold < 2 && (ScreenRes.Width < 1280 || ScreenRes.Height < 720))
{
// filter resolutions that are too small
return false;
}
return true;
}
int32 ULyraSettingValueDiscrete_Resolution::FindIndexOfDisplayResolution(const FIntPoint& InPoint) const
{
// find the current res
for (int32 i = 0, Num = Resolutions.Num(); i < Num; ++i)
{
if (Resolutions[i]->GetResolution() == InPoint)
{
return i;
}
}
return INDEX_NONE;
}
int32 ULyraSettingValueDiscrete_Resolution::FindIndexOfDisplayResolutionForceValid(const FIntPoint& InPoint) const
{
int32 Result = FindIndexOfDisplayResolution(InPoint);
if (Result == INDEX_NONE && Resolutions.Num() > 0)
{
Result = Resolutions.Num() - 1;
}
return Result;
}
int32 ULyraSettingValueDiscrete_Resolution::FindClosestResolutionIndex(const FIntPoint& Resolution) const
{
int32 Index = 0;
int32 LastDiff = Resolution.SizeSquared();
for (int32 i = 0, Num = Resolutions.Num(); i < Num; ++i)
{
// We compare the squared diagonals
int32 Diff = FMath::Abs(Resolution.SizeSquared() - Resolutions[i]->GetResolution().SizeSquared());
if (Diff <= LastDiff)
{
Index = i;
}
LastDiff = Diff;
}
return Index;
}
void ULyraSettingValueDiscrete_Resolution::GetStandardWindowResolutions(const FIntPoint& MinResolution, const FIntPoint& MaxResolution, float MinAspectRatio, TArray<FIntPoint>& OutResolutions)
{
static TArray<FIntPoint> StandardResolutions;
if (StandardResolutions.Num() == 0)
{
// Standard resolutions as provided by Wikipedia (http://en.wikipedia.org/wiki/Graphics_display_resolution)
// Extended Graphics Array
{
new(StandardResolutions) FIntPoint(1024, 768); // XGA
// WXGA (3 versions)
new(StandardResolutions) FIntPoint(1366, 768); // FWXGA
new(StandardResolutions) FIntPoint(1360, 768);
new(StandardResolutions) FIntPoint(1280, 800);
new(StandardResolutions) FIntPoint(1152, 864); // XGA+
new(StandardResolutions) FIntPoint(1440, 900); // WXGA+
new(StandardResolutions) FIntPoint(1280, 1024); // SXGA
new(StandardResolutions) FIntPoint(1400, 1050); // SXGA+
new(StandardResolutions) FIntPoint(1680, 1050); // WSXGA+
new(StandardResolutions) FIntPoint(1600, 1200); // UXGA
new(StandardResolutions) FIntPoint(1920, 1200); // WUXGA
}
// Quad Extended Graphics Array
{
new(StandardResolutions) FIntPoint(2048, 1152); // QWXGA
new(StandardResolutions) FIntPoint(2048, 1536); // QXGA
new(StandardResolutions) FIntPoint(2560, 1600); // WQXGA
new(StandardResolutions) FIntPoint(2560, 2048); // QSXGA
new(StandardResolutions) FIntPoint(3200, 2048); // WQSXGA
new(StandardResolutions) FIntPoint(3200, 2400); // QUXGA
new(StandardResolutions) FIntPoint(3840, 2400); // WQUXGA
}
// Hyper Extended Graphics Array
{
new(StandardResolutions) FIntPoint(4096, 3072); // HXGA
new(StandardResolutions) FIntPoint(5120, 3200); // WHXGA
new(StandardResolutions) FIntPoint(5120, 4096); // HSXGA
new(StandardResolutions) FIntPoint(6400, 4096); // WHSXGA
new(StandardResolutions) FIntPoint(6400, 4800); // HUXGA
new(StandardResolutions) FIntPoint(7680, 4800); // WHUXGA
}
// High-Definition
{
new(StandardResolutions) FIntPoint(640, 360); // nHD
new(StandardResolutions) FIntPoint(960, 540); // qHD
new(StandardResolutions) FIntPoint(1280, 720); // HD
new(StandardResolutions) FIntPoint(1920, 1080); // FHD
new(StandardResolutions) FIntPoint(2560, 1440); // QHD
new(StandardResolutions) FIntPoint(3200, 1800); // WQXGA+
new(StandardResolutions) FIntPoint(3840, 2160); // UHD 4K
new(StandardResolutions) FIntPoint(4096, 2160); // Digital Cinema Initiatives 4K
new(StandardResolutions) FIntPoint(7680, 4320); // FUHD
new(StandardResolutions) FIntPoint(5120, 2160); // UHD 5K
new(StandardResolutions) FIntPoint(5120, 2880); // UHD+
new(StandardResolutions) FIntPoint(15360, 8640); // QUHD
}
// Sort the list by total resolution size
StandardResolutions.Sort([](const FIntPoint& A, const FIntPoint& B) { return (A.X * A.Y) < (B.X * B.Y); });
}
// Return all standard resolutions that are within the size constraints
for (const auto& Resolution : StandardResolutions)
{
if (Resolution.X >= MinResolution.X && Resolution.Y >= MinResolution.Y && Resolution.X <= MaxResolution.X && Resolution.Y <= MaxResolution.Y)
{
const float AspectRatio = Resolution.X / (float)Resolution.Y;
if (AspectRatio > MinAspectRatio || FMath::IsNearlyEqual(AspectRatio, MinAspectRatio))
{
OutResolutions.Add(Resolution);
}
}
}
}
FText ULyraSettingValueDiscrete_Resolution::FScreenResolutionEntry::GetDisplayText() const
{
if (!OverrideText.IsEmpty())
{
return OverrideText;
}
FText Aspect = FText::GetEmpty();
// expected: 4:3=1.333, 16:9=1.777, 16:10=1.6, multi-monitor-wide: >2
float AspectRatio = (float)Width / (float)Height;
if (FMath::Abs(AspectRatio - (4.0f / 3.0f)) < KINDA_SMALL_NUMBER)
{
Aspect = LOCTEXT("AspectRatio-4:3", "4:3");
}
else if (FMath::Abs(AspectRatio - (16.0f / 9.0f)) < KINDA_SMALL_NUMBER)
{
Aspect = LOCTEXT("AspectRatio-16:9", "16:9");
}
else if (FMath::Abs(AspectRatio - (16.0f / 10.0f)) < KINDA_SMALL_NUMBER)
{
Aspect = LOCTEXT("AspectRatio-16:10", "16:10");
}
else if (FMath::Abs(AspectRatio - (3.0f / 4.0f)) < KINDA_SMALL_NUMBER)
{
Aspect = LOCTEXT("AspectRatio-3:4", "3:4");
}
else if (FMath::Abs(AspectRatio - (9.0f / 16.0f)) < KINDA_SMALL_NUMBER)
{
Aspect = LOCTEXT("AspectRatio-9:16", "9:16");
}
else if (FMath::Abs(AspectRatio - (10.0f / 16.0f)) < KINDA_SMALL_NUMBER)
{
Aspect = LOCTEXT("AspectRatio-10:16", "10:16");
}
FNumberFormattingOptions Options;
Options.UseGrouping = false;
FFormatNamedArguments Args;
Args.Add(TEXT("X"), FText::AsNumber(Width, &Options));
Args.Add(TEXT("Y"), FText::AsNumber(Height, &Options));
Args.Add(TEXT("AspectRatio"), Aspect);
Args.Add(TEXT("RefreshRate"), RefreshRate);
return FText::Format(LOCTEXT("AspectRatio", "{X} x {Y}"), Args);
}
#undef LOCTEXT_NAMESPACE