Skip to content

Commit

Permalink
Merge pull request #789 from Devsh-Graphics-Programming/ngfx-si-cmake
Browse files Browse the repository at this point in the history
  • Loading branch information
keptsecret authored Dec 26, 2024
2 parents 69cd5d2 + 048d093 commit f3c777e
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 55 deletions.
3 changes: 3 additions & 0 deletions 3rdparty/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,9 @@ NBL_ADD_GIT_TRACKING_META_LIBRARY(nabla "${NBL_ROOT_PATH}")
NBL_ADD_GIT_TRACKING_META_LIBRARY(dxc "${CMAKE_CURRENT_SOURCE_DIR}/dxc/dxc")
NBL_GENERATE_GIT_TRACKING_META()

# NGFX
include(ngfx/ngfx.cmake)

if(NBL_BUILD_IMGUI)
set(NBL_IMGUI_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/imgui")
set(NBL_IMGUI_TEST_ENGINE_PROJECT_ROOT "${THIRD_PARTY_SOURCE_DIR}/imgui_test_engine")
Expand Down
67 changes: 67 additions & 0 deletions 3rdparty/ngfx/ngfx.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
option(NBL_BUILD_WITH_NGFX "Enable NGFX build" OFF)

# NOTE: on windows default installation path is:
# "C:/Program Files/NVIDIA Corporation/Nsight Graphics <version>/SDKs/NsightGraphicsSDK" <- define as "NGFX_SDK" environment variable
# then you can pick SDK version with "NGFX_SDK_VERSION" cache variable (CMake GUI list supported)

if(NBL_BUILD_WITH_NGFX)
if(NOT DEFINED ENV{NGFX_SDK})
message(FATAL_ERROR "\"NGFX_SDK\" environment variable must be defined to build with NBL_BUILD_WITH_NGFX enabled!")
endif()

set(NGFX_SDK "$ENV{NGFX_SDK}")
cmake_path(NORMAL_PATH NGFX_SDK OUTPUT_VARIABLE NGFX_SDK)

if(NOT EXISTS "${NGFX_SDK}")
message(FATAL_ERROR "Found \"NGFX_SDK\" environment variable but it is invalid, env:NGFX_SDK=\"${NGFX_SDK}\" doesn't exist!")
endif()

file(GLOB ENTRIES "${NGFX_SDK}/*")

set(NGFX_VERSIONS "")
foreach(ENTRY ${ENTRIES})
if(IS_DIRECTORY ${ENTRY})
list(APPEND NGFX_VERSIONS ${ENTRY})
endif()
endforeach()

if(NOT NGFX_VERSIONS)
message(FATAL_ERROR "Could not find any NGFX SDK Version!")
endif()

list(TRANSFORM NGFX_VERSIONS REPLACE "${NGFX_SDK}/" "")
list(SORT NGFX_VERSIONS)
list(GET NGFX_VERSIONS -1 LATEST_NGFX_VERSION)

# on the cache variable init pick the latest version, then let user pick from list
set(NGFX_SDK_VERSION "${LATEST_NGFX_VERSION}" CACHE STRING "NGFX SDK Version")
set_property(CACHE NGFX_SDK_VERSION PROPERTY STRINGS ${NGFX_VERSIONS})

set(NGFX_SDK_VERSION "$CACHE{NGFX_SDK_VERSION}")
set(NGFX_SDK_BASE "${NGFX_SDK}/${NGFX_SDK_VERSION}")

# TODO: wanna support more *host* platforms? (*)
# NOTE: also I'm hardcoding windows x64 library requests till I know the answer for (*)
find_file(NBL_NGFX_INJECTION_HEADER NGFX_Injection.h PATHS ${NGFX_SDK_BASE}/include)
find_file(NBL_NGFX_INJECTION_DLL NGFX_Injection.dll PATHS ${NGFX_SDK_BASE}/lib/x64)
find_file(NBL_NGFX_INJECTION_IMPORT_LIBRARY NGFX_Injection.lib PATHS ${NGFX_SDK_BASE}/lib/x64)

if(NBL_NGFX_INJECTION_HEADER AND NBL_NGFX_INJECTION_DLL AND NBL_NGFX_INJECTION_IMPORT_LIBRARY)
message(STATUS "Enabled build with NVIDIA Nsight Graphics SDK ${NGFX_SDK_VERSION}\nlocated in: \"${NGFX_SDK_BASE}\"")
else()
message(STATUS "Could not enable build with NVIDIA Nsight Graphics SDK ${NGFX_SDK_VERSION} - invalid components!")
message(STATUS "Located in: \"${NGFX_SDK_BASE}\"")
message(STATUS "NBL_NGFX_INJECTION_HEADER=\"${NBL_NGFX_INJECTION_HEADER}\"")
message(STATUS "NBL_NGFX_INJECTION_DLL=\"${NBL_NGFX_INJECTION_DLL}\"")
message(STATUS "NBL_NGFX_INJECTION_IMPORT_LIBRARY=\"${NBL_NGFX_INJECTION_IMPORT_LIBRARY}\"")
message(FATAL_ERROR "You installation may be corupted, please fix it and re-run CMake or disable NBL_BUILD_WITH_NGFX!")
endif()

add_library(ngfx INTERFACE)
target_sources(ngfx INTERFACE "${NBL_NGFX_INJECTION_HEADER}")
target_include_directories(ngfx INTERFACE "${NGFX_SDK_BASE}/include")
target_link_libraries(ngfx INTERFACE "${NBL_NGFX_INJECTION_IMPORT_LIBRARY}")
target_link_options(ngfx INTERFACE "/DELAYLOAD:NGFX_Injection.dll")
target_compile_definitions(ngfx INTERFACE NGFX_INJECTION_DLL_DIR="${NGFX_SDK_BASE}/lib/x64")
target_compile_definitions(ngfx INTERFACE NGFX_VERSION="${NGFX_SDK_VERSION}")
endif()
10 changes: 8 additions & 2 deletions include/nbl/video/IAPIConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,17 @@ class NBL_API2 IAPIConnection : public core::IReferenceCounted
SDebuggerType m_debuggerType;
renderdoc_api_t* m_rdoc_api;

struct SNGFXIntegration {
bool useNGFX;
struct SNGFXIntegration
{
SNGFXIntegration();

bool useNGFX = false;

bool injectNGFXToProcess();
bool executeNGFXCommand();
inline bool isAPILoaded() { return m_loaded; }
private:
const bool m_loaded;
};
using ngfx_api_t = SNGFXIntegration;
ngfx_api_t m_ngfx_api;
Expand Down
15 changes: 15 additions & 0 deletions src/nbl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,21 @@ write_source_definitions("${_NBL_DEFINE_FILE_WRAPPER_}" "${_NBL_SOURCE_DEFINITIO
# git version tracking
target_link_libraries(Nabla PUBLIC gtml)

# NGFX
if(TARGET ngfx)
if(NBL_STATIC_BUILD)
target_link_libraries(Nabla INTERFACE ngfx)
else()
target_link_libraries(Nabla PRIVATE ngfx)
endif()

target_include_directories(Nabla PRIVATE $<TARGET_PROPERTY:ngfx,INTERFACE_INCLUDE_DIRECTORIES>)
target_compile_definitions(Nabla
PRIVATE NBL_BUILD_WITH_NGFX
PRIVATE $<TARGET_PROPERTY:ngfx,INTERFACE_COMPILE_DEFINITIONS>
)
endif()

#on MSVC it won't compile without this option!
if (MSVC)
target_compile_options(Nabla PUBLIC /bigobj)
Expand Down
171 changes: 118 additions & 53 deletions src/nbl/video/IAPIConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

#include "nbl/video/IPhysicalDevice.h"
#include "nbl/video/utilities/renderdoc.h"
#include "nbl/video/utilities/ngfx.h"

// TODO: temporary hopefully
#include "C:\Program Files\NVIDIA Corporation\Nsight Graphics 2024.1.0\SDKs\NsightGraphicsSDK\0.8.0\include\NGFX_Injection.h"
#include "nbl/system/CSystemWin32.h"

#ifdef NBL_BUILD_WITH_NGFX
#include "NGFX_Injection.h"
#endif

#if defined(_NBL_POSIX_API_)
#include <dlfcn.h>
Expand All @@ -14,7 +16,6 @@
namespace nbl::video
{


std::span<IPhysicalDevice* const> IAPIConnection::getPhysicalDevices() const
{
static_assert(sizeof(std::unique_ptr<IPhysicalDevice>) == sizeof(void*));
Expand Down Expand Up @@ -59,70 +60,134 @@ IAPIConnection::IAPIConnection(const SFeatures& enabledFeatures)

bool IAPIConnection::SNGFXIntegration::injectNGFXToProcess()
{
uint32_t numInstallations = 0;
auto result = NGFX_Injection_EnumerateInstallations(&numInstallations, nullptr);
if (numInstallations == 0 || NGFX_INJECTION_RESULT_OK != result)
#ifdef NBL_BUILD_WITH_NGFX
if (m_loaded) //! this check is mandatory!
{
useNGFX = false;
return false;
}
uint32_t numInstallations = 0;
auto result = NGFX_Injection_EnumerateInstallations(&numInstallations, nullptr);
if (numInstallations == 0 || NGFX_INJECTION_RESULT_OK != result)
{
useNGFX = false;
return false;
}

std::vector<NGFX_Injection_InstallationInfo> installations(numInstallations);
result = NGFX_Injection_EnumerateInstallations(&numInstallations, installations.data());
if (numInstallations == 0 || NGFX_INJECTION_RESULT_OK != result)
{
useNGFX = false;
return false;
}
std::vector<NGFX_Injection_InstallationInfo> installations(numInstallations);
result = NGFX_Injection_EnumerateInstallations(&numInstallations, installations.data());
if (numInstallations == 0 || NGFX_INJECTION_RESULT_OK != result)
{
useNGFX = false;
return false;
}

// get latest installation
NGFX_Injection_InstallationInfo versionInfo = installations.back();
// get latest installation
NGFX_Injection_InstallationInfo versionInfo = installations.back();

uint32_t numActivities = 0;
result = NGFX_Injection_EnumerateActivities(&versionInfo, &numActivities, nullptr);
if (numActivities == 0 || NGFX_INJECTION_RESULT_OK != result)
{
useNGFX = false;
return false;
}
uint32_t numActivities = 0;
result = NGFX_Injection_EnumerateActivities(&versionInfo, &numActivities, nullptr);
if (numActivities == 0 || NGFX_INJECTION_RESULT_OK != result)
{
useNGFX = false;
return false;
}

std::vector<NGFX_Injection_Activity> activities(numActivities);
result = NGFX_Injection_EnumerateActivities(&versionInfo, &numActivities, activities.data());
if (NGFX_INJECTION_RESULT_OK != result)
{
useNGFX = false;
return false;
}
std::vector<NGFX_Injection_Activity> activities(numActivities);
result = NGFX_Injection_EnumerateActivities(&versionInfo, &numActivities, activities.data());
if (NGFX_INJECTION_RESULT_OK != result)
{
useNGFX = false;
return false;
}

const NGFX_Injection_Activity* pActivityToInject = nullptr;
for (const NGFX_Injection_Activity& activity : activities)
{
if (activity.type == NGFX_INJECTION_ACTIVITY_FRAME_DEBUGGER) // only want frame debugger
const NGFX_Injection_Activity* pActivityToInject = nullptr;
for (const NGFX_Injection_Activity& activity : activities)
{
pActivityToInject = &activity;
break;
if (activity.type == NGFX_INJECTION_ACTIVITY_FRAME_DEBUGGER) // only want frame debugger
{
pActivityToInject = &activity;
break;
}
}
}

if (!pActivityToInject) {
useNGFX = false;
return false;
}
if (!pActivityToInject) {
useNGFX = false;
return false;
}

result = NGFX_Injection_InjectToProcess(&versionInfo, pActivityToInject);
if (NGFX_INJECTION_RESULT_OK != result)
{
useNGFX = false;
return false;
}
result = NGFX_Injection_InjectToProcess(&versionInfo, pActivityToInject);
if (NGFX_INJECTION_RESULT_OK != result)
{
useNGFX = false;
return false;
}

useNGFX = true;

return true;
} // optional TOOD: could log on "else"
#endif // NBL_BUILD_WITH_NGFX

useNGFX = true;
return true;
return false;
}

bool IAPIConnection::SNGFXIntegration::executeNGFXCommand()
{
return NGFX_Injection_ExecuteActivityCommand() == NGFX_INJECTION_RESULT_OK;
#ifdef NBL_BUILD_WITH_NGFX
if(m_loaded) //! this check is mandatory!
return NGFX_Injection_ExecuteActivityCommand() == NGFX_INJECTION_RESULT_OK; // optional TOOD: could log on "else"
#endif // NBL_BUILD_WITH_NGFX

return false;
}

IAPIConnection::SNGFXIntegration::SNGFXIntegration()
: useNGFX(false /*??*/), m_loaded([]() -> bool
{
#ifdef NBL_BUILD_WITH_NGFX
//! absolute path to official install NGFX SDK runtime directory
auto getOfficialRuntimeDirectory = []()
{
const char* sdk = std::getenv("NGFX_SDK");
const char* version = std::getenv("NGFX_VERSION");
const bool composed = sdk && version;

if (composed)
{
const auto directory = system::path(sdk) / system::path(version) / "lib" / "x64";

if (std::filesystem::exists(directory))
return directory;
}

return system::path("");
};

//! batch request with priority order & custom Nabla runtime search, I'm assuming we are loading the runtime from official SDK not custom location
//! one question is if we should have any constraints for min/max version, maybe force the "version"
//! to match the "NGFX_VERSION" define so to "what we built with", or don't have any - just like now

#if defined(_NBL_PLATFORM_WINDOWS_)
static constexpr std::string_view NGFXMODULE = "NGFX_Injection.dll";
HMODULE isAlreadyLoaded = GetModuleHandleA(NGFXMODULE.data());

if (!isAlreadyLoaded)
{
const auto dll = getOfficialRuntimeDirectory() / NGFXMODULE.data();
const HRESULT hook = system::CSystemWin32::delayLoadDLL(NGFXMODULE.data(), { NGFX_INJECTION_DLL_DIR, dll.parent_path() });

//! don't be scared if you see "No symbols loaded" - you will not hit "false" in this case, the DLL will get loaded if found,
//! proc addresses will be resolved correctly but status will scream "FAILED" because we don't have any PDB to load
if (FAILED(hook))
return false;
}
#else
#error "TODO!"
#endif

return true;
#else
return false; // no NGFX build -> no API to load
#endif
}())
{}

}

0 comments on commit f3c777e

Please sign in to comment.