Skip to content

Commit 2f8c807

Browse files
committed
Refactor IApplicationFramework global init & update CSystemWin32 delay DLL load - add new features, correct some bugs. Make CE executables hot-swap real, fix debug DLL load issues; terminate NSC exe if isAPILoaded is false
1 parent 14dee8f commit 2f8c807

File tree

6 files changed

+166
-66
lines changed

6 files changed

+166
-66
lines changed

cmake/common.cmake

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ function(nbl_handle_dll_definitions _TARGET_ _SCOPE_)
2929
set(_NABLA_OUTPUT_DIR_ "${NBL_ROOT_PATH_BINARY}/src/nbl/$<CONFIG>/devshgraphicsprogramming.nabla")
3030

3131
target_compile_definitions(${_TARGET_} ${_SCOPE_}
32-
_NABLA_DLL_NAME_="$<TARGET_FILE_NAME:Nabla>";_NABLA_OUTPUT_DIR_="${_NABLA_OUTPUT_DIR_}";_NABLA_INSTALL_DIR_="${CMAKE_INSTALL_PREFIX}"
33-
)
32+
_NABLA_DLL_NAME_="$<PATH:REMOVE_EXTENSION,$<TARGET_FILE_NAME:Nabla>>";_NABLA_OUTPUT_DIR_="${_NABLA_OUTPUT_DIR_}"
33+
)
3434
endif()
3535

3636
target_compile_definitions(${_TARGET_} ${_SCOPE_}
@@ -252,6 +252,8 @@ macro(nbl_create_executable_project _EXTRA_SOURCES _EXTRA_OPTIONS _EXTRA_INCLUDE
252252
target_compile_definitions(${EXECUTABLE_NAME}
253253
PRIVATE "-DNBL_CPACK_PACKAGE_NABLA_DLL_DIR=\"${_NBL_NABLA_PACKAGE_RUNTIME_DLL_DIR_PATH_REL_TO_TARGET_}\""
254254
PRIVATE "-DNBL_CPACK_PACKAGE_DXC_DLL_DIR=\"${_NBL_DXC_PACKAGE_RUNTIME_DLL_DIR_PATH_REL_TO_TARGET_}\""
255+
PRIVATE "-DNBL_CPACK_PACKAGE_NABLA_DLL_DIR_ABS_KEY=\"${_NBL_NABLA_PACKAGE_RUNTIME_DLL_DIR_PATH_}\""
256+
PRIVATE "-DNBL_CPACK_PACKAGE_DXC_DLL_DIR_ABS_KEY=\"${_NBL_DXC_PACKAGE_RUNTIME_DLL_DIR_PATH_}\""
255257
)
256258
endif()
257259

include/nbl/system/CSystemWin32.h

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ class NBL_API2 CSystemWin32 : public ISystem
3030
template<typename PathContainer=core::vector<system::path>>
3131
static inline HRESULT delayLoadDLL(const char* dllName, const PathContainer& paths)
3232
{
33-
#ifdef NBL_EXPLICIT_MODULE_LOAD_LOG
3433
auto getModulePath = [](HMODULE hModule) -> std::string
3534
{
3635
char path[MAX_PATH];
@@ -39,8 +38,19 @@ class NBL_API2 CSystemWin32 : public ISystem
3938

4039
return std::string(path);
4140
};
42-
#endif // NBL_EXPLICIT_MODULE_LOAD_LOG
4341

42+
const bool logStatus = bool(std::getenv("NBL_EXPLICIT_MODULE_LOAD_LOG"))
43+
#ifdef NBL_EXPLICIT_MODULE_LOAD_LOG
44+
or true
45+
#endif
46+
; // legal & on purpose
47+
48+
const bool logRequests = bool(std::getenv("NBL_EXPLICIT_MODULE_REQUEST_LOG"))
49+
#ifdef NBL_EXPLICIT_MODULE_REQUEST_LOG
50+
or true
51+
#endif
52+
; // legal & on purpose
53+
4454
const auto executableDirectory = []() -> std::filesystem::path
4555
{
4656
wchar_t path[MAX_PATH] = { 0 };
@@ -60,40 +70,52 @@ class NBL_API2 CSystemWin32 : public ISystem
6070
// first try relative to CWD
6171
{
6272
const auto path = std::filesystem::absolute(requestModulePath).string();
73+
74+
if(logRequests)
75+
printf("[INFO]: Requesting \"%s\" module load with \"%s\" search path...\n", dllName, path.c_str());
76+
6377
if (res = LoadLibraryExA(path.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH))
6478
break;
6579
}
6680

6781
// then relative to the executable's directory
6882
{
6983
const auto path = std::filesystem::absolute(executableDirectory / requestModulePath).string();
84+
85+
if (logRequests)
86+
printf("[INFO]: Requesting \"%s\" module load with \"%s\" search path...\n", dllName, path.c_str());
87+
7088
if (res = LoadLibraryExA(path.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH))
7189
break;
7290
}
7391
}
7492

7593
// if still can't find, try looking for a system wide install
7694
if (!res)
95+
{
96+
if (logRequests)
97+
printf("[INFO]: Requesting \"%s\" module load with system wide search policy...\n", dllName);
98+
7799
res = LoadLibraryExA(dllName, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
100+
}
78101

79-
#ifdef NBL_EXPLICIT_MODULE_LOAD_LOG
80102
if (res)
81103
{
82-
const auto modulePath = getModulePath(res);
83-
printf("[INFO]: Loaded \"%s\" module\n", modulePath.c_str());
104+
if (logStatus)
105+
{
106+
const auto modulePath = getModulePath(res);
107+
printf("[INFO]: Loaded \"%s\" module\n", modulePath.c_str());
108+
}
84109
}
85-
#endif // NBL_EXPLICIT_MODULE_LOAD_LOG
86110

87111
if (!res)
88112
{
89-
#ifdef NBL_EXPLICIT_MODULE_LOAD_LOG
90-
printf("[ERROR]: Could not load \"%s\" module\n", dllName);
91-
#endif // NBL_EXPLICIT_MODULE_LOAD_LOG
113+
if (logStatus)
114+
printf("[ERROR]: Could not load \"%s\" module\n", dllName);
92115

93116
return E_FAIL;
94117
}
95118

96-
// VS 17.9.6 bug
97119
__HrLoadAllImportsForDll(dllName);
98120
return true;
99121
}

include/nbl/system/IApplicationFramework.h

Lines changed: 94 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,40 +25,92 @@ class IApplicationFramework : public core::IReferenceCounted
2525
// this is safe to call multiple times
2626
static bool GlobalsInit()
2727
{
28-
#ifdef _NBL_PLATFORM_WINDOWS_
29-
#ifdef NBL_CPACK_PACKAGE_DXC_DLL_DIR
30-
#ifdef NBL_CPACK_NO_BUILD_DIRECTORY_MODULES
31-
const HRESULT dxcLoad = CSystemWin32::delayLoadDLL("dxcompiler.dll", { NBL_CPACK_PACKAGE_DXC_DLL_DIR });
32-
#else
33-
const HRESULT dxcLoad = CSystemWin32::delayLoadDLL("dxcompiler.dll", { path(_DXC_DLL_).parent_path(), NBL_CPACK_PACKAGE_DXC_DLL_DIR });
34-
#endif
28+
// TODO: update CMake and rename "DLL" in all of those defines here to "MODULE" or "RUNTIME"
29+
30+
auto getEnvInstallDirectory = []()
31+
{
32+
const char* sdk = std::getenv("NBL_INSTALL_DIRECTORY");
33+
34+
if (sdk)
35+
{
36+
const auto directory = system::path(sdk);
37+
38+
if (std::filesystem::exists(directory))
39+
return directory;
40+
}
41+
42+
return system::path("");
43+
};
44+
45+
constexpr struct
46+
{
47+
std::string_view nabla, dxc;
48+
} module =
49+
{
50+
#ifdef _NBL_SHARED_BUILD_
51+
_NABLA_DLL_NAME_
3552
#else
36-
const HRESULT dxcLoad = CSystemWin32::delayLoadDLL("dxcompiler.dll", { path(_DXC_DLL_).parent_path() });
53+
""
3754
#endif
55+
,
56+
"dxcompiler"
57+
};
3858

39-
if (FAILED(dxcLoad))
40-
return false;
41-
42-
#ifdef _NBL_SHARED_BUILD_
43-
// if there was no DLL next to the executable, then try from the Nabla build directory
44-
// else if nothing in the build dir, then try looking for Nabla in the CURRENT BUILD'S INSTALL DIR
45-
// and in CPack package install directory
46-
47-
#ifdef NBL_CPACK_PACKAGE_NABLA_DLL_DIR
48-
#ifdef NBL_CPACK_NO_BUILD_DIRECTORY_MODULES
49-
const HRESULT nablaLoad = CSystemWin32::delayLoadDLL(_NABLA_DLL_NAME_, { _NABLA_INSTALL_DIR_, NBL_CPACK_PACKAGE_NABLA_DLL_DIR });
50-
#else
51-
const HRESULT nablaLoad = CSystemWin32::delayLoadDLL(_NABLA_DLL_NAME_, { _NABLA_OUTPUT_DIR_,_NABLA_INSTALL_DIR_, NBL_CPACK_PACKAGE_NABLA_DLL_DIR });
52-
#endif
53-
#else
54-
const HRESULT nablaLoad = CSystemWin32::delayLoadDLL(_NABLA_DLL_NAME_, { _NABLA_OUTPUT_DIR_,_NABLA_INSTALL_DIR_ });
55-
#endif
56-
57-
if (FAILED(nablaLoad))
59+
const auto sdk = getEnvInstallDirectory();
60+
61+
struct
62+
{
63+
system::path nabla, dxc;
64+
} install, env, build, rel;
65+
66+
install.nabla = std::filesystem::absolute(system::path(_NABLA_INSTALL_DIR_) / NBL_CPACK_PACKAGE_NABLA_DLL_DIR_ABS_KEY);
67+
install.dxc = std::filesystem::absolute(system::path(_NABLA_INSTALL_DIR_) / NBL_CPACK_PACKAGE_DXC_DLL_DIR_ABS_KEY);
68+
69+
env.nabla = sdk / NBL_CPACK_PACKAGE_NABLA_DLL_DIR_ABS_KEY;
70+
env.dxc = sdk / NBL_CPACK_PACKAGE_DXC_DLL_DIR_ABS_KEY;
71+
72+
#ifdef _NBL_SHARED_BUILD_
73+
build.nabla = _NABLA_OUTPUT_DIR_;
74+
#endif
75+
build.dxc = path(_DXC_DLL_).parent_path();
76+
77+
#ifdef NBL_CPACK_PACKAGE_NABLA_DLL_DIR
78+
rel.nabla = NBL_CPACK_PACKAGE_NABLA_DLL_DIR;
79+
#endif
80+
81+
#ifdef NBL_CPACK_PACKAGE_DXC_DLL_DIR
82+
rel.dxc = NBL_CPACK_PACKAGE_DXC_DLL_DIR;
83+
#endif
84+
85+
auto load = [](std::string_view moduleName, const std::vector<system::path>& searchPaths)
86+
{
87+
#ifdef _NBL_PLATFORM_WINDOWS_
88+
const bool isAlreadyLoaded = GetModuleHandleA(moduleName.data());
89+
90+
if (not isAlreadyLoaded)
91+
{
92+
const HRESULT hook = system::CSystemWin32::delayLoadDLL(moduleName.data(), searchPaths);
93+
94+
//! 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,
95+
//! proc addresses will be resolved correctly but status may scream "FAILED" due to lack of a PDB to load
96+
97+
if (FAILED(hook))
5898
return false;
59-
#endif // _NBL_SHARED_BUILD_
60-
#else
61-
// nothing else needs to be done cause we have RPath
99+
}
100+
#else
101+
// nothing else needs to be done cause we have RPath
102+
// TODO: to be checked when time comes
103+
#endif
104+
105+
return true;
106+
};
107+
108+
if (not load(module.dxc, { install.dxc, env.dxc, build.dxc, rel.dxc }))
109+
return false;
110+
111+
#ifdef _NBL_SHARED_BUILD_
112+
if (not load(module.nabla, { install.nabla, env.nabla, build.nabla, rel.nabla }))
113+
return false;
62114
#endif
63115

64116
return true;
@@ -70,9 +122,13 @@ class IApplicationFramework : public core::IReferenceCounted
70122
{
71123
path CWD = system::path(argv[0]).parent_path().generic_string() + "/";
72124
auto app = core::make_smart_refctd_ptr<CRTP>(CWD/"../",CWD,CWD/"../../media/",CWD/"../../tmp/");
125+
73126
for (auto i=0; i<argc; i++)
74127
app->argv.emplace_back(argv[i]);
75128

129+
if (not app->isAPILoaded())
130+
app->onAPILoadFailure();
131+
76132
if (!app->onAppInitialized(nullptr))
77133
return -1;
78134
while (app->keepRunning())
@@ -95,11 +151,9 @@ class IApplicationFramework : public core::IReferenceCounted
95151

96152
// needs to be public because of how constructor forwarding works
97153
IApplicationFramework(const path& _localInputCWD, const path& _localOutputCWD, const path& _sharedInputCWD, const path& _sharedOutputCWD) :
98-
localInputCWD(_localInputCWD), localOutputCWD(_localOutputCWD), sharedInputCWD(_sharedInputCWD), sharedOutputCWD(_sharedOutputCWD)
99-
{
100-
const bool status = GlobalsInit();
101-
assert(status);
102-
}
154+
localInputCWD(_localInputCWD), localOutputCWD(_localOutputCWD), sharedInputCWD(_sharedInputCWD), sharedOutputCWD(_sharedOutputCWD), m_apiLoaded(GlobalsInit()) {}
155+
156+
virtual bool onAPILoadFailure() { return m_apiLoaded = false; }
103157

104158
// DEPRECATED
105159
virtual void setSystem(core::smart_refctd_ptr<ISystem>&& system) {}
@@ -112,6 +166,9 @@ class IApplicationFramework : public core::IReferenceCounted
112166
virtual void workLoopBody() = 0;
113167
virtual bool keepRunning() = 0;
114168

169+
//! returns status of global initialization - on false you are supposed to terminate the application with non-zero code (otherwise you enter undefined behavior zone)
170+
inline bool isAPILoaded() { return m_apiLoaded; }
171+
115172
protected:
116173
// need this one for skipping the whole constructor chain
117174
IApplicationFramework() = default;
@@ -130,7 +187,6 @@ class IApplicationFramework : public core::IReferenceCounted
130187
*******************************************************************
131188
*/
132189

133-
134190
/*
135191
This is a CWD which is used for reading app-local assets.
136192
Do NOT try writing to this path if you wan't your app to work on Android because on Android this CWD is located inside a readonly APK archive.
@@ -153,6 +209,8 @@ class IApplicationFramework : public core::IReferenceCounted
153209
This CWD is used to output data that can be shared between apps e.g. quantization cache
154210
*/
155211
path sharedOutputCWD;
212+
213+
bool m_apiLoaded;
156214
};
157215

158216
}

src/nbl/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,10 @@ if(NBL_EMBED_BUILTIN_RESOURCES)
651651
set_source_files_properties(${NABLA_RESOURCES_TO_EMBED_PUBLIC} PROPERTIES HEADER_FILE_ONLY TRUE)
652652
endif()
653653

654+
target_compile_definitions(Nabla
655+
PUBLIC _NABLA_INSTALL_DIR_="${CMAKE_INSTALL_PREFIX}"
656+
)
657+
654658
# generate Nabla definitions to a header
655659
glue_source_definitions(Nabla _NBL_SOURCE_DEFINITIONS_)
656660
set(_NBL_DEFINE_FILE_WRAPPER_ ${CMAKE_CURRENT_BINARY_DIR}/include/define.h)
@@ -691,7 +695,7 @@ propagate_changed_variables_to_parent_scope()
691695

692696
if(NOT NBL_STATIC_BUILD)
693697
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/generated/define.h"
694-
COMMAND "${CMAKE_COMMAND}" -DNBL_ROOT_PATH:PATH=${NBL_ROOT_PATH} -DNBL_WRAPPER_FILE:FILEPATH=${_NBL_DEFINE_FILE_WRAPPER_} -DNBL_GEN_DIRECTORY:PATH=${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/generated -D_NABLA_DLL_NAME_:STRING=$<TARGET_FILE_NAME:Nabla> -D_DXC_DLL_NAME_:STRING=${DXC_DLL_NAME} -D_NABLA_INSTALL_DIR_:PATH="${CMAKE_INSTALL_PREFIX}" -P ${NBL_ROOT_PATH}/cmake/scripts/nbl/nablaDefines.cmake
698+
COMMAND "${CMAKE_COMMAND}" -DNBL_ROOT_PATH:PATH=${NBL_ROOT_PATH} -DNBL_WRAPPER_FILE:FILEPATH=${_NBL_DEFINE_FILE_WRAPPER_} -DNBL_GEN_DIRECTORY:PATH=${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/generated -D_NABLA_DLL_NAME_:STRING=$<PATH:REMOVE_EXTENSION,$<TARGET_FILE_NAME:Nabla>> -D_DXC_DLL_NAME_:STRING=${DXC_DLL_NAME} -D_NABLA_INSTALL_DIR_:PATH="${CMAKE_INSTALL_PREFIX}" -P ${NBL_ROOT_PATH}/cmake/scripts/nbl/nablaDefines.cmake
695699
COMMENT "Launching defines.h generation script!"
696700
VERBATIM
697701
)

0 commit comments

Comments
 (0)