Skip to content

Commit

Permalink
Streamline dynamic link
Browse files Browse the repository at this point in the history
  • Loading branch information
sultim-t committed Aug 3, 2023
1 parent 4bd41f1 commit 90c15d6
Show file tree
Hide file tree
Showing 5 changed files with 437 additions and 139 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ set(Sources
"Source/VulkanDevice_Dev.cpp"
"Source/JsonParser.cpp"
"Source/SceneMeta.cpp"
"Source/Streamline.cpp"
)


Expand Down
320 changes: 320 additions & 0 deletions Source/Streamline.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
// Copyright (c) 2023 Sultim Tsyrendashiev
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include "Streamline.h"
#include "Streamline.h"
#include "Streamline.h"

#include "RgException.h"

namespace
{

static_assert( uint32_t( sl::FunctionHookID::eMaxNum ) == 20,
"Recheck functions that need to be hooked" );

void ResetStreamlineVkFunctionsToDefault()
{
RTGL1::svkCreateInstance = vkCreateInstance;
RTGL1::svkCreateWin32SurfaceKHR = vkCreateWin32SurfaceKHR;
RTGL1::svkDestroySurfaceKHR = vkDestroySurfaceKHR;

RTGL1::svkCreateDevice = vkCreateDevice;
RTGL1::svkQueuePresentKHR = vkQueuePresentKHR;
RTGL1::svkCreateSwapchainKHR = vkCreateSwapchainKHR;
RTGL1::svkDestroySwapchainKHR = vkDestroySwapchainKHR;
RTGL1::svkGetSwapchainImagesKHR = vkGetSwapchainImagesKHR;
RTGL1::svkAcquireNextImageKHR = vkAcquireNextImageKHR;
RTGL1::svkDeviceWaitIdle = vkDeviceWaitIdle;
}

auto FindInterposerDll( const std::filesystem::path& binFolder ) -> HMODULE
{
assert( binFolder.is_absolute() );

auto dllPath = binFolder / "sl.interposer.dll";

if( !sl::security::verifyEmbeddedSignature( dllPath.c_str() ) )
{
RTGL1::debug::Error( "Failed to verify signature for NVIDIA Streamline: {}",
dllPath.string() );
return nullptr;
}

return LoadLibrary( dllPath.string().c_str() );
}

auto FetchFunctions( HMODULE interposerDll ) -> std::optional< RTGL1::Streamline::Pfn >
{
if( !interposerDll )
{
return {};
}

auto fetchInto = [ interposerDll ]< typename T >( T* dst, const char* name ) {
if( auto f = reinterpret_cast< T >( GetProcAddress( interposerDll, name ) ) )
{
*dst = f;
return true;
}
RTGL1::debug::Warning( "(NVIDIA Streamline) Failed to fetch function: {}", name );
return false;
};

// clang-format off
auto pfn = RTGL1::Streamline::Pfn{};
if( !fetchInto( &pfn.slInit , "slInit" ) ) { return {}; }
if( !fetchInto( &pfn.slShutdown , "slShutdown" ) ) { return {}; }
if( !fetchInto( &pfn.slIsFeatureSupported , "slIsFeatureSupported" ) ) { return {}; }
if( !fetchInto( &pfn.slIsFeatureLoaded , "slIsFeatureLoaded" ) ) { return {}; }
if( !fetchInto( &pfn.slSetFeatureLoaded , "slSetFeatureLoaded" ) ) { return {}; }
if( !fetchInto( &pfn.slEvaluateFeature , "slEvaluateFeature" ) ) { return {}; }
if( !fetchInto( &pfn.slAllocateResources , "slAllocateResources" ) ) { return {}; }
if( !fetchInto( &pfn.slFreeResources , "slFreeResources" ) ) { return {}; }
if( !fetchInto( &pfn.slSetTag , "slSetTag" ) ) { return {}; }
if( !fetchInto( &pfn.slGetFeatureRequirements , "slGetFeatureRequirements" ) ) { return {}; }
if( !fetchInto( &pfn.slGetFeatureVersion , "slGetFeatureVersion" ) ) { return {}; }
if( !fetchInto( &pfn.slUpgradeInterface , "slUpgradeInterface" ) ) { return {}; }
if( !fetchInto( &pfn.slSetConstants , "slSetConstants" ) ) { return {}; }
if( !fetchInto( &pfn.slGetNativeInterface , "slGetNativeInterface" ) ) { return {}; }
if( !fetchInto( &pfn.slGetFeatureFunction , "slGetFeatureFunction" ) ) { return {}; }
if( !fetchInto( &pfn.slGetNewFrameToken , "slGetNewFrameToken" ) ) { return {}; }
if( !fetchInto( &pfn.slSetVulkanInfo , "slSetVulkanInfo" ) ) { return {}; }
// clang-format on

return pfn;
}

auto FetchVkInstanceFunctions( RTGL1::Streamline::Proxies& proxies, VkInstance instance )
-> std::optional< RTGL1::Streamline::ProxiesVkInstance >
{
auto fetchVkInto = [ &proxies, instance ]< typename T >( T* dst, const char* name ) {
if( auto f = reinterpret_cast< T >( proxies.vkGetInstanceProcAddr( instance, name ) ) )
{
*dst = f;
return true;
}
RTGL1::debug::Warning( "(NVIDIA Streamline) Failed to fetch function: {}", name );
return false;
};

// clang-format off
auto instPfn = RTGL1::Streamline::ProxiesVkInstance{};
if( !fetchVkInto( &instPfn.vkCreateWin32SurfaceKHR , "vkCreateWin32SurfaceKHR" ) ) { return {}; }
if( !fetchVkInto( &instPfn.vkDestroySurfaceKHR , "vkDestroySurfaceKHR" ) ) { return {}; }
// clang-format on

return instPfn;
}

auto FetchVkDeviceFunctions( RTGL1::Streamline::Proxies& proxies, VkDevice device )
-> std::optional< RTGL1::Streamline::ProxiesVkDevice >
{
auto fetchVkInto = [ &proxies, device ]< typename T >( T* dst, const char* name ) {
if( auto f = reinterpret_cast< T >( proxies.vkGetDeviceProcAddr( device, name ) ) )
{
*dst = f;
return true;
}
RTGL1::debug::Warning( "(NVIDIA Streamline) Failed to fetch function: {}", name );
return false;
};

// clang-format off
auto dvcPfn = RTGL1::Streamline::ProxiesVkDevice{};
if( !fetchVkInto( &dvcPfn.vkCreateDevice , "vkCreateDevice" ) ) { return {}; }
if( !fetchVkInto( &dvcPfn.vkQueuePresentKHR , "vkQueuePresentKHR" ) ) { return {}; }
if( !fetchVkInto( &dvcPfn.vkCreateSwapchainKHR , "vkCreateSwapchainKHR" ) ) { return {}; }
if( !fetchVkInto( &dvcPfn.vkDestroySwapchainKHR , "vkDestroySwapchainKHR" ) ) { return {}; }
if( !fetchVkInto( &dvcPfn.vkGetSwapchainImagesKHR , "vkGetSwapchainImagesKHR" ) ) { return {}; }
if( !fetchVkInto( &dvcPfn.vkAcquireNextImageKHR , "vkAcquireNextImageKHR" ) ) { return {}; }
if( !fetchVkInto( &dvcPfn.vkDeviceWaitIdle , "vkDeviceWaitIdle" ) ) { return {}; }
// clang-format on

return dvcPfn;
}

auto InitStreamline( HMODULE interposerDll,
RTGL1::Streamline::Pfn& pfn,
const char* pAppGUID,
const std::filesystem::path& binFolder,
bool isDebugBuild ) -> std::optional< RTGL1::Streamline::Proxies >
{
assert( binFolder.is_absolute() );

using namespace RTGL1;

{
const auto guidRegex =
std::regex{ "^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$" };

if( pAppGUID == nullptr )
{
throw RgException(
RG_RESULT_WRONG_FUNCTION_ARGUMENT,
"Application GUID wasn't provided. Generate and specify it to use DLSS." );
}

if( !std::regex_match( pAppGUID, guidRegex ) )
{
throw RgException(
RG_RESULT_WRONG_FUNCTION_ARGUMENT,
"Provided application GUID is not GUID. Generate and specify correct "
"GUID to use DLSS." );
}
}


if( !interposerDll )
{
return {};
}

auto fetchInto = [ interposerDll ]< typename T >( T* dst, const char* name ) {
if( auto f = reinterpret_cast< T >( GetProcAddress( interposerDll, name ) ) )
{
*dst = f;
return true;
}
RTGL1::debug::Warning( "(NVIDIA Streamline) Failed to fetch function: {}", name );
return false;
};

// clang-format off
auto proxies = Streamline::Proxies{};
if( !fetchInto( &proxies.vkGetDeviceProcAddr , "vkGetDeviceProcAddr" ) ) { return {}; }
if( !fetchInto( &proxies.vkGetInstanceProcAddr , "vkGetInstanceProcAddr" ) ) { return {}; }
if( !fetchInto( &proxies.vkCreateInstance , "vkCreateInstance" ) ) { return {}; }
// clang-format on


const wchar_t* pluginFolder = binFolder.c_str();

auto pref = sl::Preferences{};
{
pref.showConsole = isDebugBuild;
pref.logLevel = isDebugBuild ? sl::LogLevel::eVerbose : sl::LogLevel::eOff;
pref.logMessageCallback = []( sl::LogType type, const char* msg ) {
RgMessageSeverityFlags rgSeverity =
( type == sl::LogType::eError ) ? RG_MESSAGE_SEVERITY_ERROR
: ( type == sl::LogType::eWarn ) ? RG_MESSAGE_SEVERITY_WARNING
: ( type == sl::LogType::eInfo ) ? RG_MESSAGE_SEVERITY_INFO
: RG_MESSAGE_SEVERITY_VERBOSE;
debug::detail::Print( rgSeverity, msg );
};
pref.numPathsToPlugins = 1;
pref.pathsToPlugins = &pluginFolder;
pref.flags = sl::PreferenceFlags::eUseManualHooking;
pref.engineVersion = RG_RTGL_VERSION_API;
pref.projectId = pAppGUID;
pref.renderAPI = sl::RenderAPI::eVulkan;
}

if( SL_FAILED( slr, pfn.slInit( pref, sl::kSDKVersion ) ) )
{
switch( slr )
{
case sl::Result::eErrorDriverOutOfDate:
debug::Error( "(NVIDIA Streamline) Please, update to the latest drivers" );
case sl::Result::eErrorOSOutOfDate:
debug::Error( "(NVIDIA Streamline) Please, update the Windows OS" );
default:
debug::Error( "(NVIDIA Streamline) Failed to initialize Streamline. Error code: {}",
uint32_t( slr ) );
}

return {};
}

return proxies;
}

}

RTGL1::Streamline::Streamline( const char* pAppGUID,
const std::filesystem::path& binFolder,
bool isDebugBuild )
: interposerDll{ FindInterposerDll( binFolder ) }
, pfn{ FetchFunctions( interposerDll ) }
, proxies{ pfn ? InitStreamline( interposerDll, *pfn, pAppGUID, binFolder, isDebugBuild )
: std::nullopt }
{
}

RTGL1::Streamline::~Streamline()
{
ResetStreamlineVkFunctionsToDefault();
}

bool RTGL1::Streamline::HookVkCreateInstanceFunction()
{
if( proxies )
{
RTGL1::svkCreateInstance = proxies->vkCreateInstance;

assert( RTGL1::svkCreateInstance );
return true;
}
return false;
}

bool RTGL1::Streamline::HookVkInstanceFunctions( VkInstance instance )
{
if( proxies )
{
if( auto f = FetchVkInstanceFunctions( *proxies, instance ) )
{
RTGL1::svkCreateWin32SurfaceKHR = f->vkCreateWin32SurfaceKHR;
RTGL1::svkDestroySurfaceKHR = f->vkDestroySurfaceKHR;

assert( RTGL1::svkCreateWin32SurfaceKHR );
assert( RTGL1::svkDestroySurfaceKHR );
return true;
}
}
return false;
}

bool RTGL1::Streamline::HookVkDeviceFunctions( VkDevice device )
{
if( proxies )
{
if( auto f = FetchVkDeviceFunctions( *proxies, device ) )
{
RTGL1::svkCreateDevice = f->vkCreateDevice;
RTGL1::svkQueuePresentKHR = f->vkQueuePresentKHR;
RTGL1::svkCreateSwapchainKHR = f->vkCreateSwapchainKHR;
RTGL1::svkDestroySwapchainKHR = f->vkDestroySwapchainKHR;
RTGL1::svkGetSwapchainImagesKHR = f->vkGetSwapchainImagesKHR;
RTGL1::svkAcquireNextImageKHR = f->vkAcquireNextImageKHR;
RTGL1::svkDeviceWaitIdle = f->vkDeviceWaitIdle;

assert( RTGL1::svkCreateDevice );
assert( RTGL1::svkQueuePresentKHR );
assert( RTGL1::svkCreateSwapchainKHR );
assert( RTGL1::svkDestroySwapchainKHR );
assert( RTGL1::svkGetSwapchainImagesKHR );
assert( RTGL1::svkAcquireNextImageKHR );
assert( RTGL1::svkDeviceWaitIdle );
return true;
}
}
return false;
}
Loading

0 comments on commit 90c15d6

Please sign in to comment.