mirror of
https://github.com/optiscaler/OptiScaler.git
synced 2026-11-05 21:43:48 +00:00
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:
1 parent
02e8d8b92b
commit
7543d1437c
4 files changed
+176
-8
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user