Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
18 changes: 18 additions & 0 deletions src/State.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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];
Expand Down
88 changes: 76 additions & 12 deletions src/Streamline.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
#include "Streamline.h"

#include <dxgi.h>
#include <dxgi1_3.h>

#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) {
Expand All @@ -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();
Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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<decltype(&D3D11CreateDeviceAndSwapChain)>(GetProcAddress(interposer, "D3D11CreateDeviceAndSwapChain"));

hr = slD3D11CreateDeviceAndSwapChain(
Expand Down Expand Up @@ -244,7 +265,7 @@ HRESULT Streamline::CreateDeviceAndSwapChain(IDXGIAdapter* pAdapter,
slSetD3DDevice(*ppDevice);
}

PostDevice();
PostDevice(pSwapChainDesc);

return hr;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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);
}
}

static void TimerSleepQPC(int64_t targetQPC)
{
LARGE_INTEGER currentQPC;
do {
QueryPerformanceCounter(&currentQPC);
} 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);
}
}
32 changes: 29 additions & 3 deletions src/Streamline.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);

Expand All @@ -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,
Expand All @@ -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<decltype(thunk)> func;
};

struct Main_RenderWorld
{
static void thunk(bool a1)
Expand All @@ -132,6 +157,7 @@ class Streamline

static void InstallHooks()
{
stl::write_thunk_call<Main_Update_Start>(REL::RelocationID(35565, 36564).address() + REL::Relocate(0x1E, 0x3E, 0x33));
stl::write_thunk_call<Main_RenderWorld>(REL::RelocationID(35560, 36559).address() + REL::Relocate(0x831, 0x841, 0x791));
stl::write_thunk_call<MenuManagerDrawInterfaceStartHook>(REL::RelocationID(79947, 82084).address() + REL::Relocate(0x7E, 0x83, 0x97));
}
Expand Down