yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Ellie HermaszewskaMove switch statement bodies to their own lines (#5493)b118451e3

master
16.9 KiB520 linesraw
1// slang-downstream-compiler.cpp
2#include "slang-downstream-compiler-util.h"
3
4#include "../core/slang-blob.h"
5#include "../core/slang-char-util.h"
6#include "../core/slang-common.h"
7#include "../core/slang-io.h"
8#include "../core/slang-shared-library.h"
9#include "../core/slang-string-util.h"
10#include "../core/slang-type-text-util.h"
11#include "slang-com-helper.h"
12
13#ifdef SLANG_VC
14#include "windows/slang-win-visual-studio-util.h"
15#endif
16
17#include "slang-dxc-compiler.h"
18#include "slang-fxc-compiler.h"
19#include "slang-gcc-compiler-util.h"
20#include "slang-glslang-compiler.h"
21#include "slang-llvm-compiler.h"
22#include "slang-metal-compiler.h"
23#include "slang-nvrtc-compiler.h"
24#include "slang-tint-compiler.h"
25#include "slang-visual-studio-compiler-util.h"
26
27namespace Slang
28{
29
30/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompilerInfos !!!!!!!!!!!!!!!!!!!!!!*/
31
32struct DownstreamCompilerInfos
33{
34    DownstreamCompilerInfo infos[int(SLANG_PASS_THROUGH_COUNT_OF)];
35
36    static DownstreamCompilerInfos _calcInfos();
37    static DownstreamCompilerInfos s_infos;
38};
39
40/* static */ DownstreamCompilerInfos DownstreamCompilerInfos::_calcInfos()
41{
42    typedef DownstreamCompilerInfo Info;
43    typedef Info::SourceLanguageFlag SourceLanguageFlag;
44
45    DownstreamCompilerInfos infos;
46
47    infos.infos[int(SLANG_PASS_THROUGH_CLANG)] =
48        Info(SourceLanguageFlag::CPP | SourceLanguageFlag::C);
49    infos.infos[int(SLANG_PASS_THROUGH_VISUAL_STUDIO)] =
50        Info(SourceLanguageFlag::CPP | SourceLanguageFlag::C);
51    infos.infos[int(SLANG_PASS_THROUGH_GCC)] =
52        Info(SourceLanguageFlag::CPP | SourceLanguageFlag::C);
53    infos.infos[int(SLANG_PASS_THROUGH_LLVM)] =
54        Info(SourceLanguageFlag::CPP | SourceLanguageFlag::C);
55
56    infos.infos[int(SLANG_PASS_THROUGH_NVRTC)] = Info(SourceLanguageFlag::CUDA);
57
58    infos.infos[int(SLANG_PASS_THROUGH_DXC)] = Info(SourceLanguageFlag::HLSL);
59    infos.infos[int(SLANG_PASS_THROUGH_FXC)] = Info(SourceLanguageFlag::HLSL);
60    infos.infos[int(SLANG_PASS_THROUGH_GLSLANG)] = Info(SourceLanguageFlag::GLSL);
61    infos.infos[int(SLANG_PASS_THROUGH_SPIRV_OPT)] = Info(SourceLanguageFlag::SPIRV);
62
63    return infos;
64}
65
66/* static */ DownstreamCompilerInfos DownstreamCompilerInfos::s_infos =
67    DownstreamCompilerInfos::_calcInfos();
68
69/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompilerInfo !!!!!!!!!!!!!!!!!!!!!!*/
70
71/* static */ const DownstreamCompilerInfo& DownstreamCompilerInfo::getInfo(
72    SlangPassThrough compiler)
73{
74    return DownstreamCompilerInfos::s_infos.infos[int(compiler)];
75}
76
77/* static */ bool DownstreamCompilerInfo::canCompile(
78    SlangPassThrough compiler,
79    SlangSourceLanguage sourceLanguage)
80{
81    const auto& info = getInfo(compiler);
82    return (info.sourceLanguageFlags & (SourceLanguageFlags(1) << int(sourceLanguage))) != 0;
83}
84
85/* !!!!!!!!!!!!!!!!!!!!!!!!! DownstreamCompilerUtil !!!!!!!!!!!!!!!!!!!!!!*/
86
87static DownstreamCompilerMatchVersion _calcCompiledVersion()
88{
89    DownstreamCompilerMatchVersion matchVersion;
90
91#if SLANG_VC
92    matchVersion = WinVisualStudioUtil::getCompiledVersion();
93#elif SLANG_CLANG
94    matchVersion.type = SLANG_PASS_THROUGH_CLANG;
95    matchVersion.matchVersion.set(Index(__clang_major__), Index(__clang_minor__));
96#elif SLANG_GCC
97    matchVersion.type = SLANG_PASS_THROUGH_GCC;
98    matchVersion.matchVersion.set(Index(__GNUC__), Index(__GNUC_MINOR__));
99#else
100    // TODO(JS): Hmmm None is not quite the same as unknown. It works for now, but we might want to
101    // have a distinct enum for unknown.
102    matchVersion.type = SLANG_PASS_THROUGH_NONE;
103#endif
104
105    return matchVersion;
106}
107
108
109DownstreamCompilerMatchVersion DownstreamCompilerUtil::getCompiledVersion()
110{
111    static DownstreamCompilerMatchVersion s_version = _calcCompiledVersion();
112    return s_version;
113}
114
115/* static */ IDownstreamCompiler* DownstreamCompilerUtil::findCompiler(
116    const DownstreamCompilerSet* set,
117    MatchType matchType,
118    const DownstreamCompilerDesc& desc)
119{
120    List<IDownstreamCompiler*> compilers;
121    set->getCompilers(compilers);
122    return findCompiler(compilers, matchType, desc);
123}
124
125/* static */ IDownstreamCompiler* DownstreamCompilerUtil::findCompiler(
126    const List<IDownstreamCompiler*>& compilers,
127    MatchType matchType,
128    const DownstreamCompilerDesc& desc)
129{
130    if (compilers.getCount() <= 0)
131    {
132        return nullptr;
133    }
134
135    Int bestIndex = -1;
136
137    const SlangPassThrough compilerType = desc.type;
138
139    Int maxVersionValue = 0;
140    Int minVersionDiff = 0x7fffffff;
141
142    Int descVersionValue = desc.getVersionValue();
143
144    // If we don't have version set, then anything 0 or above is good enough, and just take newest
145    if (descVersionValue == 0)
146    {
147        maxVersionValue = -1;
148        matchType = MatchType::Newest;
149    }
150
151    for (Index i = 0; i < compilers.getCount(); ++i)
152    {
153        IDownstreamCompiler* compiler = compilers[i];
154        auto compilerDesc = compiler->getDesc();
155
156        if (compilerType == compilerDesc.type)
157        {
158            const Int versionValue = compilerDesc.getVersionValue();
159            switch (matchType)
160            {
161            case MatchType::MinGreaterEqual:
162                {
163                    auto diff = descVersionValue - versionValue;
164                    if (diff >= 0 && diff < minVersionDiff)
165                    {
166                        bestIndex = i;
167                        minVersionDiff = diff;
168                    }
169                    break;
170                }
171            case MatchType::MinAbsolute:
172                {
173                    auto diff = descVersionValue - versionValue;
174                    diff = (diff >= 0) ? diff : -diff;
175                    if (diff < minVersionDiff)
176                    {
177                        bestIndex = i;
178                        minVersionDiff = diff;
179                    }
180                    break;
181                }
182            case MatchType::Newest:
183                {
184                    if (versionValue > maxVersionValue)
185                    {
186                        maxVersionValue = versionValue;
187                        bestIndex = i;
188                    }
189                    break;
190                }
191            }
192        }
193    }
194
195    return (bestIndex >= 0) ? compilers[bestIndex] : nullptr;
196}
197
198/* static */ IDownstreamCompiler* DownstreamCompilerUtil::findCompiler(
199    const List<IDownstreamCompiler*>& compilers,
200    const DownstreamCompilerDesc& desc)
201{
202    for (auto compiler : compilers)
203    {
204        if (compiler->getDesc() == desc)
205        {
206            return compiler;
207        }
208    }
209    return nullptr;
210}
211
212/* static */ IDownstreamCompiler* DownstreamCompilerUtil::findCompiler(
213    const List<IDownstreamCompiler*>& compilers,
214    SlangPassThrough type,
215    const SemanticVersion& version)
216{
217    DownstreamCompilerDesc desc;
218    desc.type = type;
219    desc.version = version;
220    return findCompiler(compilers, desc);
221}
222
223/* static */ void DownstreamCompilerUtil::findVersions(
224    const List<IDownstreamCompiler*>& compilers,
225    SlangPassThrough type,
226    List<SemanticVersion>& outVersions)
227{
228    for (auto compiler : compilers)
229    {
230        auto desc = compiler->getDesc();
231
232        if (desc.type == type)
233        {
234            outVersions.add(desc.version);
235        }
236    }
237}
238
239/* static */ IDownstreamCompiler* DownstreamCompilerUtil::findClosestCompiler(
240    const List<IDownstreamCompiler*>& compilers,
241    const DownstreamCompilerMatchVersion& matchVersion)
242{
243    List<SemanticVersion> versions;
244
245    findVersions(compilers, matchVersion.type, versions);
246
247    if (versions.getCount() > 0)
248    {
249        if (versions.getCount() == 1)
250        {
251            // Must be that one
252            return findCompiler(compilers, matchVersion.type, versions[0]);
253        }
254
255        // Okay lets find the best one
256        auto bestVersion = MatchSemanticVersion::findAnyBest(
257            versions.getBuffer(),
258            versions.getCount(),
259            matchVersion.matchVersion);
260
261        // If one is found use it
262        if (bestVersion.isSet())
263        {
264            return findCompiler(compilers, matchVersion.type, bestVersion);
265        }
266    }
267
268    {
269        // TODO(JS):
270        // NOTE! This may not really be appropriate, because LLVM is *not* interchangable with
271        // a 'normal' C++ compiler as cannot access standard libraries/headers.
272        // So `slang-llvm` can't be used for 'host' code.
273
274        // These compilers should be usable interchangably. The order is important, as the first one
275        // that matches will be used, so LLVM is used before CLANG or GCC if appropriate
276        const SlangPassThrough compatiblePassThroughs[] = {
277            SLANG_PASS_THROUGH_LLVM,
278            SLANG_PASS_THROUGH_CLANG,
279            SLANG_PASS_THROUGH_GCC,
280        };
281
282        // Check the version is one of the compatible types
283        if (makeConstArrayView(compatiblePassThroughs).indexOf(matchVersion.type) >= 0)
284        {
285            // Try each compatible type in turn
286            for (auto passThrough : compatiblePassThroughs)
287            {
288                versions.clear();
289                findVersions(compilers, passThrough, versions);
290
291                if (versions.getCount() > 0)
292                {
293                    // Get the latest version (as we have no way to really compare)
294                    auto latestVersion =
295                        SemanticVersion::getLatest(versions.getBuffer(), versions.getCount());
296                    return findCompiler(compilers, matchVersion.type, latestVersion);
297                }
298            }
299        }
300    }
301
302    return nullptr;
303}
304
305/* static */ IDownstreamCompiler* DownstreamCompilerUtil::findClosestCompiler(
306    const DownstreamCompilerSet* set,
307    const DownstreamCompilerMatchVersion& matchVersion)
308{
309    List<IDownstreamCompiler*> compilers;
310    set->getCompilers(compilers);
311    return findClosestCompiler(compilers, matchVersion);
312}
313
314/* static */ void DownstreamCompilerUtil::updateDefault(
315    DownstreamCompilerSet* set,
316    SlangSourceLanguage sourceLanguage)
317{
318    IDownstreamCompiler* compiler = nullptr;
319
320    switch (sourceLanguage)
321    {
322    case SLANG_SOURCE_LANGUAGE_CPP:
323    case SLANG_SOURCE_LANGUAGE_C:
324        {
325            // Find the compiler closest to the compiler this was compiled with
326            if (!compiler)
327            {
328                compiler = findClosestCompiler(set, getCompiledVersion());
329            }
330            break;
331        }
332    case SLANG_SOURCE_LANGUAGE_CUDA:
333        {
334            DownstreamCompilerDesc desc;
335            desc.type = SLANG_PASS_THROUGH_NVRTC;
336            compiler = findCompiler(set, MatchType::Newest, desc);
337            break;
338        }
339    default:
340        break;
341    }
342
343    set->setDefaultCompiler(sourceLanguage, compiler);
344}
345
346/* static */ void DownstreamCompilerUtil::updateDefaults(DownstreamCompilerSet* set)
347{
348    for (Index i = 0; i < Index(SLANG_SOURCE_LANGUAGE_COUNT_OF); ++i)
349    {
350        updateDefault(set, SlangSourceLanguage(i));
351    }
352}
353
354/* static */ void DownstreamCompilerUtil::setDefaultLocators(
355    DownstreamCompilerLocatorFunc outFuncs[int(SLANG_PASS_THROUGH_COUNT_OF)])
356{
357    outFuncs[int(SLANG_PASS_THROUGH_VISUAL_STUDIO)] = &VisualStudioCompilerUtil::locateCompilers;
358    outFuncs[int(SLANG_PASS_THROUGH_CLANG)] = &GCCDownstreamCompilerUtil::locateClangCompilers;
359    outFuncs[int(SLANG_PASS_THROUGH_GCC)] = &GCCDownstreamCompilerUtil::locateGCCCompilers;
360    outFuncs[int(SLANG_PASS_THROUGH_NVRTC)] = &NVRTCDownstreamCompilerUtil::locateCompilers;
361    outFuncs[int(SLANG_PASS_THROUGH_DXC)] = &DXCDownstreamCompilerUtil::locateCompilers;
362    outFuncs[int(SLANG_PASS_THROUGH_FXC)] = &FXCDownstreamCompilerUtil::locateCompilers;
363    outFuncs[int(SLANG_PASS_THROUGH_GLSLANG)] = &GlslangDownstreamCompilerUtil::locateCompilers;
364    outFuncs[int(SLANG_PASS_THROUGH_SPIRV_OPT)] = &SpirvOptDownstreamCompilerUtil::locateCompilers;
365    outFuncs[int(SLANG_PASS_THROUGH_LLVM)] = &LLVMDownstreamCompilerUtil::locateCompilers;
366    outFuncs[int(SLANG_PASS_THROUGH_SPIRV_DIS)] = &SpirvDisDownstreamCompilerUtil::locateCompilers;
367    outFuncs[int(SLANG_PASS_THROUGH_METAL)] = &MetalDownstreamCompilerUtil::locateCompilers;
368    outFuncs[int(SLANG_PASS_THROUGH_TINT)] = &TintDownstreamCompilerUtil::locateCompilers;
369}
370
371static String _getParentPath(const String& path)
372{
373    // If we can get the canonical path, we'll do that before getting the parent
374    String canonicalPath;
375    if (SLANG_SUCCEEDED(Path::getCanonical(path, canonicalPath)))
376    {
377        return Path::getParentDirectory(canonicalPath);
378    }
379    else
380    {
381        return Path::getParentDirectory(path);
382    }
383}
384
385static SlangResult _findPaths(
386    const String& path,
387    const char* libraryName,
388    String& outParentPath,
389    String& outLibraryPath)
390{
391    // Try to determine what the path is by looking up the path type
392    SlangPathType pathType;
393    if (SLANG_SUCCEEDED(Path::getPathType(path, &pathType)))
394    {
395        if (pathType == SLANG_PATH_TYPE_DIRECTORY)
396        {
397            outParentPath = path;
398            outLibraryPath = Path::combine(outParentPath, libraryName);
399        }
400        else
401        {
402            SLANG_ASSERT(pathType == SLANG_PATH_TYPE_FILE);
403
404            outParentPath = _getParentPath(path);
405            outLibraryPath = path;
406        }
407
408        return SLANG_OK;
409    }
410
411    // If this failed the path could be to a shared library, but we may need to convert to the
412    // shared library filename first
413    const String sharedLibraryFilePath = SharedLibrary::calcPlatformPath(path.getUnownedSlice());
414    if (SLANG_SUCCEEDED(Path::getPathType(sharedLibraryFilePath, &pathType)) &&
415        pathType == SLANG_PATH_TYPE_FILE)
416    {
417        // We pass in the shared library path, as canonical paths can sometimes only apply to
418        // pre-existing objects.
419        outParentPath = _getParentPath(sharedLibraryFilePath);
420        // The original path should work as is for the SharedLibrary load. Notably we don't use the
421        // sharedLibraryFilePath as this is the wrong name to do a SharedLibrary load with.
422        outLibraryPath = path;
423
424        return SLANG_OK;
425    }
426
427    return SLANG_FAIL;
428}
429
430/* static */ SlangResult DownstreamCompilerUtil::loadSharedLibrary(
431    const String& path,
432    ISlangSharedLibraryLoader* loader,
433    const char* const* dependentNames,
434    const char* inLibraryName,
435    ComPtr<ISlangSharedLibrary>& outSharedLib)
436{
437    String parentPath;
438    String libraryPath;
439
440    // If a path is passed in lets, try and determine what kind of path it is.
441    if (path.getLength())
442    {
443        if (SLANG_FAILED(_findPaths(path, inLibraryName, parentPath, libraryPath)))
444        {
445            // We have a few scenarios here.
446            // 1) The path could be the shared library/dll filename, that will be found through some
447            // operating system mechanism 2) That the shared library is *NOT* on the filesystem
448            // directly (the loader does something different) 3) Permissions or some other mechanism
449            // stops the lookup from working
450
451            // We should probably assume that the path means something, else why set it.
452            // It's probably less likely that it is a directory that we can't detect - as if it's a
453            // directory as part of an app it's permissions should allow detection, or be made to
454            // allow it.
455
456            // All this being the case we should probably assume that it is the shared library name.
457            libraryPath = path;
458
459            // Attempt to get a parent. If there isn't one this will be empty, which will mean it
460            // will be ignored, which is probably what we want if path is just a shared library name
461            parentPath = Path::getParentDirectory(libraryPath);
462        }
463    }
464
465    // Keep all dependent libs in scope, before we load the library we want
466    List<ComPtr<ISlangSharedLibrary>> dependentLibs;
467
468    // Try to load any dependent libs from the parent path
469    if (dependentNames)
470    {
471        for (const char* const* cur = dependentNames; *cur; ++cur)
472        {
473            const char* dependentName = *cur;
474            ComPtr<ISlangSharedLibrary> lib;
475            if (parentPath.getLength())
476            {
477                String dependentPath = Path::combine(parentPath, dependentName);
478                loader->loadSharedLibrary(dependentPath.getBuffer(), lib.writeRef());
479            }
480            else
481            {
482                loader->loadSharedLibrary(dependentName, lib.writeRef());
483            }
484
485            if (lib)
486            {
487                dependentLibs.add(lib);
488            }
489        }
490    }
491
492    if (libraryPath.getLength())
493    {
494        // If we hare a library path use that
495        return loader->loadSharedLibrary(libraryPath.getBuffer(), outSharedLib.writeRef());
496    }
497    else
498    {
499        // Else just use the name that was passed in.
500        return loader->loadSharedLibrary(inLibraryName, outSharedLib.writeRef());
501    }
502}
503
504/* static */ void DownstreamCompilerUtil::appendAsText(
505    const DownstreamCompilerDesc& desc,
506    StringBuilder& out)
507{
508    out << TypeTextUtil::getPassThroughAsHumanText(desc.type);
509
510    // Append the version if there is a version
511    if (desc.version.isSet())
512    {
513        out << " ";
514        out << desc.version.m_major;
515        out << ".";
516        out << desc.version.m_minor;
517    }
518}
519
520} // namespace Slang