yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
c8d189bd5
master
1// slang-platform.cpp 2 3#define _CRT_SECURE_NO_WARNINGS 4 5#include "slang-platform.h" 6 7#include "slang-common.h" 8#include "slang-io.h" 9 10#ifdef _WIN32 11#include <windows.h> 12#else 13#include "slang-string.h" 14 15#include <dlfcn.h> 16#endif 17 18#if SLANG_HAS_BACKTRACE 19#include <execinfo.h> 20#endif 21 22#if SLANG_LINUX_FAMILY 23#include <limits.h> 24#include <unistd.h> 25#endif 26 27namespace Slang 28{ 29// SharedLibrary 30 31/* static */ SlangResult SharedLibrary ::load (const char * path ,SharedLibrary ::Handle & handleOut ) 32{ 33StringBuilder builder ; 34calcPlatformPath (UnownedStringSlice (path ),builder ); 35return loadWithPlatformPath (builder .begin (),handleOut ); 36} 37 38/* static */ void SharedLibrary ::calcPlatformPath ( 39const UnownedStringSlice & path , 40StringBuilder & outPath ) 41{ 42// Work out the shared library name 43String parent = Path ::getParentDirectory (path ); 44String filename = Path ::getFileName (path ); 45 46if (parent .getLength ()> 0 ) 47 { 48// Work out the filename platform name (as in add .dll say on windows) 49StringBuilder platformFileNameBuilder ; 50SharedLibrary ::appendPlatformFileName (filename .getUnownedSlice (),platformFileNameBuilder ); 51 52Path ::combineIntoBuilder ( 53parent .getUnownedSlice (), 54platformFileNameBuilder .getUnownedSlice (), 55outPath ); 56 } 57else if (filename .getLength ()> 0 ) 58 { 59appendPlatformFileName (filename .getUnownedSlice (),outPath ); 60 } 61} 62 63/* static */ String SharedLibrary ::calcPlatformPath (const UnownedStringSlice & path ) 64{ 65StringBuilder builder ; 66calcPlatformPath (path ,builder ); 67return builder .toString (); 68} 69 70#ifdef _WIN32 71 72// Make sure SlangResult match for common standard window HRESULT 73SLANG_COMPILE_TIME_ASSERT (E_FAIL == SLANG_FAIL ); 74SLANG_COMPILE_TIME_ASSERT (E_NOINTERFACE == SLANG_E_NO_INTERFACE ); 75SLANG_COMPILE_TIME_ASSERT (E_HANDLE == SLANG_E_INVALID_HANDLE ); 76SLANG_COMPILE_TIME_ASSERT (E_NOTIMPL == SLANG_E_NOT_IMPLEMENTED ); 77SLANG_COMPILE_TIME_ASSERT (E_INVALIDARG == SLANG_E_INVALID_ARG ); 78SLANG_COMPILE_TIME_ASSERT (E_OUTOFMEMORY == SLANG_E_OUT_OF_MEMORY ); 79 80/* static */ SlangResult PlatformUtil ::getInstancePath (StringBuilder & out ) 81{ 82wchar_t path [_MAX_PATH ]; 83 ::GetModuleFileName (::GetModuleHandle (NULL ),path ,SLANG_COUNT_OF (path )); 84String pathString = String ::fromWString (path ); 85 86// We don't want the instance name, just the path to it 87out .clear (); 88out .append (Path ::getParentDirectory (pathString )); 89 90return out .getLength ()> 0 ?SLANG_OK :SLANG_FAIL ; 91} 92 93/* static */ SlangResult PlatformUtil ::appendResult (SlangResult res ,StringBuilder & builderOut ) 94{ 95if (SLANG_FAILED (res )&& res != SLANG_FAIL ) 96 { 97LPWSTR buffer = nullptr ; 98FormatMessage ( 99FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ALLOCATE_BUFFER , 100nullptr , 101res , 102MAKELANGID (LANG_NEUTRAL ,SUBLANG_DEFAULT ),// Default language 103 (LPWSTR )& buffer , 1040 , 105nullptr ); 106 107if (buffer ) 108 { 109builderOut <<" " ; 110// Convert to string 111builderOut .append (String ::fromWString (buffer )); 112LocalFree (buffer ); 113return SLANG_OK ; 114 } 115 } 116return SLANG_FAIL ; 117} 118 119/* static */ SlangResult SharedLibrary ::loadWithPlatformPath ( 120char const * platformFileName , 121SharedLibrary ::Handle & handleOut ) 122{ 123handleOut = nullptr ; 124if (!platformFileName || strlen (platformFileName )== 0 ) 125 { 126if (!GetModuleHandleExW (0 ,nullptr , (HMODULE * )& handleOut )) 127return SLANG_FAIL ; 128return SLANG_OK ; 129 } 130 131// We try to search the DLL in two different attempts. 132// First attempt - LoadLibraryExW() 133// If it failed to find one, we will use LoadLibraryW() to search over all PATH. 134// Search order: 1) The directory that contains the DLL (LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR). 135// This directory is searched only for dependencies of the DLL being loaded. 136// 2) Application directory 137// 3) User directories (AddDllDirectory/SetDllDirectory) 138// 4) System32 139// 5) PATH environment variable (by the 2nd attempt with LoadLibraryW()) 140// https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw 141// https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-loadlibraryw 142String platformFileNameStr (platformFileName ); 143OSString wideFileName = platformFileNameStr .toWString (); 144HMODULE handle = LoadLibraryExW (wideFileName ,nullptr ,LOAD_LIBRARY_SEARCH_DEFAULT_DIRS ); 145 146if (!handle ) 147handle = LoadLibraryW (wideFileName ); 148// If still not found, return an error. 149if (!handle ) 150 { 151const DWORD lastError = GetLastError (); 152switch (lastError ) 153 { 154case ERROR_MOD_NOT_FOUND : 155case ERROR_PATH_NOT_FOUND : 156case ERROR_FILE_NOT_FOUND : 157 { 158return SLANG_E_NOT_FOUND ; 159 } 160case ERROR_INVALID_ACCESS : 161case ERROR_ACCESS_DENIED : 162case ERROR_INVALID_DATA : 163 { 164return SLANG_E_CANNOT_OPEN ; 165 } 166default : 167break ; 168 } 169// Turn to Result, if not one of the well known errors 170return HRESULT_FROM_WIN32 (lastError ); 171 } 172handleOut = (Handle )handle ; 173return SLANG_OK ; 174} 175 176/* static */ void SharedLibrary ::unload (Handle handle ) 177{ 178SLANG_ASSERT (handle ); 179 ::FreeLibrary ((HMODULE )handle ); 180} 181 182/* static */ void * SharedLibrary ::findSymbolAddressByName (Handle handle ,char const * name ) 183{ 184SLANG_ASSERT (handle ); 185return reinterpret_cast < void *> (GetProcAddress ((HMODULE )handle ,name )); 186} 187 188/* static */ void SharedLibrary ::appendPlatformFileName ( 189const UnownedStringSlice & name , 190StringBuilder & dst ) 191{ 192dst .append (name ); 193dst .append (".dll" ); 194} 195 196#else // _WIN32 197/* static */ SlangResult PlatformUtil ::getInstancePath ([[maybe_unused ]]StringBuilder & out ) 198{ 199#if defined(__linux__ )|| defined(__CYGWIN__ ) 200char path [PATH_MAX ]; 201ssize_t len = readlink ("/proc/self/exe" ,path ,sizeof (path )- 1 ); 202if (len == -1 ) 203 { 204return SLANG_FAIL ; 205 } 206 207path [len ]= '\0' ; 208String pathString (path ); 209 210// We don't want the instance name, just the path to it 211out .clear (); 212out .append (Path ::getParentDirectory (pathString )); 213 214return out .getLength ()> 0 ?SLANG_OK :SLANG_FAIL ; 215#else 216return SLANG_E_NOT_IMPLEMENTED ; 217#endif 218} 219 220/* static */ SlangResult PlatformUtil ::appendResult ( 221 [[maybe_unused ]]SlangResult res , 222 [[maybe_unused ]]StringBuilder & builderOut ) 223{ 224return SLANG_E_NOT_IMPLEMENTED ; 225} 226 227/* static */ SlangResult SharedLibrary ::loadWithPlatformPath ( 228char const * platformFileName , 229Handle & handleOut ) 230{ 231handleOut = nullptr ; 232// Work around 233// https://github.com/microsoft/DirectXShaderCompiler/issues/5119 and 234// https://github.com/doitsujin/dxvk/issues/3330 235// libdxcompiler.so invokes UB on dlclose, the dxvk libs break GDB when 236// closed 237const auto unclosableLibNames = {"libdxcompiler" ,"libdxvk_d3d11" ,"libdxvk_dxgi" }; 238bool isUnclosable = false; 239for (auto n :unclosableLibNames ) 240 { 241if (strncmp (platformFileName ,n ,strlen (n ))== 0 ) 242 { 243isUnclosable = true; 244break ; 245 } 246 } 247if (strlen (platformFileName )== 0 ) 248platformFileName = nullptr ; 249const auto mode = RTLD_NOW |RTLD_GLOBAL | (isUnclosable ?RTLD_NODELETE :0 ); 250void * h = dlopen (platformFileName ,mode ); 251if (!h ) 252 { 253#if 0 254// We can't output the error message here, because it will cause output when testing what code gen is available 255if (auto msg = dlerror ()) 256 { 257fprintf (stderr ,"error: %s\n" ,msg ); 258 } 259#endif 260return SLANG_FAIL ; 261 } 262handleOut = (Handle )h ; 263return SLANG_OK ; 264} 265 266/* static */ void SharedLibrary ::unload (Handle handle ) 267{ 268SLANG_ASSERT (handle ); 269dlclose (handle ); 270} 271 272/* static */ void * SharedLibrary ::findSymbolAddressByName (Handle handle ,char const * name ) 273{ 274return dlsym ((void * )handle ,name ); 275} 276 277/* static */ void SharedLibrary ::appendPlatformFileName ( 278const UnownedStringSlice & name , 279StringBuilder & dst ) 280{ 281#if __CYGWIN__ 282dst .append (name ); 283dst .append (".dll" ); 284#elif SLANG_APPLE_FAMILY 285dst .append ("lib" ); 286dst .append (name ); 287dst .append (".dylib" ); 288#elif SLANG_LINUX_FAMILY 289if (!name .startsWith ("lib" )) 290dst .append ("lib" ); 291dst .append (name ); 292if (name .indexOf (UnownedStringSlice (".so." ))== -1 ) 293dst .append (".so" ); 294#else 295// Just guess we can do with the name on it's own 296dst .append (name ); 297#endif 298} 299 300#endif // _WIN32 301 302 303/* static */ SlangResult PlatformUtil ::getEnvironmentVariable ( 304const UnownedStringSlice & name , 305StringBuilder & out ) 306{ 307const char * value = getenv (String (name ).getBuffer ()); 308if (value ) 309 { 310out .append (value ); 311return SLANG_OK ; 312 } 313return SLANG_E_NOT_FOUND ; 314} 315 316/* static */ PlatformKind PlatformUtil ::getPlatformKind () 317{ 318#if SLANG_WINRT 319return PlatformKind ::WinRT ; 320#elif SLANG_XBOXONE 321return PlatformKind ::XBoxOne ; 322#elif SLANG_WIN64 323return PlatformKind ::Win64 ; 324#elif SLANG_X360 325return PlatformKind ::X360 ; 326#elif SLANG_WIN32 327return PlatformKind ::Win32 ; 328#elif SLANG_ANDROID 329return PlatformKind ::Android ; 330#elif SLANG_LINUX 331return PlatformKind ::Linux ; 332#elif SLANG_IOS 333return PlatformKind ::IOS ; 334#elif SLANG_OSX 335return PlatformKind ::OSX ; 336#elif SLANG_PS3 337return PlatformKind ::PS3 ; 338#elif SLANG_SLANG_PS4 339return PlatformKind ::PS4 ; 340#elif SLANG_PSP2 341return PlatformKind ::PSP2 ; 342#elif SLANG_WIIU 343return PlatformKind ::WIIU ; 344#else 345return PlatformKind ::Unknown ; 346#endif 347} 348 349static const PlatformFlags s_familyFlags [int (PlatformFamily ::CountOf )]= { 3500 ,// Unknown 351PlatformFlag ::WinRT |PlatformFlag ::Win32 |PlatformFlag ::Win64 ,// Windows 352PlatformFlag ::WinRT |PlatformFlag ::Win32 |PlatformFlag ::Win64 |PlatformFlag ::X360 | 353PlatformFlag ::XBoxOne ,// Microsoft 354PlatformFlag ::Linux |PlatformFlag ::Android ,// Linux 355PlatformFlag ::IOS |PlatformFlag ::OSX ,// Apple 356PlatformFlag ::Linux |PlatformFlag ::Android |PlatformFlag ::IOS |PlatformFlag ::OSX ,// Unix 357}; 358 359/* static */ PlatformFlags PlatformUtil ::getPlatformFlags (PlatformFamily family ) 360{ 361return s_familyFlags [int (family )]; 362} 363 364/* static */ SlangResult PlatformUtil ::outputDebugMessage ([[maybe_unused ]]const char * text ) 365{ 366#ifdef _WIN32 367String textStr (text ); 368OutputDebugStringW (textStr .toWString ()); 369return SLANG_OK ; 370#else 371return SLANG_E_NOT_AVAILABLE ; 372#endif 373} 374 375/* static */ void PlatformUtil ::backtrace () 376{ 377#if SLANG_HAS_BACKTRACE 378// Print stack trace for debugging assistance 379void * stackTrace [64 ]; 380int stackDepth = ::backtrace (stackTrace ,64 ); 381char ** symbols = ::backtrace_symbols (stackTrace ,stackDepth ); 382if (symbols ) 383 { 384for (int i = 0 ;i < stackDepth ;++ i ) 385 { 386fprintf (stdout ,"%s\n" ,symbols [i ]); 387 } 388free (symbols ); 389 } 390fprintf (stdout ,"\n" ); 391#else 392fprintf (stdout ,"Stack trace not available on this platform.\n" ); 393#endif 394} 395 396}// namespace Slang