yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
b118451e3
master
1// slang-fxc-compiler.cpp 2#include "slang-fxc-compiler.h" 3 4#if SLANG_ENABLE_DXBC_SUPPORT 5 6#include "../core/slang-blob.h" 7#include "../core/slang-char-util.h" 8#include "../core/slang-common.h" 9#include "../core/slang-io.h" 10#include "../core/slang-semantic-version.h" 11#include "../core/slang-shared-library.h" 12#include "../core/slang-string-slice-pool.h" 13#include "../core/slang-string-util.h" 14#include "slang-artifact-associated-impl.h" 15#include "slang-artifact-desc-util.h" 16#include "slang-artifact-diagnostic-util.h" 17#include "slang-com-helper.h" 18#include "slang-include-system.h" 19#include "slang-source-loc.h" 20 21// Enable calling through to `fxc` or `dxc` to 22// generate code on Windows. 23#ifdef _WIN32 24#include <d3dcompiler.h> 25#include <windows.h> 26#endif 27 28// Some of the `D3DCOMPILE_*` constants aren't available in all 29// versions of `d3dcompiler.h`, so we define them here just in case 30#ifndef D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES 31#define D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES (1 << 20) 32#endif 33 34#ifndef D3DCOMPILE_ALL_RESOURCES_BOUND 35#define D3DCOMPILE_ALL_RESOURCES_BOUND (1 << 21) 36#endif 37 38#endif // SLANG_ENABLE_DXBC_SUPPORT 39 40namespace Slang 41{ 42 43#if SLANG_ENABLE_DXBC_SUPPORT 44 45static UnownedStringSlice _getSlice (ID3DBlob * blob ) 46{ 47return StringUtil ::getSlice ((ISlangBlob * )blob ); 48} 49 50struct FxcIncludeHandler :ID3DInclude 51{ 52 53STDMETHOD (Open ) 54 (D3D_INCLUDE_TYPE includeType , 55LPCSTR fileName , 56LPCVOID parentData , 57LPCVOID * outData , 58UINT * outSize )override 59 { 60SLANG_UNUSED (includeType ); 61// NOTE! The pParentData means the *text* of any previous include. 62// In order to work out what *path* that came from, we need to seach which source file it 63// came from, and use it's path 64 65// Assume the root pathInfo initially 66PathInfo includedFromPathInfo = m_rootPathInfo ; 67 68// Lets try and find the parent source if there is any 69if (parentData ) 70 { 71SourceFile * foundSourceFile = 72m_system .getSourceManager ()-> findSourceFileByContentRecursively ( 73 (const char * )parentData ); 74if (foundSourceFile ) 75 { 76includedFromPathInfo = foundSourceFile -> getPathInfo (); 77 } 78 } 79 80String path (fileName ); 81PathInfo pathInfo ; 82ComPtr < ISlangBlob > blob ; 83 84SLANG_RETURN_ON_FAIL ( 85m_system .findAndLoadFile (path ,includedFromPathInfo .foundPath ,pathInfo ,blob )); 86 87// Return the data 88* outData = blob -> getBufferPointer (); 89* outSize = (UINT )blob -> getBufferSize (); 90 91return S_OK ; 92 } 93 94STDMETHOD (Close )(LPCVOID pData )override 95 { 96SLANG_UNUSED (pData ); 97return S_OK ; 98 } 99FxcIncludeHandler ( 100SearchDirectoryList * searchDirectories , 101ISlangFileSystemExt * fileSystemExt , 102SourceManager * sourceManager ) 103 :m_system (searchDirectories ,fileSystemExt ,sourceManager ) 104 { 105 } 106 107PathInfo m_rootPathInfo ; 108IncludeSystem m_system ; 109}; 110 111class FXCDownstreamCompiler :public DownstreamCompilerBase 112{ 113public : 114typedef DownstreamCompilerBase Super ; 115 116// IDownstreamCompiler 117virtual SLANG_NO_THROW SlangResult SLANG_MCALL 118compile (const CompileOptions & options ,IArtifact ** outArtifact )SLANG_OVERRIDE ; 119virtual SLANG_NO_THROW bool SLANG_MCALL 120canConvert (const ArtifactDesc & from ,const ArtifactDesc & to )SLANG_OVERRIDE ; 121virtual SLANG_NO_THROW SlangResult SLANG_MCALL 122convert (IArtifact * from ,const ArtifactDesc & to ,IArtifact ** outArtifact )SLANG_OVERRIDE ; 123virtual SLANG_NO_THROW bool SLANG_MCALL isFileBased ()SLANG_OVERRIDE {return false; } 124 125/// Must be called before use 126SlangResult init (ISlangSharedLibrary * library ); 127 128FXCDownstreamCompiler () {} 129 130protected : 131pD3DCompile m_compile = nullptr ; 132pD3DDisassemble m_disassemble = nullptr ; 133 134ComPtr < ISlangSharedLibrary > m_sharedLibrary ; 135}; 136 137SlangResult FXCDownstreamCompiler ::init (ISlangSharedLibrary * library ) 138{ 139m_compile = (pD3DCompile )library -> findFuncByName ("D3DCompile" ); 140m_disassemble = (pD3DDisassemble )library -> findFuncByName ("D3DDisassemble" ); 141 142if (!m_compile || !m_disassemble ) 143 { 144return SLANG_FAIL ; 145 } 146 147m_sharedLibrary = library ; 148 149// It's not clear how to query for a version, but we can get a version number from the header 150m_desc = Desc (SLANG_PASS_THROUGH_FXC ,D3D_COMPILER_VERSION ); 151 152return SLANG_OK ; 153} 154 155static SlangResult _parseDiagnosticLine ( 156SliceAllocator & allocator , 157const UnownedStringSlice & line , 158List < UnownedStringSlice >& lineSlices , 159ArtifactDiagnostic & outDiagnostic ) 160{ 161/* tests/diagnostics/syntax-error-intrinsic.slang(14,2): error X3000: syntax error: unexpected 162* token '@' */ 163if (lineSlices .getCount ()< 3 ) 164 { 165return SLANG_FAIL ; 166 } 167 168SLANG_RETURN_ON_FAIL ( 169ArtifactDiagnosticUtil ::splitPathLocation (allocator ,lineSlices [0 ],outDiagnostic )); 170 171 { 172const UnownedStringSlice severityAndCodeSlice = lineSlices [1 ].trim (); 173const UnownedStringSlice severitySlice = 174StringUtil ::getAtInSplit (severityAndCodeSlice ,' ' ,0 ); 175 176outDiagnostic .code = 177allocator .allocate (StringUtil ::getAtInSplit (severityAndCodeSlice ,' ' ,1 )); 178 179outDiagnostic .severity = ArtifactDiagnostic ::Severity ::Error ; 180if (severitySlice == "warning" ) 181 { 182outDiagnostic .severity = ArtifactDiagnostic ::Severity ::Warning ; 183 } 184 } 185 186outDiagnostic .text = allocator .allocate (lineSlices [2 ].begin (),line .end ()); 187return SLANG_OK ; 188} 189 190SlangResult FXCDownstreamCompiler ::compile (const CompileOptions & inOptions ,IArtifact ** outArtifact ) 191{ 192if (!isVersionCompatible (inOptions )) 193 { 194// Not possible to compile with this version of the interface. 195return SLANG_E_NOT_IMPLEMENTED ; 196 } 197 198CompileOptions options = getCompatibleVersion (& inOptions ); 199 200// This compiler can only deal with a single source artifact 201if (options .sourceArtifacts .count != 1 ) 202 { 203return SLANG_FAIL ; 204 } 205 206IArtifact * sourceArtifact = options .sourceArtifacts [0 ]; 207 208if (options .sourceLanguage != SLANG_SOURCE_LANGUAGE_HLSL || options .targetType != SLANG_DXBC ) 209 { 210SLANG_ASSERT (!"Can only compile HLSL to DXBC" ); 211return SLANG_FAIL ; 212 } 213 214// If we have been invoked in a pass-through mode, then we need to make sure 215// that the downstream compiler sees whatever options were passed to Slang 216// via the command line or API. 217// 218// TODO: more pieces of information should be added here as needed. 219// 220 221SearchDirectoryList searchDirectories ; 222for (const auto & includePath :options .includePaths ) 223 { 224searchDirectories .searchDirectories .add (asString (includePath )); 225 } 226 227const auto sourcePath = ArtifactUtil ::findPath (sourceArtifact ); 228 229// Use the default fileSystemExt is not set 230ID3DInclude * includeHandler = nullptr ; 231 232FxcIncludeHandler fxcIncludeHandlerStorage ( 233& searchDirectories , 234options .fileSystemExt , 235options .sourceManager ); 236if (options .fileSystemExt ) 237 { 238 239if (sourcePath .getLength ()> 0 ) 240 { 241fxcIncludeHandlerStorage .m_rootPathInfo = PathInfo ::makePath (sourcePath ); 242 } 243includeHandler = & fxcIncludeHandlerStorage ; 244 } 245 246List < D3D_SHADER_MACRO > dxMacrosStorage ; 247D3D_SHADER_MACRO const * dxMacros = nullptr ; 248 249if (options .defines .count > 0 ) 250 { 251for (const auto & define :options .defines ) 252 { 253D3D_SHADER_MACRO dxMacro ; 254dxMacro .Name = define .nameWithSig ; 255dxMacro .Definition = define .value ; 256dxMacrosStorage .add (dxMacro ); 257 } 258D3D_SHADER_MACRO nullTerminator = {0 ,0 }; 259dxMacrosStorage .add (nullTerminator ); 260 261dxMacros = dxMacrosStorage .getBuffer (); 262 } 263 264DWORD flags = 0 ; 265 266switch (options .floatingPointMode ) 267 { 268default : 269break ; 270 271case FloatingPointMode ::Precise : 272flags |=D3DCOMPILE_IEEE_STRICTNESS ; 273break ; 274 } 275 276flags |=D3DCOMPILE_ENABLE_STRICTNESS ; 277flags |=D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES ; 278 279switch (options .optimizationLevel ) 280 { 281default : 282break ; 283 284case OptimizationLevel ::None : 285flags |=D3DCOMPILE_OPTIMIZATION_LEVEL0 ; 286break ; 287case OptimizationLevel ::Default : 288flags |=D3DCOMPILE_OPTIMIZATION_LEVEL1 ; 289break ; 290case OptimizationLevel ::High : 291flags |=D3DCOMPILE_OPTIMIZATION_LEVEL2 ; 292break ; 293case OptimizationLevel ::Maximal : 294flags |=D3DCOMPILE_OPTIMIZATION_LEVEL3 ; 295break ; 296 } 297 298switch (options .debugInfoType ) 299 { 300case DebugInfoType ::None : 301break ; 302 303default : 304flags |=D3DCOMPILE_DEBUG ; 305break ; 306 } 307 308ComPtr < ISlangBlob > sourceBlob ; 309SLANG_RETURN_ON_FAIL (sourceArtifact -> loadBlob (ArtifactKeep ::Yes ,sourceBlob .writeRef ())); 310 311ComPtr < ID3DBlob > codeBlob ; 312ComPtr < ID3DBlob > diagnosticsBlob ; 313HRESULT hr = m_compile ( 314sourceBlob -> getBufferPointer (), 315sourceBlob -> getBufferSize (), 316String (sourcePath ).getBuffer (), 317dxMacros , 318includeHandler , 319options .entryPointName , 320options .profileName , 321flags , 3220 ,// unused: effect flags 323codeBlob .writeRef (), 324diagnosticsBlob .writeRef ()); 325 326auto diagnostics = ArtifactDiagnostics ::create (); 327 328// HRESULT is compatible with SlangResult 329diagnostics -> setResult (hr ); 330 331SliceAllocator allocator ; 332 333if (diagnosticsBlob ) 334 { 335UnownedStringSlice diagnosticText = _getSlice (diagnosticsBlob ); 336diagnostics -> setRaw (asCharSlice (diagnosticText )); 337 338SlangResult diagnosticParseRes = ArtifactDiagnosticUtil ::parseColonDelimitedDiagnostics ( 339allocator , 340diagnosticText , 3410 , 342_parseDiagnosticLine , 343diagnostics ); 344SLANG_UNUSED (diagnosticParseRes ); 345SLANG_ASSERT (SLANG_SUCCEEDED (diagnosticParseRes )); 346 } 347 348// If FXC failed, make sure we have an error in the diagnostics 349if (FAILED (hr )) 350 { 351diagnostics -> requireErrorDiagnostic (); 352 } 353 354auto artifact = ArtifactUtil ::createArtifactForCompileTarget (options .targetType ); 355 356ArtifactUtil ::addAssociated (artifact ,diagnostics ); 357 358if (codeBlob ) 359 { 360// ID3DBlob is compatible with ISlangBlob, so just cast away... 361artifact -> addRepresentationUnknown ((ISlangBlob * )codeBlob .get ()); 362 } 363 364* outArtifact = artifact .detach (); 365return SLANG_OK ; 366} 367 368bool FXCDownstreamCompiler ::canConvert (const ArtifactDesc & from ,const ArtifactDesc & to ) 369{ 370// Can only disassemble blobs that are DXBC 371return ArtifactDescUtil ::isDisassembly (from ,to )&& from .payload == ArtifactPayload ::DXBC ; 372} 373 374SlangResult FXCDownstreamCompiler ::convert ( 375IArtifact * from , 376const ArtifactDesc & to , 377IArtifact ** outArtifact ) 378{ 379if (!canConvert (from -> getDesc (),to )) 380 { 381return SLANG_FAIL ; 382 } 383 384ComPtr < ISlangBlob > dxbcBlob ; 385SLANG_RETURN_ON_FAIL (from -> loadBlob (ArtifactKeep ::No ,dxbcBlob .writeRef ())); 386 387ComPtr < ID3DBlob > disassemblyBlob ; 388SLANG_RETURN_ON_FAIL (m_disassemble ( 389dxbcBlob -> getBufferPointer (), 390dxbcBlob -> getBufferSize (), 3910 , 392nullptr , 393disassemblyBlob .writeRef ())); 394 395auto artifact = ArtifactUtil ::createArtifact (to ); 396// ISlangBlob is compatible with ID3DBlob 397artifact -> addRepresentationUnknown ((ISlangBlob * )disassemblyBlob .get ()); 398 399* outArtifact = artifact .detach (); 400return SLANG_OK ; 401} 402 403/* static */ SlangResult FXCDownstreamCompilerUtil ::locateCompilers ( 404const String & path , 405ISlangSharedLibraryLoader * loader , 406DownstreamCompilerSet * set ) 407{ 408ComPtr < ISlangSharedLibrary > library ; 409 410const char * const libName = "d3dcompiler_47" ; 411SLANG_RETURN_ON_FAIL ( 412DownstreamCompilerUtil ::loadSharedLibrary (path ,loader ,nullptr ,libName ,library )); 413 414SLANG_ASSERT (library ); 415if (!library ) 416 { 417return SLANG_FAIL ; 418 } 419 420auto compiler = new FXCDownstreamCompiler ; 421ComPtr < IDownstreamCompiler > compilerInft (compiler ); 422 423SLANG_RETURN_ON_FAIL (compiler -> init (library )); 424 425set -> addCompiler (compilerInft ); 426return SLANG_OK ; 427} 428 429#else // SLANG_ENABLE_DXBC_SUPPORT 430 431/* static */ SlangResult FXCDownstreamCompilerUtil ::locateCompilers ( 432const String & path , 433ISlangSharedLibraryLoader * loader , 434DownstreamCompilerSet * set ) 435{ 436SLANG_UNUSED (path ); 437SLANG_UNUSED (loader ); 438SLANG_UNUSED (set ); 439return SLANG_E_NOT_AVAILABLE ; 440} 441 442#endif // else SLANG_ENABLE_DXBC_SUPPORT 443 444}// namespace Slang