Bind active Streamline plugins and correct graphics resource handling

Resolve DLSSG, Reflex and PCL functions after device selection so driver-selected plugins supply initialized entry points. Preserve the depth-plane SRV format and check the Vulkan framebuffer handle during teardown.

Vulkan framebuffer guard incorporates y4my4my4m work (7b7220bb / 7c3b65dc). Validation: Release x64 build and installed-runtime Streamline active-plugin smoke test; Jedi depth-view diagnosis and NR gameplay retest recorded separately.
This commit is contained in:
wilsjo2 committed 2026-09-13 05:40:13 +10:00
1 parent 02e8d8b92b
commit 7543d1437c
4 files changed
+176 -8

No files matched your search

+1 -1
View File
@@ -480,7 +480,7 @@ void MenuOverlayVk::DestroyVulkanObjects(bool shutdown)
fd->BackbufferView = VK_NULL_HANDLE;
}
if (fd->BackbufferView != VK_NULL_HANDLE)
if (fd->Framebuffer != VK_NULL_HANDLE)
{
vkDestroyFramebuffer(_ImVulkan_Info.Device, fd->Framebuffer, VK_NULL_HANDLE);
fd->Framebuffer = VK_NULL_HANDLE;
+46 -6
View File
@@ -285,6 +285,48 @@ class StreamlineProxy
return _slVersion;
}
template <typename T>
static bool ResolveActiveFeature(sl::Feature feature, const char* name, T& target, HMODULE& module,
bool required = true)
{
void* function = nullptr;
auto result = _slGetFeatureFunction(feature, name, function);
target = result == sl::Result::eOk ? reinterpret_cast<T>(function) : nullptr;
if (target == nullptr)
{
if (required)
LOG_ERROR("Active Streamline function {} unavailable: {}", name, (int) result);
return !required;
}
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(function), &module);
return true;
}
static sl::Result SetD3DDeviceAndBind(void* device)
{
auto result = _slSetD3DDevice(device);
if (result != sl::Result::eOk || !_isD3D12Requested)
return result;
// OTA overrides may replace the bundled DLLs. Resolve only from the plugins
// actually initialized by this interposer, after the device has been set.
bool ok = true;
auto& state = State::Instance();
ok &= ResolveActiveFeature(sl::kFeatureDLSS_G, "slDLSSGSetOptions", _slDLSSGSetOptions, state.optiSlDLSSG);
ok &= ResolveActiveFeature(sl::kFeatureDLSS_G, "slDLSSGGetState", _slDLSSGGetState, state.optiSlDLSSG);
ok &= ResolveActiveFeature(sl::kFeatureReflex, "slReflexGetState", _slReflexGetState, state.optiSlReflex);
ok &= ResolveActiveFeature(sl::kFeatureReflex, "slReflexSleep", _slReflexSleep, state.optiSlReflex);
ok &= ResolveActiveFeature(sl::kFeatureReflex, "slReflexSetOptions", _slReflexSetOptions, state.optiSlReflex);
ResolveActiveFeature(sl::kFeatureReflex, "slReflexSetCameraData", _slReflexSetCameraData, state.optiSlReflex, false);
ResolveActiveFeature(sl::kFeatureReflex, "slReflexGetPredictedCameraData", _slReflexGetPredictedCameraData, state.optiSlReflex, false);
ResolveActiveFeature(sl::kFeaturePCL, "slPCLGetState", _slPCLGetState, state.optiSlPCL, false);
ok &= ResolveActiveFeature(sl::kFeaturePCL, "slPCLSetMarker", _slPCLSetMarker, state.optiSlPCL);
ok &= ResolveActiveFeature(sl::kFeaturePCL, "slPCLSetOptions", _slPCLSetOptions, state.optiSlPCL);
LOG_INFO("Active Streamline feature binding: {}", ok);
return ok ? sl::Result::eOk : sl::Result::eErrorFeatureMissing;
}
static bool InitWithD3D12(ID3D12Device* device)
{
if (_isD3D12Inited)
@@ -358,16 +400,13 @@ class StreamlineProxy
State::DisableChecks(owner);
}
_isD3D12Requested = true;
auto initResult = StreamlineProxy::Init()(pref, sl::kSDKVersion);
State::EnableChecks(owner);
if (initResult == sl::Result::eOk)
{
State::Instance().optiSlDLSSG = StreamlineProxy::HookStreamlineDLSSG();
State::Instance().optiSlReflex = StreamlineProxy::HookStreamlineReflex();
State::Instance().optiSlPCL = StreamlineProxy::HookStreamlinePCL();
if (State::Instance().gameQuirks & GameQuirk::CreateSLOnThe2ndDevice)
{
// slSetD3DDevice moved to hkD3D12CreateDevice
@@ -375,7 +414,7 @@ class StreamlineProxy
}
else
{
auto result = _slSetD3DDevice(device);
auto result = SetD3DDeviceAndBind(device);
if (result == sl::Result::eOk)
{
auto reflexConst = sl::ReflexOptions {};
@@ -451,7 +490,7 @@ class StreamlineProxy
static PFN_slGetNativeInterface GetNativeInterface() { return _slGetNativeInterface; }
static PFN_slGetFeatureFunction GetFeatureFunction() { return _slGetFeatureFunction; }
static PFN_slGetNewFrameToken GetNewFrameToken() { return _slGetNewFrameToken; }
static PFN_slSetD3DDevice SetD3DDevice() { return _slSetD3DDevice; }
static PFN_slSetD3DDevice SetD3DDevice() { return &SetD3DDeviceAndBind; }
static PFN_CreateDxgiFactory CreateDxgiFactory() { return _slCreateDxgiFactory; }
static PFN_CreateDxgiFactory1 CreateDxgiFactory1() { return _slCreateDxgiFactory1; }
static PFN_CreateDxgiFactory2 CreateDxgiFactory2() { return _slCreateDxgiFactory2; }
@@ -475,6 +514,7 @@ class StreamlineProxy
inline static bool _isInited = false;
inline static bool _isD3D11Inited = false;
inline static bool _isD3D12Inited = false;
inline static bool _isD3D12Requested = false;
// Interposer
inline static PFN_slInit _slInit = nullptr;
+5 -1
View File
@@ -198,7 +198,11 @@ void Shader_Dx12::CreateShaderResourceView(ID3D12Device* device, ID3D12Resource*
}
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Format = TranslateTypelessFormats((format != DXGI_FORMAT_UNKNOWN) ? format : desc.Format);
const auto viewFormat = (format != DXGI_FORMAT_UNKNOWN) ? format : desc.Format;
// NR's copied depth guide already has the depth-plane SRV format. The shared
// translator maps it back to a DSV format, which removes the device on SRV creation.
srvDesc.Format = viewFormat == DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS
? viewFormat : TranslateTypelessFormats(viewFormat);
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
const UINT mipLevels = (desc.MipLevels) ? static_cast<UINT>(desc.MipLevels) : static_cast<UINT>(-1);
+124
View File
@@ -0,0 +1,124 @@
// Installed-runtime regression for Streamline's active-plugin binding contract.
// Build: cl /std:c++20 /EHsc /I external/streamline tests/streamline_active_plugin_smoke.cpp /link d3d12.lib dxgi.lib
// Run: streamline_active_plugin_smoke.exe <directory containing sl.interposer.dll> <game bin directory>
// Runs without a game. It checks startup/plugin identity, not presentation or frame generation.
#include <windows.h>
#include <d3d12.h>
#include <sl.h>
#include <sl_dlss_g.h>
#include <sl_reflex.h>
#include <sl_pcl.h>
#include <cstdio>
#include <filesystem>
#include <stdexcept>
static void Require(bool ok, const char* message)
{
if (!ok)
throw std::runtime_error(message);
}
template <typename T> static T Export(HMODULE module, const char* name)
{
auto result = reinterpret_cast<T>(GetProcAddress(module, name));
Require(result != nullptr, name);
return result;
}
static void PrintModule(const char* label, void* function)
{
HMODULE module = nullptr;
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(function), &module);
wchar_t path[MAX_PATH] {};
GetModuleFileNameW(module, path, MAX_PATH);
std::printf("%s: %ls\n", label, path);
}
// Contain the old-path access violation to demonstrate the regression without launching a game.
static unsigned CallReflex(decltype(&slReflexSetOptions) function, const sl::ReflexOptions& options)
{
__try { return static_cast<unsigned>(function(options)); }
__except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{ return EXCEPTION_ACCESS_VIOLATION; }
}
int wmain(int argc, wchar_t** argv)
{
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
try
{
Require(argc == 3, "Expected Streamline and game binary directories");
const std::filesystem::path directory(argv[1]);
const auto interposer = LoadLibraryW((directory / L"sl.interposer.dll").c_str());
Require(interposer != nullptr, "Load Streamline interposer");
const auto init = Export<decltype(&slInit)>(interposer, "slInit");
const auto setDevice = Export<decltype(&slSetD3DDevice)>(interposer, "slSetD3DDevice");
const auto getFeature = Export<decltype(&slGetFeatureFunction)>(interposer, "slGetFeatureFunction");
const auto shutdown = Export<decltype(&slShutdown)>(interposer, "slShutdown");
const sl::Feature features[] = { sl::kFeatureDLSS_G, sl::kFeatureReflex, sl::kFeaturePCL };
const wchar_t* paths[] = { argv[1], argv[2] };
sl::Preferences pref;
pref.applicationId = 0x0F71CA1E;
pref.featuresToLoad = features;
pref.numFeaturesToLoad = 3;
pref.pathsToPlugins = paths;
pref.numPathsToPlugins = 2;
pref.renderAPI = sl::RenderAPI::eD3D12;
pref.flags |= sl::PreferenceFlags::eUseManualHooking;
pref.flags |= sl::PreferenceFlags::eUseFrameBasedResourceTagging;
pref.flags |= sl::PreferenceFlags::eUseDXGIFactoryProxy;
pref.flags &= ~sl::PreferenceFlags::eAllowOTA;
pref.flags &= ~sl::PreferenceFlags::eLoadDownloadedPlugins;
Require(init(pref, sl::kSDKVersion) == sl::Result::eOk, "slInit");
// Mirror the previous binding order: the bundled plugin is loaded before device selection.
const auto bundled = LoadLibraryW((directory / L"sl.reflex.dll").c_str());
const auto getPlugin = Export<void* (*)(const char*)>(bundled, "slGetPluginFunction");
const auto stale = reinterpret_cast<decltype(&slReflexSetOptions)>(getPlugin("slReflexSetOptions"));
Require(stale != nullptr, "Bundled Reflex export");
ID3D12Device* device = nullptr;
Require(SUCCEEDED(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&device))), "D3D12 device");
Require(setDevice(device) == sl::Result::eOk, "slSetD3DDevice");
void* function = nullptr;
Require(getFeature(sl::kFeatureReflex, "slReflexSetOptions", function) == sl::Result::eOk && function,
"Active Reflex function");
PrintModule("Bundled Reflex", reinterpret_cast<void*>(stale));
PrintModule("Active Reflex", function);
sl::ReflexOptions options;
options.mode = sl::ReflexMode::eOff;
options.useMarkersToOptimize = false;
const auto activeReflex = reinterpret_cast<decltype(&slReflexSetOptions)>(function);
const auto activeResult = CallReflex(activeReflex, options);
Require(activeResult == static_cast<unsigned>(sl::Result::eOk), "Active Reflex startup call");
std::puts("PASS: active Reflex startup call");
for (const auto& binding : { std::pair{sl::kFeatureDLSS_G, "slDLSSGSetOptions"},
{sl::kFeatureDLSS_G, "slDLSSGGetState"},
{sl::kFeatureReflex, "slReflexGetState"},
{sl::kFeatureReflex, "slReflexSleep"},
{sl::kFeaturePCL, "slPCLSetMarker"},
{sl::kFeaturePCL, "slPCLSetOptions"} })
{
function = nullptr;
Require(getFeature(binding.first, binding.second, function) == sl::Result::eOk && function, binding.second);
}
std::puts("PASS: all required DLSSG/Reflex/PCL functions resolved from active plugins");
if (stale != activeReflex)
{
const auto staleResult = CallReflex(stale, options);
std::printf("Old bundled call result: 0x%08x\n", staleResult);
Require(staleResult == EXCEPTION_ACCESS_VIOLATION, "Expected old-path access violation");
std::puts("PASS: reproduced original access violation; active-plugin binding avoids it");
}
else
std::puts("No plugin replacement on this runtime: active and bundled functions match");
shutdown();
device->Release();
return 0;
}
catch (const std::exception& error)
{
std::fprintf(stderr, "FAIL: %s\n", error.what());
return 1;
}
}