Fix slDLSSGSetOptions hooks when using steam overlay; force DLSSG Auto to always mean On; marker fix for Roadcraft

This commit is contained in:
FakeMichau
2025-08-22 13:57:45 +02:00
parent d22233d559
commit 5b79f77d5d
9 changed files with 197 additions and 22 deletions
+1
View File
@@ -147,6 +147,7 @@ DEFINE_NAME_VECTORS(slInterposer, "sl.interposer");
DEFINE_NAME_VECTORS(slDlss, "sl.dlss");
DEFINE_NAME_VECTORS(slDlssg, "sl.dlss_g");
DEFINE_NAME_VECTORS(slReflex, "sl.reflex");
DEFINE_NAME_VECTORS(slPcl, "sl.pcl");
DEFINE_NAME_VECTORS(slCommon, "sl.common");
DEFINE_NAME_VECTORS(xess, "libxess");
+2 -2
View File
@@ -259,7 +259,7 @@ copy "$(SolutionDir)external\directx_agility_sdk\LICENSE.txt" $(SolutionDir)x64\
copy NUL "$(SolutionDir)x64\Release\a\!! EXTRACT ALL FILES TO GAME FOLDER !!" /Y</Command>
</PostBuildEvent>
<PreBuildEvent>
<Command>powershell -Command "$date = Get-Date -Format 'yyyyMMdd_HHmmss'; Set-Content -Path '$(ProjectDir)resource_build_date.h' -Value ('#define VER_BUILD_DATE ""' + $date + '"""')</Command>
<Command>powershell -Command "Start-Sleep -Seconds 2; $date = Get-Date -Format 'yyyyMMdd_HHmmss'; Set-Content -Path '$(ProjectDir)resource_build_date.h' -Value ('#define VER_BUILD_DATE ""' + $date + '"""')</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseDebug|x64'">
@@ -289,7 +289,7 @@ copy NUL "$(SolutionDir)x64\Release\a\!! EXTRACT ALL FILES TO GAME FOLDER !!" /Y
<Command>powershell -Command "$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'; $fileName = '$(OutDir)OptiScaler_' + $timestamp + '.7z'; $backupFileName = '$(OutDir)OptiScaler_' + $timestamp + '_Backup.7z'; &amp; '7z.exe' a -t7z $fileName '$(TargetPath)'; &amp; '7z.exe' a -t7z $backupFileName '$(OutDir)$(TargetName).*'"</Command>
</PostBuildEvent>
<PreBuildEvent>
<Command>powershell -Command "$date = Get-Date -Format 'yyyyMMdd_HHmmss'; Set-Content -Path '$(ProjectDir)resource_build_date.h' -Value ('#define VER_BUILD_DATE ""' + $date + '"""')</Command>
<Command>powershell -Command "Start-Sleep -Seconds 2; $date = Get-Date -Format 'yyyyMMdd_HHmmss'; Set-Content -Path '$(ProjectDir)resource_build_date.h' -Value ('#define VER_BUILD_DATE ""' + $date + '"""')</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
+12 -3
View File
@@ -18,15 +18,14 @@ typedef BOOL (*PFN_VerQueryValueW)(LPCVOID pBlock, LPCWSTR lpSubBlock, LPVOID* l
/// <returns>Caller module filename</returns>
std::string Util::WhoIsTheCaller(void* returnAddress)
{
HMODULE hModule = NULL;
char callerPath[MAX_PATH] = { 0 };
// Get the return address from the current function call.
// void* returnAddress = _ReturnAddress();
// Get the base address of the module containing the return address.
if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCSTR) returnAddress, &hModule))
if (HMODULE hModule = GetCallerModule(returnAddress); hModule != nullptr)
{
// Get the full path of the calling module.
GetModuleFileNameA(hModule, callerPath, sizeof(callerPath));
@@ -38,6 +37,16 @@ std::string Util::WhoIsTheCaller(void* returnAddress)
return "";
}
HMODULE Util::GetCallerModule(void* returnAddress)
{
HMODULE hModule = NULL;
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCSTR) returnAddress, &hModule);
return hModule;
}
std::wstring Util::GetWindowTitle(HWND hwnd)
{
const int maxLength = 512;
+1
View File
@@ -43,6 +43,7 @@ std::wstring GetWindowTitle(HWND hwnd);
std::optional<std::filesystem::path> FindFilePath(const std::filesystem::path& startDir,
const std::filesystem::path fileName);
std::string WhoIsTheCaller(void* returnAddress);
HMODULE GetCallerModule(void* returnAddress);
MonitorInfo GetMonitorInfoForWindow(HWND hwnd);
MonitorInfo GetMonitorInfoForOutput(IDXGIOutput* pOutput);
+8
View File
@@ -818,6 +818,14 @@ static void CheckWorkingMode()
StreamlineHooks::hookReflex(slReflex);
}
HMODULE slPcl = nullptr;
slPcl = GetDllNameWModule(&slPclNamesW);
if (slPcl != nullptr)
{
LOG_DEBUG("sl.pcl.dll already in memory");
StreamlineHooks::hookPcl(slPcl);
}
HMODULE slCommon = nullptr;
slCommon = GetDllNameWModule(&slCommonNamesW);
if (slCommon != nullptr)
+35
View File
@@ -210,6 +210,23 @@ class KernelHooks
return reflexModule;
}
// sl.pcl.dll
if (CheckDllName(&lcaseLibName, &slPclNames))
{
auto pclModule = KernelBaseProxy::LoadLibraryExA_()(lpLibFullPath, NULL, 0);
if (pclModule != nullptr)
{
StreamlineHooks::hookPcl(pclModule);
}
else
{
LOG_ERROR("Trying to load dll: {}", lcaseLibName);
}
return pclModule;
}
// sl.common.dll
if (CheckDllName(&lcaseLibName, &slCommonNames))
{
@@ -683,6 +700,24 @@ class KernelHooks
return reflexModule;
}
// sl.pcl.dll
if (CheckDllNameW(&lcaseLibName, &slPclNamesW) ||
(lcaseLibName.contains(L"/versions/") && lcaseLibName.contains(L"/sl_pcl_")))
{
auto pclModule = KernelBaseProxy::LoadLibraryExW_()(lpLibFullPath, NULL, 0);
if (pclModule != nullptr)
{
StreamlineHooks::hookPcl(pclModule);
}
else
{
LOG_ERROR("Trying to load dll as sl.pcl: {}", lcaseLibNameA);
}
return pclModule;
}
// sl.common.dll
if (CheckDllNameW(&lcaseLibName, &slCommonNamesW) ||
(lcaseLibName.contains(L"/versions/") && lcaseLibName.contains(L"/sl_common_")))
+122 -17
View File
@@ -24,6 +24,7 @@ decltype(&slAllocateResources) StreamlineHooks::o_slAllocateResources = nullptr;
decltype(&slSetConstants) StreamlineHooks::o_slSetConstants = nullptr;
decltype(&slGetNativeInterface) StreamlineHooks::o_slGetNativeInterface = nullptr;
decltype(&slSetD3DDevice) StreamlineHooks::o_slSetD3DDevice = nullptr;
decltype(&slGetNewFrameToken) StreamlineHooks::o_slGetNewFrameToken = nullptr;
decltype(&sl1::slInit) StreamlineHooks::o_slInit_sl1 = nullptr;
@@ -46,6 +47,10 @@ StreamlineHooks::PFN_slOnPluginLoad StreamlineHooks::o_reflex_slOnPluginLoad = n
decltype(&slReflexSetOptions) StreamlineHooks::o_slReflexSetOptions = nullptr;
sl::ReflexMode StreamlineHooks::reflexGamesLastMode = sl::ReflexMode::eOff;
// PCL
StreamlineHooks::PFN_slGetPluginFunction StreamlineHooks::o_pcl_slGetPluginFunction = nullptr;
decltype(&slPCLSetMarker) StreamlineHooks::o_slPCLSetMarker = nullptr;
// Common
StreamlineHooks::PFN_slGetPluginFunction StreamlineHooks::o_common_slGetPluginFunction = nullptr;
StreamlineHooks::PFN_slOnPluginLoad StreamlineHooks::o_common_slOnPluginLoad = nullptr;
@@ -460,27 +465,27 @@ bool StreamlineHooks::hkcommon_slOnPluginLoad(void* params, const char* loaderJS
sl::Result StreamlineHooks::hkslDLSSGSetOptions(const sl::ViewportHandle& viewport, const sl::DLSSGOptions& options)
{
if (State::Instance().api != API::Vulkan)
return o_slDLSSGSetOptions(viewport, options);
// Make DLSSG auto always mean On
sl::DLSSGOptions newOptions = options;
newOptions.mode = newOptions.mode == sl::DLSSGMode::eOff ? sl::DLSSGMode::eOff : sl::DLSSGMode::eOn;
// Only matters for Vulkan, DX doesn't use this delay
if (options.mode != sl::DLSSGMode::eOff && !MenuOverlayBase::IsVisible())
State::Instance().delayMenuRenderBy = 10;
if (MenuOverlayBase::IsVisible())
if (State::Instance().api == API::Vulkan)
{
sl::DLSSGOptions newOptions = options;
newOptions.mode = sl::DLSSGMode::eOff;
newOptions.flags |= sl::DLSSGFlags::eRetainResourcesWhenOff;
// Only matters for Vulkan, DX doesn't use this delay
if (options.mode != sl::DLSSGMode::eOff && !MenuOverlayBase::IsVisible())
State::Instance().delayMenuRenderBy = 10;
LOG_TRACE("DLSSG Modified Mode: {}", (uint32_t) newOptions.mode);
ReflexHooks::setDlssgDetectedState(false);
return o_slDLSSGSetOptions(viewport, newOptions);
if (MenuOverlayBase::IsVisible())
{
newOptions.mode = sl::DLSSGMode::eOff;
newOptions.flags |= sl::DLSSGFlags::eRetainResourcesWhenOff;
ReflexHooks::setDlssgDetectedState(false);
}
}
// Can't tell if eAuto means enabled or disabled
ReflexHooks::setDlssgDetectedState(options.mode == sl::DLSSGMode::eOn);
return o_slDLSSGSetOptions(viewport, options);
LOG_TRACE("DLSSG Modified Mode: {}", magic_enum::enum_name(newOptions.mode));
return o_slDLSSGSetOptions(viewport, newOptions);
}
bool StreamlineHooks::hkreflex_slOnPluginLoad(void* params, const char* loaderJSON, const char** pluginJSON)
@@ -528,8 +533,18 @@ void* StreamlineHooks::hkdlssg_slGetPluginFunction(const char* functionName)
return &hkdlssg_slOnPluginLoad;
}
if (strcmp(functionName, "slDLSSGSetOptions") == 0 && State::Instance().api == API::Vulkan)
if (strcmp(functionName, "slDLSSGSetOptions") == 0)
{
// Give steam overlay the original as it seems to be hooking it
auto steamOverlay = KernelBaseProxy::GetModuleHandleA_()("gameoverlayrenderer64.dll");
if (steamOverlay != nullptr)
{
if (HMODULE callerModule = Util::GetCallerModule(_ReturnAddress()); callerModule == steamOverlay)
{
return o_dlssg_slGetPluginFunction(functionName);
}
}
o_slDLSSGSetOptions = (decltype(&slDLSSGSetOptions)) o_dlssg_slGetPluginFunction(functionName);
return &hkslDLSSGSetOptions;
}
@@ -582,6 +597,45 @@ void* StreamlineHooks::hkreflex_slGetPluginFunction(const char* functionName)
return o_reflex_slGetPluginFunction(functionName);
}
sl::Result StreamlineHooks::hkslPCLSetMarker(sl::PCLMarker marker, const sl::FrameToken& frame)
{
// HACK for broken games
static uint64_t last_simulation_end_id = 0;
if (marker == sl::PCLMarker::eSimulationEnd)
{
last_simulation_end_id = frame;
}
if (marker == sl::PCLMarker::eSimulationStart && last_simulation_end_id >= frame && o_slGetNewFrameToken)
{
const uint64_t correction_offset = last_simulation_end_id - frame + 1;
uint32_t newFrameId = frame + correction_offset;
sl::FrameToken* newFramePointer {};
auto result = o_slGetNewFrameToken(newFramePointer, &newFrameId);
LOG_WARN("Simulation start marker sent after end marker, offset: {}", correction_offset);
result = o_slPCLSetMarker(marker, *newFramePointer);
return result;
}
return o_slPCLSetMarker(marker, frame);
}
void* StreamlineHooks::hkpcl_slGetPluginFunction(const char* functionName)
{
LOG_DEBUG("{}", functionName);
if (strcmp(functionName, "slPCLSetMarker") == 0)
{
o_slPCLSetMarker = (decltype(&slPCLSetMarker)) o_pcl_slGetPluginFunction(functionName);
return &hkslPCLSetMarker;
}
return o_pcl_slGetPluginFunction(functionName);
}
bool StreamlineHooks::hk_setVoid(void* self, const char* key, void** value)
{
// LOG_DEBUG("{}", key);
@@ -762,6 +816,8 @@ void StreamlineHooks::hookInterposer(HMODULE slInterposer)
KernelBaseProxy::GetProcAddress_()(slInterposer, "slGetNativeInterface"));
o_slSetD3DDevice = reinterpret_cast<decltype(&slSetD3DDevice)>(
KernelBaseProxy::GetProcAddress_()(slInterposer, "slSetD3DDevice"));
o_slGetNewFrameToken = reinterpret_cast<decltype(&slGetNewFrameToken)>(
KernelBaseProxy::GetProcAddress_()(slInterposer, "slGetNewFrameToken")); // Not hooked
if (o_slInit != nullptr)
{
@@ -960,6 +1016,55 @@ void StreamlineHooks::hookReflex(HMODULE slReflex)
}
}
// SL PCL
void StreamlineHooks::unhookPcl()
{
LOG_FUNC();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
if (o_pcl_slGetPluginFunction)
{
DetourDetach(&(PVOID&) o_pcl_slGetPluginFunction, hkpcl_slGetPluginFunction);
o_pcl_slGetPluginFunction = nullptr;
}
DetourTransactionCommit();
}
void StreamlineHooks::hookPcl(HMODULE slPcl)
{
LOG_FUNC();
if (!slPcl)
{
LOG_WARN("Pcl module in NULL");
return;
}
if (!(State::Instance().gameQuirks & GameQuirk::FixSlSimulationMarkers))
return;
if (o_pcl_slGetPluginFunction)
unhookPcl();
o_pcl_slGetPluginFunction =
reinterpret_cast<PFN_slGetPluginFunction>(KernelBaseProxy::GetProcAddress_()(slPcl, "slGetPluginFunction"));
if (o_pcl_slGetPluginFunction != nullptr)
{
LOG_TRACE("Hooking slGetPluginFunction in sl.pcl");
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&) o_pcl_slGetPluginFunction, hkpcl_slGetPluginFunction);
DetourTransactionCommit();
}
}
// SL COMMON
void StreamlineHooks::unhookCommon()
+12
View File
@@ -33,6 +33,9 @@ class StreamlineHooks
static void unhookReflex();
static void hookReflex(HMODULE slReflex);
static void unhookPcl();
static void hookPcl(HMODULE slPcl);
static void unhookCommon();
static void hookCommon(HMODULE slCommon);
@@ -49,6 +52,7 @@ class StreamlineHooks
static decltype(&slSetConstants) o_slSetConstants;
static decltype(&slGetNativeInterface) o_slGetNativeInterface;
static decltype(&slSetD3DDevice) o_slSetD3DDevice;
static decltype(&slGetNewFrameToken) o_slGetNewFrameToken;
static decltype(&sl1::slInit) o_slInit_sl1;
@@ -86,6 +90,7 @@ class StreamlineHooks
static PFN_slGetPluginFunction o_dlssg_slGetPluginFunction;
static PFN_slOnPluginLoad o_dlssg_slOnPluginLoad;
static decltype(&slDLSSGSetOptions) o_slDLSSGSetOptions;
static decltype(&slDLSSGGetState) o_slDLSSGGetState;
static bool hkdlssg_slOnPluginLoad(void* params, const char* loaderJSON, const char** pluginJSON);
static sl::Result hkslSetConstants(const sl::Constants& values, const sl::FrameToken& frame,
@@ -105,6 +110,13 @@ class StreamlineHooks
static bool hkreflex_slSetConstants_sl1(const void* data, uint32_t frameIndex, uint32_t id);
static void* hkreflex_slGetPluginFunction(const char* functionName);
// PCL
static PFN_slGetPluginFunction o_pcl_slGetPluginFunction;
static decltype(&slPCLSetMarker) o_slPCLSetMarker;
static void* hkpcl_slGetPluginFunction(const char* functionName);
static sl::Result hkslPCLSetMarker(sl::PCLMarker marker, const sl::FrameToken& frame);
// Common
static PFN_slGetPluginFunction o_common_slGetPluginFunction;
static PFN_slOnPluginLoad o_common_slOnPluginLoad;
+4
View File
@@ -32,6 +32,7 @@ enum class GameQuirk : uint64_t
VulkanDLSSBarrierFixup,
ForceUnrealEngine,
NoFSRFGFirstSwapchain,
FixSlSimulationMarkers,
// Don't forget to add the new entry to printQuirks
_
};
@@ -199,6 +200,7 @@ static const QuirkEntry quirkTable[] = {
QUIRK_ENTRY("minecraft.windows.exe", GameQuirk::KernelBaseHooks),
QUIRK_ENTRY("prey.exe", GameQuirk::DontUseNTShared, GameQuirk::DisableOptiXessPipelineCreation,
GameQuirk::DisableDxgiSpoofing),
QUIRK_ENTRY("roadcraft - retail.exe", GameQuirk::FixSlSimulationMarkers),
// VULKAN
// ------
@@ -275,6 +277,8 @@ static void printQuirks(flag_set<GameQuirk>& quirks)
spdlog::info("Quirk: Skipping upscaling for first 10 frames");
if (quirks & GameQuirk::NoFSRFGFirstSwapchain)
spdlog::info("Quirk: Skip turning the first swapchain created into an FSR swapchain");
if (quirks & GameQuirk::FixSlSimulationMarkers)
spdlog::info("Quirk: Correct simulation start marker's frame id");
return;
}