diff --git a/OptiScaler/State.h b/OptiScaler/State.h index c3057b17..4284a230 100644 --- a/OptiScaler/State.h +++ b/OptiScaler/State.h @@ -7,6 +7,7 @@ #include #include #include +#include typedef enum API { @@ -16,17 +17,15 @@ typedef enum API Vulkan, } API; -typedef enum GameQuirk +enum class GameQuirk { - Other, - Cyberpunk, - FMF2, - RDR1, - Banishers, - SplitFiction, - PoE2, - KernelBaseHooks -} GameQuirk; + CyberpunkHudlessStateOverride, + SkipFsr3Method, + FastFeatureReset, + LoadD3D12Manually, + KernelBaseHooks, + _ +}; typedef enum FGType : uint32_t { @@ -52,11 +51,12 @@ class State bool NvngxDx12Inited = false; bool NvngxVkInited = false; + flag_set gameQuirks; + // Reseting on creation of new feature std::optional AutoExposure; // DLSSG - GameQuirk gameQuirk = GameQuirk::Other; bool NukemsFilesAvailable = false; bool DLSSGDebugView = false; bool DLSSGInterpolatedOnly = false; diff --git a/OptiScaler/dllmain.cpp b/OptiScaler/dllmain.cpp index c77d93c7..f2b4802e 100644 --- a/OptiScaler/dllmain.cpp +++ b/OptiScaler/dllmain.cpp @@ -637,9 +637,9 @@ static void CheckWorkingMode() HooksDx::HookDx12(); } - if (D3d12Proxy::Module() == nullptr && State::Instance().gameQuirk == PoE2) + if (D3d12Proxy::Module() == nullptr && State::Instance().gameQuirks & GameQuirk::LoadD3D12Manually) { - LOG_DEBUG("Loading d3d12.dll for PoE2"); + LOG_DEBUG("Loading d3d12.dll manually"); D3d12Proxy::Init(); } @@ -865,7 +865,7 @@ static void CheckQuirks() if (exePathFilename == "cyberpunk2077.exe") { - State::Instance().gameQuirk = Cyberpunk; + State::Instance().gameQuirks.set(GameQuirk::CyberpunkHudlessStateOverride); // Disabled OptiFG for now if (Config::Instance()->FGType.value_or_default() == FGType::OptiFG) @@ -876,8 +876,6 @@ static void CheckQuirks() } else if (exePathFilename == "fmf2-win64-shipping.exe") { - State::Instance().gameQuirk = FMF2; - if (!Config::Instance()->UseFsr3Inputs.has_value()) { Config::Instance()->UseFsr3Inputs.set_volatile_value(false); @@ -892,15 +890,15 @@ static void CheckQuirks() } else if (exePathFilename == "rdr.exe" || exePathFilename == "playrdr.exe") { - State::Instance().gameQuirk = RDR1; + State::Instance().gameQuirks.set(GameQuirk::SkipFsr3Method); + if (Config::Instance()->FGType.value_or_default() == FGType::OptiFG) Config::Instance()->FGType.set_volatile_value(FGType::NoFG); + LOG_INFO("Enabling a quirk for RDR1 (Disable FSR-FG Swapchain)"); } else if (exePathFilename == "banishers-win64-shipping.exe") { - State::Instance().gameQuirk = Banishers; - if (!Config::Instance()->Fsr2Pattern.has_value()) { Config::Instance()->Fsr2Pattern.set_volatile_value(false); @@ -909,23 +907,23 @@ static void CheckQuirks() } else if (exePathFilename == "splitfiction.exe") { - State::Instance().gameQuirk = SplitFiction; + State::Instance().gameQuirks.set(GameQuirk::FastFeatureReset); LOG_INFO("Enabling a quirk for Split Fiction (Quick upscaler reinit)"); } else if (exePathFilename == "minecraft.windows.exe") { - State::Instance().gameQuirk = KernelBaseHooks; + State::Instance().gameQuirks.set(GameQuirk::KernelBaseHooks); LOG_INFO("Enabling a quirk for Minecraft (Enable KernelBase hooks)"); } else if (exePathFilename == "nms.exe") { - State::Instance().gameQuirk = KernelBaseHooks; + State::Instance().gameQuirks.set(GameQuirk::KernelBaseHooks); LOG_INFO("Enabling a quirk for No Man's Sky (Enable KernelBase hooks)"); } else if (exePathFilename == "pathofexile.exe" || exePathFilename == "pathofexile_x64.exe" || exePathFilename == "pathofexile_x64steam.exe" || exePathFilename == "pathofexilesteam.exe") { - State::Instance().gameQuirk = PoE2; + State::Instance().gameQuirks.set(GameQuirk::LoadD3D12Manually); LOG_INFO("Enabling a quirk for PoE2 (Load d3d12.dll)"); } } diff --git a/OptiScaler/hooks/Kernel_Hooks.h b/OptiScaler/hooks/Kernel_Hooks.h index 288e1f76..6fb27c85 100644 --- a/OptiScaler/hooks/Kernel_Hooks.h +++ b/OptiScaler/hooks/Kernel_Hooks.h @@ -1755,7 +1755,7 @@ class KernelHooks // These hooks cause stability regressions // o_KB_FreeLibrary = KernelBaseProxy::Hook_FreeLibrary(hk_KB_FreeLibrary); - if (State::Instance().gameQuirk == KernelBaseHooks) + if (State::Instance().gameQuirks & GameQuirk::KernelBaseHooks) { // o_KB_LoadLibraryA = KernelBaseProxy::Hook_LoadLibraryA(hk_KB_LoadLibraryA); // o_KB_LoadLibraryW = KernelBaseProxy::Hook_LoadLibraryW(hk_KB_LoadLibraryW); diff --git a/OptiScaler/hooks/Streamline_Hooks.cpp b/OptiScaler/hooks/Streamline_Hooks.cpp index a4f76d22..aa81d38c 100644 --- a/OptiScaler/hooks/Streamline_Hooks.cpp +++ b/OptiScaler/hooks/Streamline_Hooks.cpp @@ -83,7 +83,7 @@ sl::Result StreamlineHooks::hkslSetTag(sl::ViewportHandle& viewport, sl::Resourc { for (uint32_t i = 0; i < numTags; i++) { - if (State::Instance().gameQuirk == Cyberpunk && tags[i].type == 2 && + if (State::Instance().gameQuirks & GameQuirk::CyberpunkHudlessStateOverride && tags[i].type == 2 && tags[i].resource->state == (D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)) { diff --git a/OptiScaler/include/flag-set-cpp/flag_set.hpp b/OptiScaler/include/flag-set-cpp/flag_set.hpp new file mode 100644 index 00000000..ed6ae10b --- /dev/null +++ b/OptiScaler/include/flag-set-cpp/flag_set.hpp @@ -0,0 +1,177 @@ +// flag_set is a type-safe class for using enums as flags in C++14 with an underlying std::bitset. +// See https://github.com/mrts/flag-set-cpp +// Licence: MIT, see LICENCE + +#pragma once + +#include +#include +#include +#include + +template +class flag_set +{ +public: + flag_set() = default; + + explicit flag_set(const T& val) { flags.set(static_cast(val)); } + + // Binary operations. + + flag_set& operator&=(const T& val) noexcept + { + bool tmp = flags.test(static_cast(val)); + flags.reset(); + flags.set(static_cast(val), tmp); + return *this; + } + + flag_set& operator&=(const flag_set& o) noexcept + { + flags &= o.flags; + return *this; + } + + flag_set& operator|=(const T& val) noexcept + { + flags.set(static_cast(val)); + return *this; + } + + flag_set& operator|=(const flag_set& o) noexcept + { + flags |= o.flags; + return *this; + } + + // The resulting bitset can contain at most 1 bit. + flag_set operator&(const T& val) const + { + flag_set ret(*this); + ret &= val; + + assert(ret.flags.count() <= 1); + return ret; + } + + flag_set operator&(const flag_set& val) const + { + flag_set ret(*this); + ret.flags &= val.flags; + + return ret; + } + + // The resulting bitset contains at least 1 bit. + flag_set operator|(const T& val) const + { + flag_set ret(*this); + ret |= val; + + assert(ret.flags.count() >= 1); + return ret; + } + + flag_set operator|(const flag_set& val) const + { + flag_set ret(*this); + ret.flags |= val.flags; + + return ret; + } + + flag_set operator~() const + { + flag_set cp(*this); + cp.flags.flip(); + + return cp; + } + + // The bitset evaluates to true if any bit is set. + explicit operator bool() const { return flags.any(); } + + // Methods from std::bitset. + + bool operator==(const flag_set& o) const { return flags == o.flags; } + + std::size_t size() const { return flags.size(); } + + std::size_t count() const { return flags.count(); } + + flag_set& set() + { + flags.set(); + return *this; + } + + flag_set& reset() + { + flags.reset(); + return *this; + } + + flag_set& flip() + { + flags.flip(); + return *this; + } + + flag_set& set(const T& val, bool value = true) + { + flags.set(static_cast(val), value); + return *this; + } + + flag_set& reset(const T& val) + { + flags.reset(static_cast(val)); + return *this; + } + + flag_set& flip(const T& val) + { + flags.flip(static_cast(val)); + return *this; + } + + constexpr bool operator[](const T& val) const { return flags[static_cast(val)]; } + + std::string to_string() const { return flags.to_string(); } + + // Operator for outputting to std::ostream. + friend std::ostream& operator<<(std::ostream& stream, const flag_set& self) + { + return stream << self.flags; + } + +private: + using u_type = std::underlying_type_t; + + // _ is last value sentinel and must be present in enum T. + std::bitset(T::_)> flags; +}; + +template +struct is_enum_that_contains_sentinel : std::false_type +{ +}; + +template +struct is_enum_that_contains_sentinel(T::_))> : std::is_enum +{ +}; + +// Operator that combines two enumeration values into a flag_set only if the +// enumeration contains the sentinel `_`. +template +std::enable_if_t::value, flag_set> operator|(const T& lhs, + const T& rhs) +{ + flag_set fs; + fs |= lhs; + fs |= rhs; + + return fs; +} diff --git a/OptiScaler/inputs/FSR3_Dx12.cpp b/OptiScaler/inputs/FSR3_Dx12.cpp index 56b7b2c3..7983df22 100644 --- a/OptiScaler/inputs/FSR3_Dx12.cpp +++ b/OptiScaler/inputs/FSR3_Dx12.cpp @@ -702,7 +702,8 @@ void HookFSR3ExeInputs() (PFN_ffxFsr3UpscalerContextCreate) scanner::GetAddress(exeNameV, createPattern, 0); // RDR1 have duplicate methods and first found one is not used - if (o_ffxFsr3UpscalerContextCreate_Pattern_Dx12 != nullptr && State::Instance().gameQuirk == RDR1) + if (o_ffxFsr3UpscalerContextCreate_Pattern_Dx12 != nullptr && + State::Instance().gameQuirks & GameQuirk::SkipFsr3Method) o_ffxFsr3UpscalerContextCreate_Pattern_Dx12 = (PFN_ffxFsr3UpscalerContextCreate) scanner::GetAddress( exeNameV, createPattern, 0, (size_t) o_ffxFsr3UpscalerContextCreate_Pattern_Dx12 + 2); @@ -716,7 +717,8 @@ void HookFSR3ExeInputs() "? ? ? 48 83 C1 18 48 ? ? ? ? 48 ? ? ? ? E8 ? ? ? ? 44 8B 83"); // RDR1 have duplicate methods and first found one is not used - if (State::Instance().gameQuirk == RDR1 && o_ffxFsr3UpscalerContextCreate_Pattern_Dx12 != nullptr) + if (State::Instance().gameQuirks & GameQuirk::SkipFsr3Method && + o_ffxFsr3UpscalerContextCreate_Pattern_Dx12 != nullptr) o_ffxFsr3UpscalerContextDestroy_Pattern_Dx12 = (PFN_ffxFsr3UpscalerContextDestroy) scanner::GetAddress( exeNameV, destroyPattern, 0, (size_t) o_ffxFsr3UpscalerContextCreate_Pattern_Dx12); else @@ -735,7 +737,8 @@ void HookFSR3ExeInputs() "? ? ? 77 15 48 83 B9 ? ? ? ? ? 75 06 B8 ? ? ? ? C3"); // RDR1 have duplicate methods and first found one is not used - if (State::Instance().gameQuirk == RDR1 && o_ffxFsr3UpscalerContextCreate_Pattern_Dx12 != nullptr) + if (State::Instance().gameQuirks & GameQuirk::SkipFsr3Method && + o_ffxFsr3UpscalerContextCreate_Pattern_Dx12 != nullptr) o_ffxFsr3UpscalerContextDispatch_Pattern_Dx12 = (PFN_ffxFsr3UpscalerContextDispatch) scanner::GetAddress( exeNameV, dispatchPattern, 0, (size_t) o_ffxFsr3UpscalerContextCreate_Pattern_Dx12); else diff --git a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp index e6f07692..e9f67eba 100644 --- a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp +++ b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp @@ -1299,9 +1299,9 @@ NVSDK_NGX_API NVSDK_NGX_Result NVSDK_NGX_D3D12_EvaluateFeature(ID3D12GraphicsCom dc = nullptr; - if (State::Instance().gameQuirk == SplitFiction) + if (State::Instance().gameQuirks & GameQuirk::FastFeatureReset) { - LOG_DEBUG("sleeping before reset of current feature for 100ms (Split Fiction)"); + LOG_DEBUG("sleeping before reset of current feature for 100ms (Fast Feature Reset)"); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } else