diff --git a/src/Hooks.cpp b/src/Hooks.cpp index ec3a3f692f..a384324f45 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -176,7 +176,15 @@ struct IDXGISwapChain_Present { State::GetSingleton()->Reset(); Menu::GetSingleton()->DrawOverlay(); - Streamline::GetSingleton()->Present(); + + auto streamline = Streamline::GetSingleton(); + streamline->Present(); + + if (streamline->settings.frameGenerationMode == sl::DLSSGMode::eOn) { + SyncInterval = 0; + Flags = DXGI_PRESENT_ALLOW_TEARING; + } + auto retval = func(This, SyncInterval, Flags); TracyD3D11Collect(State::GetSingleton()->tracyCtx); return retval; @@ -268,7 +276,7 @@ HRESULT WINAPI hk_D3D11CreateDeviceAndSwapChain( [[maybe_unused]] const D3D_FEATURE_LEVEL* pFeatureLevels, [[maybe_unused]] UINT FeatureLevels, UINT SDKVersion, - const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc, + DXGI_SWAP_CHAIN_DESC* pSwapChainDesc, IDXGISwapChain** ppSwapChain, ID3D11Device** ppDevice, D3D_FEATURE_LEVEL* pFeatureLevel, diff --git a/src/State.cpp b/src/State.cpp index 85431e88ed..c79ff3af95 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -284,6 +284,20 @@ void State::Load(ConfigMode a_configMode, bool a_allowReload) logger::warn("Missing settings for Upscaling, using default."); } + auto streamline = Streamline::GetSingleton(); + auto& streamlineJson = settings[streamline->GetShortName()]; + if (streamlineJson.is_object()) { + logger::info("Loading Streamline settings"); + try { + streamline->LoadSettings(streamlineJson); + } catch (...) { + logger::warn("Invalid settings for Streamline, using default."); + streamline->RestoreDefaultSettings(); + } + } else { + logger::warn("Missing settings for Streamline, using default."); + } + for (auto* feature : Feature::GetFeatureList()) { try { const std::string featureName = feature->GetShortName(); @@ -364,6 +378,10 @@ void State::Save(ConfigMode a_configMode) auto& upscalingJson = settings[upscaling->GetShortName()]; upscaling->SaveSettings(upscalingJson); + auto streamline = Streamline::GetSingleton(); + auto& streamlineJson = settings[streamline->GetShortName()]; + streamline->SaveSettings(streamlineJson); + json originalShaders; for (int classIndex = 0; classIndex < RE::BSShader::Type::Total - 1; ++classIndex) { originalShaders[magic_enum::enum_name((RE::BSShader::Type)(classIndex + 1))] = enabledClasses[classIndex]; diff --git a/src/Streamline.cpp b/src/Streamline.cpp index edce5c68a7..e6acebd88a 100644 --- a/src/Streamline.cpp +++ b/src/Streamline.cpp @@ -1,12 +1,18 @@ #include "Streamline.h" #include +#include #include "Hooks.h" #include "Util.h" #include "Upscaling.h" +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( + Streamline::Settings, + frameLimitMode, + frameGenerationMode); + void LoggingCallback(sl::LogType type, const char* msg) { switch (type) { @@ -30,11 +36,12 @@ void Streamline::DrawSettings() ImGui::Text("Frame Generation can only be enabled or disabled in the mod manager, it can only be temporarily toggled in-game"); if (ImGui::TreeNodeEx("NVIDIA DLSS Frame Generation", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::Text("Requires an NVIDIA GeForce RTX 40 Series or newer"); + ImGui::Text("Requires an NVIDIA GeForce RTX 40/50 Series or newer"); if (featureDLSSG) { const char* frameGenerationModes[] = { "Off", "On" }; - ImGui::SliderInt("Frame Generation", (int*)&frameGenerationMode, 0, 1, std::format("{}", frameGenerationModes[(uint)frameGenerationMode]).c_str()); - frameGenerationMode = (sl::DLSSGMode)std::min(2u, (uint)frameGenerationMode); + ImGui::SliderInt("Frame Limit (Refresh Rate)", (int*)&settings.frameLimitMode, 0, 1, std::format("{}", frameGenerationModes[(uint)settings.frameLimitMode]).c_str()); + ImGui::SliderInt("Frame Generation", (int*)&settings.frameGenerationMode, 0, 1, std::format("{}", frameGenerationModes[(uint)settings.frameGenerationMode]).c_str()); + settings.frameGenerationMode = (sl::DLSSGMode)std::min(2u, (uint)settings.frameGenerationMode); } else { } ImGui::TreePop(); @@ -109,7 +116,7 @@ void Streamline::Initialize() } } -void Streamline::PostDevice() +void Streamline::PostDevice(DXGI_SWAP_CHAIN_DESC* a_swapChainDesc) { // Hook up all of the feature functions using the sl function slGetFeatureFunction @@ -130,6 +137,18 @@ void Streamline::PostDevice() slGetFeatureFunction(sl::kFeatureReflex, "slReflexSleep", (void*&)slReflexSleep); slGetFeatureFunction(sl::kFeatureReflex, "slReflexSetOptions", (void*&)slReflexSetOptions); } + + MONITORINFOEX monitorInfo = {}; + monitorInfo.cbSize = sizeof(MONITORINFOEX); + + HMONITOR monitor = MonitorFromWindow(a_swapChainDesc->OutputWindow, MONITOR_DEFAULTTONEAREST); + GetMonitorInfo(monitor, &monitorInfo); + + DEVMODE devMode = {}; + devMode.dmSize = sizeof(DEVMODE); + + EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode); + refreshRate = devMode.dmDisplayFrequency; } HRESULT Streamline::CreateDXGIFactory(REFIID riid, void** ppFactory) @@ -149,7 +168,7 @@ HRESULT Streamline::CreateDeviceAndSwapChain(IDXGIAdapter* pAdapter, const D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, - const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc, + DXGI_SWAP_CHAIN_DESC* pSwapChainDesc, IDXGISwapChain** ppSwapChain, ID3D11Device** ppDevice, D3D_FEATURE_LEVEL* pFeatureLevel, @@ -211,6 +230,8 @@ HRESULT Streamline::CreateDeviceAndSwapChain(IDXGIAdapter* pAdapter, if (featureDLSSG && !REL::Module::IsVR()) { logger::info("[Streamline] Proxying D3D11CreateDeviceAndSwapChain to add D3D12 swapchain"); + pSwapChainDesc->Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + auto slD3D11CreateDeviceAndSwapChain = reinterpret_cast(GetProcAddress(interposer, "D3D11CreateDeviceAndSwapChain")); hr = slD3D11CreateDeviceAndSwapChain( @@ -244,7 +265,7 @@ HRESULT Streamline::CreateDeviceAndSwapChain(IDXGIAdapter* pAdapter, slSetD3DDevice(*ppDevice); } - PostDevice(); + PostDevice(pSwapChainDesc); return hr; } @@ -321,7 +342,7 @@ void Streamline::SetupResources() void Streamline::CopyResourcesToSharedBuffers() { - if (!(featureDLSSG && !REL::Module::IsVR()) || frameGenerationMode == sl::DLSSGMode::eOff) + if (!(featureDLSSG && !REL::Module::IsVR()) || settings.frameGenerationMode == sl::DLSSGMode::eOff) return; auto& context = State::GetSingleton()->context; @@ -384,13 +405,13 @@ void Streamline::Present() UpdateConstants(); - static auto currentFrameGenerationMode = frameGenerationMode; + static auto currentFrameGenerationMode = settings.frameGenerationMode; - if (currentFrameGenerationMode != frameGenerationMode) { - currentFrameGenerationMode = frameGenerationMode; + if (currentFrameGenerationMode != settings.frameGenerationMode) { + currentFrameGenerationMode = settings.frameGenerationMode; sl::DLSSGOptions options{}; - options.mode = frameGenerationMode; + options.mode = settings.frameGenerationMode; options.flags = sl::DLSSGFlags::eRetainResourcesWhenOff; if (SL_FAILED(result, slDLSSGSetOptions(viewport, options))) { @@ -571,10 +592,53 @@ void Streamline::UpdateConstants() } } +void Streamline::SaveSettings(json& o_json) +{ + o_json = settings; +} + +void Streamline::LoadSettings(json& o_json) +{ + settings = o_json; +} + +void Streamline::RestoreDefaultSettings() +{ + settings = {}; +} + void Streamline::DestroyDLSSResources() { sl::DLSSOptions dlssOptions{}; dlssOptions.mode = sl::DLSSMode::eOff; slDLSSSetOptions(viewport, dlssOptions); slFreeResources(sl::kFeatureDLSS, viewport); -} \ No newline at end of file +} + +static void TimerSleepQPC(int64_t targetQPC) +{ + LARGE_INTEGER currentQPC; + do { + QueryPerformanceCounter(¤tQPC); + } while (currentQPC.QuadPart < targetQPC); +} + +void Streamline::BeginFrame() +{ + if (featureDLSSG && settings.frameGenerationMode == sl::DLSSGMode::eOn && settings.frameLimitMode) { + LARGE_INTEGER qpf; + QueryPerformanceFrequency(&qpf); + + double bestRefreshRate = refreshRate - (refreshRate * refreshRate) / 3600.0; + int64_t targetFrameTicks = int64_t(double(qpf.QuadPart) / (bestRefreshRate * 0.5)); + + static LARGE_INTEGER lastFrame = {}; + LARGE_INTEGER timeNow; + QueryPerformanceCounter(&timeNow); + int64_t delta = timeNow.QuadPart - lastFrame.QuadPart; + if (delta < targetFrameTicks) { + TimerSleepQPC(lastFrame.QuadPart + targetFrameTicks); + } + QueryPerformanceCounter(&lastFrame); + } +} diff --git a/src/Streamline.h b/src/Streamline.h index c8ef713609..481b42acf2 100644 --- a/src/Streamline.h +++ b/src/Streamline.h @@ -29,10 +29,18 @@ class Streamline bool featureDLSSG = false; bool featureReflex = false; + double refreshRate = 60.0; + sl::ViewportHandle viewport{ 0 }; sl::FrameToken* frameToken; - sl::DLSSGMode frameGenerationMode = sl::DLSSGMode::eOn; + struct Settings + { + sl::DLSSGMode frameGenerationMode = sl::DLSSGMode::eOn; + int frameLimitMode = 0; + }; + + Settings settings{}; HMODULE interposer = NULL; @@ -79,7 +87,7 @@ class Streamline void LoadInterposer(); void Initialize(); - void PostDevice(); + void PostDevice(DXGI_SWAP_CHAIN_DESC* a_swapChainDesc); HRESULT CreateDXGIFactory(REFIID riid, void** ppFactory); @@ -90,7 +98,7 @@ class Streamline const D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, - const DXGI_SWAP_CHAIN_DESC* pSwapChainDesc, + DXGI_SWAP_CHAIN_DESC* pSwapChainDesc, IDXGISwapChain** ppSwapChain, ID3D11Device** ppDevice, D3D_FEATURE_LEVEL* pFeatureLevel, @@ -104,8 +112,25 @@ class Streamline void Upscale(Texture2D* a_color, Texture2D* a_alphaMask, sl::DLSSPreset a_preset); void UpdateConstants(); + void SaveSettings(json& o_json); + void LoadSettings(json& o_json); + + void RestoreDefaultSettings(); + void DestroyDLSSResources(); + void BeginFrame(); + + struct Main_Update_Start + { + static void thunk(INT64 a_unk) + { + GetSingleton()->BeginFrame(); + func(a_unk); + } + static inline REL::Relocation func; + }; + struct Main_RenderWorld { static void thunk(bool a1) @@ -132,6 +157,7 @@ class Streamline static void InstallHooks() { + stl::write_thunk_call(REL::RelocationID(35565, 36564).address() + REL::Relocate(0x1E, 0x3E, 0x33)); stl::write_thunk_call(REL::RelocationID(35560, 36559).address() + REL::Relocate(0x831, 0x841, 0x791)); stl::write_thunk_call(REL::RelocationID(79947, 82084).address() + REL::Relocate(0x7E, 0x83, 0x97)); }