yum-mirror/slang

Making it easier to work with shaders

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

aidanfnvAdd arguments for controlling floating point denormal mode (#7461)d50c3f34a

master
17.1 KiB505 linesraw
1#ifndef SLANG_DOWNSTREAM_COMPILER_H
2#define SLANG_DOWNSTREAM_COMPILER_H
3
4#include "../core/slang-common.h"
5#include "../core/slang-io.h"
6#include "../core/slang-platform.h"
7#include "../core/slang-process-util.h"
8#include "../core/slang-semantic-version.h"
9#include "../core/slang-string.h"
10#include "slang-artifact-associated.h"
11#include "slang-artifact.h"
12#include "slang-com-ptr.h"
13
14#include <type_traits>
15
16namespace Slang
17{
18
19struct SourceManager;
20
21// Compiler description
22struct DownstreamCompilerDesc
23{
24    typedef DownstreamCompilerDesc ThisType;
25
26    HashCode getHashCode() const { return combineHash(HashCode(type), version.getHashCode()); }
27    bool operator==(const ThisType& rhs) const
28    {
29        return type == rhs.type && version == rhs.version;
30    }
31    bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
32
33    /// Get the version as a value
34    Int getVersionValue() const { return version.m_major * 100 + version.m_minor; }
35
36    /// true if has a version set
37    bool hasVersion() const { return version.isSet(); }
38
39    /// Ctor
40    explicit DownstreamCompilerDesc(
41        SlangPassThrough inType = SLANG_PASS_THROUGH_NONE,
42        Int inMajorVersion = 0,
43        Int inMinorVersion = 0)
44        : type(inType), version(int(inMajorVersion), int(inMinorVersion))
45    {
46    }
47    explicit DownstreamCompilerDesc(SlangPassThrough inType, const SemanticVersion& inVersion)
48        : type(inType), version(inVersion)
49    {
50    }
51
52    SlangPassThrough type;   ///< The type of the compiler
53    SemanticVersion version; ///< The version of the compiler
54};
55
56/* Placed at the start of structs that are versioned.
57The id uniquely identifies a compatible set of versions.
58The size indicates the struct size. It should be considered as a kind of version number.
59The larger the number for the target the newer the *compatible* version (assuming the identifiers
60match).
61
62Note that size versioning *only* works, if adding a field *doesn't* use any existing unused "pad"
63bytes. This implies that any new members *must* take into account padding/alignment. Any additions
64that have alignment *less* than the alignment of struct may need padding.
65*/
66struct VersionedStruct
67{
68    typedef VersionedStruct ThisType;
69    VersionedStruct(uint32_t inIdentifier, size_t inSize)
70        : identifier(inIdentifier), size(uint32_t(inSize))
71    {
72    }
73
74    /// True if the versions are identical
75    bool operator==(const ThisType& rhs) const
76    {
77        return identifier == rhs.identifier && size == rhs.size;
78    }
79    bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
80
81    VersionedStruct(const ThisType& rhs) = default;
82    ThisType& operator=(const ThisType& rhs) = default;
83
84    uint32_t identifier;
85    uint32_t size;
86};
87
88template<typename T>
89T getCompatibleVersion(const T* inT)
90{
91    const VersionedStruct* in = &inT->version;
92
93    // It must be at the start of the struct
94    SLANG_ASSERT((void*)in == (void*)inT);
95
96    // Note that the struct is passed in by pointer rather than reference, because
97    // we must ensure that it is not sliced.
98
99    // Must match
100    SLANG_ASSERT(T::kVersionIdentifier == in->identifier);
101
102    // If the same size we can just use what we have
103    if (in->size == sizeof(T))
104    {
105        return *inT;
106    }
107
108    // Initialize a new T to copy into
109    T t;
110
111    // Keep a copy of the version as will be overwritten
112    const auto currentVersion = t.version;
113
114    // If the size is smaller we just copy the bytes that we have.
115    // NOTE! This only works if care is taken with padding/end bytes of previous versions
116    // see above on VersionedStruct
117    if (in->size < sizeof(T))
118    {
119        // Copy up the size that's stored
120        ::memcpy(&t, in, in->size);
121    }
122    else
123    {
124        t = *inT;
125    }
126
127    t.version = currentVersion;
128    return t;
129}
130
131template<typename T>
132bool isVersionCompatible(const VersionedStruct& ver)
133{
134    return ver.identifier == T::kVersionIdentifier;
135}
136
137template<typename T>
138bool isVersionCompatible(const T& in)
139{
140    return isVersionCompatible<T>(in.version);
141}
142
143/* Downstream compile options
144
145NOTE! This type is trafficed across shared library boundaries and *versioned*.
146In particular
147
148* The struct can only contain types that can be trivially memcpyd (checked by static_assert);
149* New fields can only be added to the end of the struct
150* New fields must take into account alignment/padding such that they do not share bytes in previous
151version sizes
152*/
153struct DownstreamCompileOptions
154{
155    typedef DownstreamCompileOptions ThisType;
156
157    // A unique identifer for this particular struct kind. If the struct become incompatible
158    // a new id should be used to identify a specific style. If the change is only to add members
159    // to the end, this should be handled via the version size at use sites.
160    static const uint32_t kVersionIdentifier = 0x34296897;
161
162    typedef uint32_t Flags;
163    struct Flag
164    {
165        enum Enum : Flags
166        {
167            EnableExceptionHandling =
168                0x01, ///< Enables exception handling support (say as optionally supported by C++)
169            Verbose = 0x02,              ///< Give more verbose diagnostics
170            EnableSecurityChecks = 0x04, ///< Enable runtime security checks (such as for buffer
171                                         ///< overruns) - enabling typically decreases performance
172            EnableFloat16 = 0x08,        ///< If set compiles with support for float16/half
173        };
174    };
175
176    enum class OptimizationLevel : uint8_t
177    {
178        None,    ///< Don't optimize at all.
179        Default, ///< Default optimization level: balance code quality and compilation time.
180        High,    ///< Optimize aggressively.
181        Maximal, ///< Include optimizations that may take a very long time, or may involve severe
182                 ///< space-vs-speed tradeoffs
183    };
184
185    enum class DebugInfoType : uint8_t
186    {
187        None,     ///< Don't emit debug information at all.
188        Minimal,  ///< Emit as little debug information as possible, while still supporting stack
189                  ///< traces.
190        Standard, ///< Emit whatever is the standard level of debug information for each target.
191        Maximal,  ///< Emit as much debug information as possible for each target.
192    };
193    enum class FloatingPointMode : uint8_t
194    {
195        Default,
196        Fast,
197        Precise,
198    };
199
200    enum class FloatingPointDenormalMode : uint8_t
201    {
202        Any,
203        Preserve,
204        FlushToZero,
205    };
206
207    enum PipelineType : uint8_t
208    {
209        Unknown,
210        Compute,
211        Rasterization,
212        RayTracing,
213    };
214
215    struct Define
216    {
217        TerminatedCharSlice nameWithSig; ///< If macro takes parameters include in brackets
218        TerminatedCharSlice value;
219    };
220
221    struct CapabilityVersion
222    {
223        enum class Kind : uint8_t
224        {
225            CUDASM, ///< What the version is for
226            SPIRV,
227        };
228        Kind kind;
229        SemanticVersion version;
230    };
231
232    // These members must be the first members of the struct!
233    VersionedStruct version = VersionedStruct(kVersionIdentifier, sizeof(ThisType));
234
235    OptimizationLevel optimizationLevel = OptimizationLevel::Default;
236    DebugInfoType debugInfoType = DebugInfoType::Standard;
237    SlangCompileTarget targetType = SLANG_HOST_EXECUTABLE;
238    SlangSourceLanguage sourceLanguage = SLANG_SOURCE_LANGUAGE_CPP;
239    FloatingPointMode floatingPointMode = FloatingPointMode::Default;
240    PipelineType pipelineType = PipelineType::Unknown;
241    SlangMatrixLayoutMode matrixLayout = SLANG_MATRIX_LAYOUT_MODE_UNKNOWN;
242
243    Flags flags = Flag::EnableExceptionHandling;
244
245    PlatformKind platform = PlatformKind::Unknown;
246
247    /// The path/name of the output module. Should not have the extension, as that will be added for
248    /// each of the target types. If not set a module path will be internally generated internally
249    /// on a command line based compiler
250    TerminatedCharSlice modulePath;
251
252    Slice<Define> defines;
253
254    /// The source artifacts
255    Slice<IArtifact*> sourceArtifacts;
256
257    Slice<TerminatedCharSlice> includePaths;
258    Slice<TerminatedCharSlice> libraryPaths;
259
260    /// Libraries to link against.
261    Slice<IArtifact*> libraries;
262
263    Slice<CapabilityVersion> requiredCapabilityVersions;
264
265    /// For compilers/compiles that require an entry point name, else can be empty
266    TerminatedCharSlice entryPointName;
267    /// Profile name to use, only required for compiles that need to compile against a a specific
268    /// profiles. Profile names are tied to compilers and targets.
269    TerminatedCharSlice profileName;
270    // According to DirectX Raytracing Specification, PAQs are supported in Shader Model 6.7 and
271    // above
272    bool enablePAQ = false;
273
274    /// The stage being compiled for
275    SlangStage stage = SLANG_STAGE_NONE;
276
277    /// Arguments that are specific to a particular compiler implementation.
278    Slice<TerminatedCharSlice> compilerSpecificArguments;
279
280    /// NOTE! Not all downstream compilers can use the fileSystemExt/sourceManager. This option will
281    /// be ignored in those scenarios.
282    ISlangFileSystemExt* fileSystemExt = nullptr;
283    SourceManager* sourceManager = nullptr;
284
285    // The debug info format to use.
286    SlangDebugInfoFormat m_debugInfoFormat = SLANG_DEBUG_INFO_FORMAT_DEFAULT;
287
288    // The floating point denormal handling mode to use for each floating point precision
289    FloatingPointDenormalMode denormalModeFp16 = FloatingPointDenormalMode::Any;
290    FloatingPointDenormalMode denormalModeFp32 = FloatingPointDenormalMode::Any;
291    FloatingPointDenormalMode denormalModeFp64 = FloatingPointDenormalMode::Any;
292};
293static_assert(std::is_trivially_copyable_v<DownstreamCompileOptions>);
294
295#define SLANG_ALIAS_DEPRECATED_VERSION(name, id, firstField, lastField)                           \
296    struct name##_AliasDeprecated##id                                                             \
297    {                                                                                             \
298        static const ptrdiff_t kStart = SLANG_OFFSET_OF(name, firstField);                        \
299        static const ptrdiff_t kEnd = SLANG_OFFSET_OF(name, lastField) + sizeof(name::lastField); \
300    };
301
302/* Used to indicate what kind of products are expected to be produced for a compilation. */
303typedef uint32_t DownstreamProductFlags;
304struct DownstreamProductFlag
305{
306    enum Enum : DownstreamProductFlags
307    {
308        Debug = 0x1,         ///< Used by debugger during execution
309        Execution = 0x2,     ///< Required for execution
310        Compile = 0x4,       ///< A product *required* for compilation
311        Miscellaneous = 0x8, ///< Anything else
312    };
313    enum Mask : DownstreamProductFlags
314    {
315        All = 0xf, ///< All the flags
316    };
317};
318
319class IDownstreamCompiler : public ICastable
320{
321public:
322    SLANG_COM_INTERFACE(
323        0x167b8ba7,
324        0xbd41,
325        0x469a,
326        {0x92, 0x28, 0xb8, 0x53, 0xc8, 0xea, 0x56, 0x6d})
327
328    typedef DownstreamCompilerDesc Desc;
329    typedef DownstreamCompileOptions CompileOptions;
330
331    typedef CompileOptions::OptimizationLevel OptimizationLevel;
332    typedef CompileOptions::DebugInfoType DebugInfoType;
333    typedef CompileOptions::FloatingPointMode FloatingPointMode;
334    typedef CompileOptions::PipelineType PipelineType;
335    typedef CompileOptions::Define Define;
336    typedef CompileOptions::CapabilityVersion CapabilityVersion;
337
338    /// Get the desc of this compiler
339    virtual SLANG_NO_THROW const Desc& SLANG_MCALL getDesc() = 0;
340    /// Compile using the specified options. The result is in resOut
341    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
342    compile(const CompileOptions& options, IArtifact** outArtifact) = 0;
343    /// Returns true if compiler can do a transformation of `from` to `to` Artifact types
344    virtual SLANG_NO_THROW bool SLANG_MCALL
345    canConvert(const ArtifactDesc& from, const ArtifactDesc& to) = 0;
346    /// Converts an artifact `from` to a desc of `to` and puts the result in outArtifact
347    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
348    convert(IArtifact* from, const ArtifactDesc& to, IArtifact** outArtifact) = 0;
349    /// Get the version of this compiler
350    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
351    getVersionString(slang::IBlob** outVersionString) = 0;
352    /// Validate and return the result
353    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
354    validate(const uint32_t* contents, int contentsSize) = 0;
355    /// Disassemble and print to stdout
356    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
357    disassemble(const uint32_t* contents, int contentsSize) = 0;
358    /// Disassemble and return the result as a string
359    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
360    disassembleWithResult(const uint32_t* contents, int contentsSize, String& outString) = 0;
361
362    /// True if underlying compiler uses file system to communicate source
363    virtual SLANG_NO_THROW bool SLANG_MCALL isFileBased() = 0;
364
365    virtual SLANG_NO_THROW int SLANG_MCALL link(
366        const uint32_t** modules,
367        const uint32_t* moduleSizes,
368        const uint32_t moduleCount,
369        IArtifact** outArtifact)
370    {
371        SLANG_UNREFERENCED_PARAMETER(modules);
372        SLANG_UNREFERENCED_PARAMETER(moduleSizes);
373        SLANG_UNREFERENCED_PARAMETER(moduleCount);
374        SLANG_UNREFERENCED_PARAMETER(outArtifact);
375        return 0;
376    }
377};
378
379class DownstreamCompilerBase : public ComBaseObject, public IDownstreamCompiler
380{
381public:
382    SLANG_COM_BASE_IUNKNOWN_ALL
383
384    // ICastable
385    virtual SLANG_NO_THROW void* SLANG_MCALL castAs(const Guid& guid) SLANG_OVERRIDE;
386
387    // IDownstreamCompiler
388    virtual SLANG_NO_THROW const Desc& SLANG_MCALL getDesc() SLANG_OVERRIDE { return m_desc; }
389    virtual SLANG_NO_THROW bool SLANG_MCALL
390    canConvert(const ArtifactDesc& from, const ArtifactDesc& to) SLANG_OVERRIDE
391    {
392        SLANG_UNUSED(from);
393        SLANG_UNUSED(to);
394        return false;
395    }
396    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
397    convert(IArtifact* from, const ArtifactDesc& to, IArtifact** outArtifact) SLANG_OVERRIDE;
398    virtual SLANG_NO_THROW SlangResult SLANG_MCALL getVersionString(slang::IBlob** outVersionString)
399        SLANG_OVERRIDE
400    {
401        *outVersionString = nullptr;
402        return SLANG_FAIL;
403    }
404    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
405    validate(const uint32_t* contents, int contentsSize) SLANG_OVERRIDE
406    {
407        SLANG_UNUSED(contents);
408        SLANG_UNUSED(contentsSize);
409        return SLANG_FAIL;
410    }
411    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
412    disassemble(const uint32_t* contents, int contentsSize) SLANG_OVERRIDE
413    {
414        SLANG_UNUSED(contents);
415        SLANG_UNUSED(contentsSize);
416        return SLANG_FAIL;
417    }
418
419    virtual SLANG_NO_THROW SlangResult SLANG_MCALL disassembleWithResult(
420        const uint32_t* contents,
421        int contentsSize,
422        String& outString) SLANG_OVERRIDE
423    {
424        SLANG_UNUSED(contents);
425        SLANG_UNUSED(contentsSize);
426        SLANG_UNUSED(outString);
427        return SLANG_FAIL;
428    }
429
430    DownstreamCompilerBase(const Desc& desc)
431        : m_desc(desc)
432    {
433    }
434    DownstreamCompilerBase() {}
435
436    void* getInterface(const Guid& guid);
437    void* getObject(const Guid& guid);
438
439    Desc m_desc;
440};
441
442class CommandLineDownstreamCompiler : public DownstreamCompilerBase
443{
444public:
445    typedef DownstreamCompilerBase Super;
446
447    // IDownstreamCompiler
448    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
449    compile(const CompileOptions& options, IArtifact** outArtifact) SLANG_OVERRIDE;
450    virtual SLANG_NO_THROW bool SLANG_MCALL isFileBased() SLANG_OVERRIDE { return true; }
451
452    // Functions to be implemented for a specific CommandLine
453
454    /// Given options determines the paths to products produced (including the 'moduleFilePath').
455    /// Note that does *not* guarentee all products were or should be produced. Just aims to include
456    /// all that could be produced, such that can be removed on completion.
457    virtual SlangResult calcCompileProducts(
458        const CompileOptions& options,
459        DownstreamProductFlags flags,
460        IOSFileArtifactRepresentation* lockFile,
461        List<ComPtr<IArtifact>>& outArtifacts) = 0;
462
463    virtual SlangResult calcArgs(const CompileOptions& options, CommandLine& cmdLine) = 0;
464    virtual SlangResult parseOutput(
465        const ExecuteResult& exeResult,
466        IArtifactDiagnostics* diagnostics) = 0;
467
468    CommandLineDownstreamCompiler(const Desc& desc, const ExecutableLocation& exe)
469        : Super(desc)
470    {
471        m_cmdLine.setExecutableLocation(exe);
472    }
473
474    CommandLineDownstreamCompiler(const Desc& desc, const CommandLine& cmdLine)
475        : Super(desc), m_cmdLine(cmdLine)
476    {
477    }
478
479    CommandLineDownstreamCompiler(const Desc& desc)
480        : Super(desc)
481    {
482    }
483
484    CommandLine m_cmdLine;
485};
486
487/* Only purpose of having base-class here is to make all the DownstreamCompiler types available
488 * directly in derived Utils */
489struct DownstreamCompilerUtilBase
490{
491    typedef DownstreamCompileOptions CompileOptions;
492
493    typedef CompileOptions::OptimizationLevel OptimizationLevel;
494    typedef CompileOptions::DebugInfoType DebugInfoType;
495
496    typedef CompileOptions::FloatingPointMode FloatingPointMode;
497    typedef CompileOptions::FloatingPointDenormalMode FloatingPointDenormalMode;
498
499    typedef DownstreamProductFlag ProductFlag;
500    typedef DownstreamProductFlags ProductFlags;
501};
502
503} // namespace Slang
504
505#endif