From 1185bd464092f372430cbfaa15a7be4dcaa90752 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Tue, 6 Nov 2018 14:28:25 -0500 Subject: Feature/shared library refactor (#712) * * Added ISlangSharedLibraryLoader and ISlangSharedLibrary * Implemented default implementations * Added slang API function to get/set the ISlangSharedLibraryLoader on the session * Put function caching onto the Session - so that if the loader is chaged, its easy to reset the shared libraries, and functions * Run premake. * Fix problem with setting null, would cause an unnecessary function/shared lib flush. * * Unload SharedLibrary when DefaultSharedLibrary is deleted. * Make SharedLibrary handle unload safely if already unloaded. * Refactor SharedLibrary, such that it becomes a utility class - simplifying it's semantics. * Simplified ISlangSharedLibrary such that doesn't have unload and isLoaded so easier to implement. Use updated SharedLibrary impl. * Disable aarch64 on windows * Premake windows files without aarch64 build. * Moved slang-shared-library to core (so can be used in code outside of main slang) Fixed problem in premake5 where on windows projects were incorrectly constructed * Allowed RefObject to base class of com types Added ConfigurableSharedLibraryLoader Added -dxc-path -fxc-path -glslang-path Fix problem with dxc-path not honoring it's path when loading dxil * Added documentation for command line control of dll loading paths. * Remove some tabbing issues. * Change name of include guard. --- source/core/core.vcxproj | 2 + source/core/core.vcxproj.filters | 6 ++ source/core/platform.cpp | 144 +++++++++++++++++++++-------------- source/core/platform.h | 56 +++++++++----- source/core/slang-shared-library.cpp | 115 ++++++++++++++++++++++++++++ source/core/slang-shared-library.h | 122 +++++++++++++++++++++++++++++ source/core/slang-string-util.h | 11 +-- source/core/smart-pointer.h | 12 +-- 8 files changed, 380 insertions(+), 88 deletions(-) create mode 100644 source/core/slang-shared-library.cpp create mode 100644 source/core/slang-shared-library.h (limited to 'source/core') diff --git a/source/core/core.vcxproj b/source/core/core.vcxproj index f3e89b518..97dc19d66 100644 --- a/source/core/core.vcxproj +++ b/source/core/core.vcxproj @@ -190,6 +190,7 @@ + @@ -207,6 +208,7 @@ + diff --git a/source/core/core.vcxproj.filters b/source/core/core.vcxproj.filters index 5031e67e7..b470f9b43 100644 --- a/source/core/core.vcxproj.filters +++ b/source/core/core.vcxproj.filters @@ -69,6 +69,9 @@ Header Files + + Header Files + Header Files @@ -116,6 +119,9 @@ Source Files + + Source Files + Source Files diff --git a/source/core/platform.cpp b/source/core/platform.cpp index 374606e4a..b96240def 100644 --- a/source/core/platform.cpp +++ b/source/core/platform.cpp @@ -1,6 +1,8 @@ // platform.cpp #include "platform.h" +#include "common.h" + #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #define NOMINMAX @@ -16,72 +18,102 @@ namespace Slang { // SharedLibrary - SharedLibrary SharedLibrary::load(char const* name) - { - SharedLibrary result; - result.handle = nullptr; +/* static */SlangResult SharedLibrary::load(const char* filename, SharedLibrary::Handle& handleOut) +{ + StringBuilder builder; + appendPlatformFileName(UnownedStringSlice(filename), builder); + return loadWithPlatformFilename(builder.begin(), handleOut); +} #ifdef _WIN32 - { - HMODULE h = LoadLibraryA(name); - result.handle = (Handle) h; - } -#else - { - String fullName; - fullName.append("lib"); - fullName.append(name); - fullName.append(".so"); - - void* h = dlopen(fullName.Buffer(), RTLD_NOW|RTLD_LOCAL); - if(!h) - { - if(auto msg = dlerror()) - { - fprintf(stderr, "error: %s\n", msg); - } - } - result.handle = (Handle) h; - } -#endif +/* static */SlangResult SharedLibrary::loadWithPlatformFilename(char const* platformFileName, SharedLibrary::Handle& handleOut) +{ + handleOut = nullptr; + // https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-loadlibrarya + const HMODULE h = LoadLibraryA(platformFileName); + if (!h) + { + const DWORD lastError = GetLastError(); + switch (lastError) + { + case ERROR_MOD_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_FILE_NOT_FOUND: + { + return SLANG_E_NOT_FOUND; + } + case ERROR_INVALID_ACCESS: + case ERROR_ACCESS_DENIED: + case ERROR_INVALID_DATA: + { + return SLANG_E_CANNOT_OPEN; + } + default: break; + } + // Turn to Result, if not one of the well known errors + return HRESULT_FROM_WIN32(lastError); + } + handleOut = (Handle)h; + return SLANG_OK; +} - return result; - } +/* static */void SharedLibrary::unload(Handle handle) +{ + SLANG_ASSERT(handle); + ::FreeLibrary((HMODULE)handle); +} + +/* static */SharedLibrary::FuncPtr SharedLibrary::findFuncByName(Handle handle, char const* name) +{ + SLANG_ASSERT(handle); + return (FuncPtr)GetProcAddress((HMODULE)handle, name); +} + +/* static */void SharedLibrary::appendPlatformFileName(const UnownedStringSlice& name, StringBuilder& dst) +{ + // Windows doesn't need the extension or any prefix to work + dst.Append(name); +} + +#else // _WIN32 + +/* static */SlangResult SharedLibrary::loadWithPlatformFilename(char const* platformFileName, Handle& handleOut) +{ + handleOut = nullptr; - void SharedLibrary::unload() + void* h = dlopen(platformFileName, RTLD_NOW | RTLD_LOCAL); + if(!h) { -#ifdef _WIN32 + if(auto msg = dlerror()) { - FreeLibrary( - (HMODULE) handle); + fprintf(stderr, "error: %s\n", msg); } -#else - { - dlclose(handle); - } -#endif - + return SLANG_FAIL; } + handleOut = (Handle)h; + return SLANG_OK; +} - SharedLibrary::FuncPtr SharedLibrary::findFuncByName(char const* name) - { - FuncPtr funcPtr = nullptr; +/* static */void SharedLibrary::unload(Handle handle) +{ + SLANG_ASSERT(handle); + dlclose(handle); +} -#ifdef _WIN32 - { - funcPtr = (FuncPtr) GetProcAddress( - (HMODULE) handle, - name); - } -#else - { - funcPtr = (FuncPtr) dlsym( - (void*) handle, - name); - } -#endif +/* static */SharedLibrary::FuncPtr SharedLibrary::findFuncByName(Handle handle, char const* name) +{ + SLANG_ASSERT(handle); + return (FuncPtr)dlsym((void*)handle, name); +} + +/* static */void SharedLibrary::appendPlatformFileName(const UnownedStringSlice& name, StringBuilder& dst) +{ + dst.Append("lib"); + dst.Append(name); + dst.Append(".so"); +} + +#endif // _WIN32 - return funcPtr; - } } \ No newline at end of file diff --git a/source/core/platform.h b/source/core/platform.h index fef698e6f..a545d139d 100644 --- a/source/core/platform.h +++ b/source/core/platform.h @@ -2,30 +2,50 @@ #ifndef SLANG_CORE_PLATFORM_H_INCLUDED #define SLANG_CORE_PLATFORM_H_INCLUDED +#include "../../slang.h" +#include "../core/slang-string.h" + namespace Slang { // Interface for working with shared libraries - // in a platfomr-independent fashion. + // in a platform-independent fashion. struct SharedLibrary { typedef struct SharedLibraryImpl* Handle; - Handle handle; - - // Attempt to load a shared library for - // the current platform. - static SharedLibrary load(char const* name); - - // If this refers to a valid loaded library, - // then attempt to unload it - void unload(); - - typedef void (*FuncPtr)(void); - - FuncPtr findFuncByName(char const* name); - - - operator Handle() { return handle; } - }; + + typedef void(*FuncPtr)(void); + + /// Load via an unadorned filename + /// + /// @param the unadorned filename + /// @return Returns a non null handle for the shared library on success. nullptr indicated failure + static SlangResult load(const char* filename, Handle& handleOut); + + /// Attempt to load a shared library for + /// the current platform. Returns null handle on failure + /// The platform specific filename can be generated from a call to appendPlatformFileName + /// + /// @param platformFileName the platform specific file name. + /// @return Returns a non null handle for the shared library on success. nullptr indicated failure + static SlangResult loadWithPlatformFilename(char const* platformFileName, Handle& handleOut); + + /// Unload the library that was returned from load as handle + /// @param The valid handle returned from load + static void unload(Handle handle); + + /// Given a shared library handle and a name, return the associated function + /// Return nullptr if function is not found + /// @param The shared library handle as returned by loadPlatformLibrary + static FuncPtr findFuncByName(Handle handle, char const* name); + + /// Append to the end of dst, the name, with any platform specific additions + /// The input name should be unadorned with any 'lib' prefix or extension + static void appendPlatformFileName(const UnownedStringSlice& name, StringBuilder& dst); + + private: + /// Not constructible! + SharedLibrary(); + }; #ifndef _MSC_VER #define _fileno fileno diff --git a/source/core/slang-shared-library.cpp b/source/core/slang-shared-library.cpp new file mode 100644 index 000000000..716f570c4 --- /dev/null +++ b/source/core/slang-shared-library.cpp @@ -0,0 +1,115 @@ +#include "slang-shared-library.h" + +#include "../../slang-com-ptr.h" +#include "../core/slang-io.h" +#include "../core/slang-string-util.h" + +namespace Slang +{ + +// Allocate static const storage for the various interface IDs that the Slang API needs to expose +static const Guid IID_ISlangUnknown = SLANG_UUID_ISlangUnknown; +static const Guid IID_ISlangSharedLibrary = SLANG_UUID_ISlangSharedLibrary; +static const Guid IID_ISlangSharedLibraryLoader = SLANG_UUID_ISlangSharedLibraryLoader; + +/* !!!!!!!!!!!!!!!!!!!!!!!!!! DefaultSharedLibraryLoader !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + +/* static */const char* DefaultSharedLibraryLoader::s_libraryNames[int(SharedLibraryType::CountOf)] = +{ + nullptr, // SharedLibraryType::Unknown + "dxcompiler", // SharedLibraryType::Dxc + "d3dcompiler_47", // SharedLibraryType::Fxc + "slang-glslang", // SharedLibraryType::Glslang + "dxil", // SharedLibraryType::Dxil +}; + +/* static */DefaultSharedLibraryLoader DefaultSharedLibraryLoader::s_singleton; + +/* static */SharedLibraryType DefaultSharedLibraryLoader::getSharedLibraryTypeFromName(const UnownedStringSlice& name) +{ + // Start from 1 to skip Unknown + for (int i = 1; i < SLANG_COUNT_OF(s_libraryNames); ++i) + { + if (name == s_libraryNames[i]) + { + return SharedLibraryType(i); + } + } + return SharedLibraryType::Unknown; +} + +ISlangUnknown* DefaultSharedLibraryLoader::getInterface(const Guid& guid) +{ + return (guid == IID_ISlangUnknown || guid == IID_ISlangSharedLibraryLoader) ? static_cast(this) : nullptr; +} + +SlangResult DefaultSharedLibraryLoader::loadSharedLibrary(const char* path, ISlangSharedLibrary** sharedLibraryOut) +{ + *sharedLibraryOut = nullptr; + // Try loading + SharedLibrary::Handle handle; + SLANG_RETURN_ON_FAIL(SharedLibrary::load(path, handle)); + *sharedLibraryOut = ComPtr(new DefaultSharedLibrary(handle)).detach(); + return SLANG_OK; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!! DefaultSharedLibrary !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + +ISlangUnknown* DefaultSharedLibrary::getInterface(const Guid& guid) +{ + return (guid == IID_ISlangUnknown || guid == IID_ISlangSharedLibrary) ? static_cast(this) : nullptr; +} + +DefaultSharedLibrary::~DefaultSharedLibrary() +{ + SharedLibrary::unload(m_sharedLibraryHandle); +} + +SlangFuncPtr DefaultSharedLibrary::findFuncByName(char const* name) +{ + return SharedLibrary::findFuncByName(m_sharedLibraryHandle, name); +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!! ConfigurableSharedLibraryLoader !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + +ISlangUnknown* ConfigurableSharedLibraryLoader::getInterface(const Guid& guid) +{ + return (guid == IID_ISlangUnknown || guid == IID_ISlangSharedLibraryLoader) ? static_cast(this) : nullptr; +} + +SlangResult ConfigurableSharedLibraryLoader::loadSharedLibrary(const char* path, ISlangSharedLibrary** sharedLibraryOut) +{ + Entry* entry = m_entryMap.TryGetValue(String(path)); + if (entry) + { + SharedLibrary::Handle handle; + SLANG_RETURN_ON_FAIL(entry->func(path, entry->entryString, handle)); + SLANG_ASSERT(handle); + + ComPtr sharedLib(new DefaultSharedLibrary(handle)); + *sharedLibraryOut = sharedLib.detach(); + return SLANG_OK; + } + + return DefaultSharedLibraryLoader::getSingleton()->loadSharedLibrary(path, sharedLibraryOut); +} + +/* static */Result ConfigurableSharedLibraryLoader::replace(const char* pathIn, const String& entryString, SharedLibrary::Handle& handleOut) +{ + SLANG_UNUSED(pathIn); + // The replacement is the *whole* string + return SharedLibrary::loadWithPlatformFilename(entryString.begin(), handleOut); +} + +/* static */Result ConfigurableSharedLibraryLoader::changePath(const char* pathIn, const String& entryString, SharedLibrary::Handle& handleOut ) +{ + // Okay we need to reconstruct the name and insert the path + StringBuilder builder; + SharedLibrary::appendPlatformFileName(UnownedStringSlice(pathIn), builder); + String path = Path::Combine(entryString, builder); + + return SharedLibrary::loadWithPlatformFilename(path.begin(), handleOut); +} + + +} \ No newline at end of file diff --git a/source/core/slang-shared-library.h b/source/core/slang-shared-library.h new file mode 100644 index 000000000..62d15b6b4 --- /dev/null +++ b/source/core/slang-shared-library.h @@ -0,0 +1,122 @@ +#ifndef SLANG_SHARED_LIBRARY_H_INCLUDED +#define SLANG_SHARED_LIBRARY_H_INCLUDED + +#include "../../slang.h" +#include "../../slang-com-helper.h" +#include "../../slang-com-ptr.h" + +#include "../core/platform.h" +#include "../core/common.h" +#include "../core/dictionary.h" + +namespace Slang +{ + +/* NOTE! Do not change this enum without making the appropriate changes to DefaultSharedLibraryLoader::s_libraryNames */ +enum class SharedLibraryType +{ + Unknown, ///< Unknown compiler + Dxc, ///< Dxc compiler + Fxc, ///< Fxc compiler + Glslang, ///< Slang specific glslang compiler + Dxil, ///< Dxil is used with dxc + CountOf, +}; + +class DefaultSharedLibraryLoader : public ISlangSharedLibraryLoader +{ +public: + + // ISlangUnknown + // override ref counting, as DefaultSharedLibraryLoader is singleton + SLANG_IUNKNOWN_QUERY_INTERFACE + SLANG_NO_THROW uint32_t SLANG_MCALL addRef() SLANG_OVERRIDE { return 1; } + SLANG_NO_THROW uint32_t SLANG_MCALL release() SLANG_OVERRIDE { return 1; } + + // ISlangSharedLibraryLoader + virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadSharedLibrary(const char* path, + ISlangSharedLibrary** sharedLibraryOut) SLANG_OVERRIDE; + + /// Get the singleton + static DefaultSharedLibraryLoader* getSingleton() { return &s_singleton; } + + /// Get the type from the name + static SharedLibraryType getSharedLibraryTypeFromName(const UnownedStringSlice& name); + + /// Get the name from the type, or nullptr if not known + static const char* getSharedLibraryNameFromType(SharedLibraryType type) { return s_libraryNames[int(type)]; } + + /// Make a shared library to it's name + static const char* s_libraryNames[int(SharedLibraryType::CountOf)]; + +private: + /// Make so not constructible + DefaultSharedLibraryLoader() {} + virtual ~DefaultSharedLibraryLoader() {} + + ISlangUnknown* getInterface(const Guid& guid); + + static DefaultSharedLibraryLoader s_singleton; +}; + +class DefaultSharedLibrary : public ISlangSharedLibrary, public RefObject +{ + public: + // ISlangUnknown + SLANG_REF_OBJECT_IUNKNOWN_ALL + + // ISlangSharedLibrary + virtual SLANG_NO_THROW SlangFuncPtr SLANG_MCALL findFuncByName(char const* name) SLANG_OVERRIDE; + + /// Ctor. + DefaultSharedLibrary(const SharedLibrary::Handle sharedLibraryHandle): + m_sharedLibraryHandle(sharedLibraryHandle) + { + SLANG_ASSERT(sharedLibraryHandle); + } + + /// Need virtual dtor to keep delete this happy + virtual ~DefaultSharedLibrary(); + + protected: + ISlangUnknown* getInterface(const Guid& guid); + + SharedLibrary::Handle m_sharedLibraryHandle = nullptr; +}; + +class ConfigurableSharedLibraryLoader: public ISlangSharedLibraryLoader, public RefObject +{ +public: + typedef Result (*Func)(const char* pathIn, const String& entryString, SharedLibrary::Handle& handleOut); + + // IUnknown + SLANG_REF_OBJECT_IUNKNOWN_ALL + + // ISlangSharedLibraryLoader + virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadSharedLibrary(const char* path, ISlangSharedLibrary** sharedLibraryOut) SLANG_OVERRIDE; + + /// Function to replace the the path with entryString + static Result replace(const char* pathIn, const String& entryString, SharedLibrary::Handle& handleOut); + /// Function to change the path using the entryStrinct + static Result changePath(const char* pathIn, const String& entryString, SharedLibrary::Handle& handleOut); + + void addEntry(const String& libName, Func func, const String& entryString) { m_entryMap.Add(libName, Entry{ func, entryString} ); } + void addEntry(SharedLibraryType libType, Func func, const String& entryString) { m_entryMap.Add(DefaultSharedLibraryLoader::getSharedLibraryNameFromType(libType), Entry { func, entryString} ); } + + virtual ~ConfigurableSharedLibraryLoader() {} + protected: + + struct Entry + { + Func func; + String entryString; + }; + + ISlangUnknown* getInterface(const Guid& guid); + + Dictionary m_entryMap; +}; + +} + +#endif // SLANG_SHARED_LIBRARY_H_INCLUDED \ No newline at end of file diff --git a/source/core/slang-string-util.h b/source/core/slang-string-util.h index 47d92f2fe..b365b6cf5 100644 --- a/source/core/slang-string-util.h +++ b/source/core/slang-string-util.h @@ -13,11 +13,11 @@ namespace Slang { /** A blob that uses a `String` for its storage. */ -class StringBlob : public ISlangBlob +class StringBlob : public ISlangBlob, public RefObject { public: // ISlangUnknown - SLANG_IUNKNOWN_ALL + SLANG_REF_OBJECT_IUNKNOWN_ALL // ISlangBlob SLANG_NO_THROW void const* SLANG_MCALL getBufferPointer() SLANG_OVERRIDE { return m_string.Buffer(); } @@ -30,15 +30,9 @@ public: : m_string(string) {} - /// Need virtual dtor, because BlobBase is derived from and release impl used is the one in the base class (that doesn't know the derived type) - /// Alternatively could be implemented by always using SLANG_IUNKNOWN_RELEASE in derived types - this would make derived types slightly smaller/faster - virtual ~StringBlob() {} - protected: ISlangUnknown* getInterface(const Guid& guid); - String m_string; - uint32_t m_refCount = 0; }; struct StringUtil @@ -62,7 +56,6 @@ struct StringUtil /// Create a blob from a string static ComPtr createStringBlob(const String& string); - }; } // namespace Slang diff --git a/source/core/smart-pointer.h b/source/core/smart-pointer.h index abd7763ee..dd00acabd 100644 --- a/source/core/smart-pointer.h +++ b/source/core/smart-pointer.h @@ -32,23 +32,25 @@ namespace Slang virtual ~RefObject() {} - void addReference() + UInt addReference() { - referenceCount++; + return ++referenceCount; } - void decreaseReference() + UInt decreaseReference() { - --referenceCount; + return --referenceCount; } - void releaseReference() + UInt releaseReference() { SLANG_ASSERT(referenceCount != 0); if(--referenceCount == 0) { delete this; + return 0; } + return referenceCount; } bool isUniquelyReferenced() -- cgit v1.2.3