yum-mirror/slang

Making it easier to work with shaders

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

jarcherNVAdd command line option for separate debug info (#7178)0d16228ae

master
18.1 KiB572 linesraw
1// slang-artifact.h
2#ifndef SLANG_ARTIFACT_H
3#define SLANG_ARTIFACT_H
4
5#include "../core/slang-basic.h"
6#include "slang-com-helper.h"
7
8#include <type_traits>
9
10namespace Slang
11{
12
13/* Simplest slice types. We can't use UnownedStringSlice etc, because they implement functionality
14in libraries, and we want to use these types in headers. If we wanted a C implementation it would be
15easy to use a macro to generate the functionality */
16
17template<typename T>
18struct Slice
19{
20    const T* begin() const { return data; }
21    const T* end() const { return data + count; }
22
23    const T& operator[](Index index) const
24    {
25        SLANG_ASSERT(index >= 0 && index < count);
26        return data[index];
27    }
28
29    Slice()
30        : count(0), data(nullptr)
31    {
32    }
33    Slice(const T* inData, Count inCount)
34        : data(inData), count(inCount)
35    {
36    }
37
38    const T* data;
39    Count count;
40};
41
42template<typename T>
43SLANG_FORCE_INLINE Slice<T> makeSlice(const T* inData, Count inCount)
44{
45    return Slice<T>(inData, inCount);
46}
47
48struct CharSlice : public Slice<char>
49{
50    typedef CharSlice ThisType;
51    typedef Slice<char> Super;
52
53    bool operator==(const ThisType& rhs) const
54    {
55        return count == rhs.count && (data == rhs.data || ::memcmp(data, rhs.data, count) == 0);
56    }
57    bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
58
59    explicit CharSlice(const char* in)
60        : Super(in, ::strlen(in))
61    {
62    }
63    CharSlice(const char* in, Count inCount)
64        : Super(in, inCount)
65    {
66    }
67    CharSlice()
68        : Super(nullptr, 0)
69    {
70    }
71    explicit CharSlice(const String& s)
72        : CharSlice(s.begin(), s.getLength()){};
73};
74static_assert(std::is_trivially_copyable_v<CharSlice>);
75
76struct TerminatedCharSlice : public CharSlice
77{
78    typedef TerminatedCharSlice ThisType;
79    typedef CharSlice Super;
80
81    SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const { return Super::operator==(rhs); }
82    SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
83
84    /// Make convertable to char*
85    SLANG_FORCE_INLINE operator const char*() const { return data; }
86
87    explicit TerminatedCharSlice(const char* in)
88        : Super(in)
89    {
90    }
91    TerminatedCharSlice(const char* in, Count inCount)
92        : Super(in, inCount)
93    {
94        SLANG_ASSERT(in[inCount] == 0);
95    }
96    TerminatedCharSlice()
97        : Super("", 0)
98    {
99    }
100};
101static_assert(std::is_trivially_copyable_v<TerminatedCharSlice>);
102
103/* As a rule of thumb, if we can define some aspect in a hierarchy then we should do so at the
104highest level. If some aspect can apply to multiple items identically we move that to a separate
105enum.
106
107NOTE!
108New Kinds must be added at the end. Values can be deprecated, or disabled
109but never removed, without breaking binary compatability.
110
111Any change requires a change to SLANG_ARTIFACT_KIND
112*/
113enum class ArtifactKind : uint8_t
114{
115    Invalid, ///< Invalid
116    Base,    ///< Base kind of all valid kinds
117
118    None,    ///< Doesn't contain anything
119    Unknown, ///< Unknown
120
121    BinaryFormat, ///< A generic binary format.
122
123    Container,            ///< Container like types
124    Zip,                  ///< Zip container
125    RiffContainer,        ///< Riff container
126    RiffLz4Container,     ///< Riff container using Lz4 compression
127    RiffDeflateContainer, ///< Riff container using deflate compression
128
129    Text, ///< Representation is text. Encoding is utf8, unless prefixed with 'encoding'.
130
131    Source,    ///< Source (Source type is in payload)
132    Assembly,  ///< Assembly (Type is in payload)
133    HumanText, ///< Text for human consumption
134
135    CompileBinary, ///< Kinds which are 'binary like' - can be executed, linked with and so forth.
136
137    ObjectCode,    ///< Object file
138    Library,       ///< Library (collection of object code)
139    Executable,    ///< Executable
140    SharedLibrary, ///< Shared library - can be dynamically linked
141    HostCallable,  ///< Code can be executed directly on the host
142
143    Instance, ///< Primary representation is an interface/class instance
144
145    Json, ///< It's JSON
146
147    CountOf,
148};
149
150/* Payload.
151
152SlangIR and LLVMIR can be GPU or CPU orientated, so put in own category.
153
154NOTE!
155New Payloads must be added at the end. Values can be deprecated, or disabled
156but never removed, without breaking binary compatability.
157
158Any change requires a change to SLANG_ARTIFACT_PAYLOAD
159*/
160enum class ArtifactPayload : uint8_t
161{
162    Invalid, ///< Is invalid - indicates some kind of problem
163    Base,    ///< The base of the hierarchy
164
165    None,    ///< Doesn't have a payload
166    Unknown, ///< Unknown but probably valid
167
168    Source, ///< Source code
169
170    C,     ///< C source
171    Cpp,   ///< C++ source
172    HLSL,  ///< HLSL source
173    GLSL,  ///< GLSL source
174    CUDA,  ///< CUDA source
175    Metal, ///< Metal source
176    Slang, ///< Slang source
177    WGSL,  ///< WGSL source
178
179    KernelLike, ///< GPU Kernel like
180
181    DXIL,       ///< DXIL
182    DXBC,       ///< DXBC
183    SPIRV,      ///< SPIR-V
184    PTX,        ///< PTX. NOTE! PTX is a text format, but is handable to CUDA API.
185    MetalAIR,   ///< Metal AIR
186    CuBin,      ///< CUDA binary
187    WGSL_SPIRV, ///< SPIR-V derived via WebGPU shading language
188
189    CPULike, ///< CPU code
190
191    UnknownCPU,   ///< CPU code for unknown/undetermined type
192    X86,          ///< X86
193    X86_64,       ///< X86_64
194    Aarch,        ///< 32 bit arm
195    Aarch64,      ///< Aarch64
196    HostCPU,      ///< HostCPU
197    UniversalCPU, ///< CPU code for multiple CPU types
198
199    GeneralIR, ///< General purpose IR representation (IR)
200
201    SlangIR, ///< Slang IR
202    LLVMIR,  ///< LLVM IR
203
204    AST, ///< Abstract syntax tree (AST)
205
206    SlangAST, ///< Slang AST
207
208    CompileResults, ///< Payload is a collection of compilation results
209
210    Metadata, ///< Metadata
211
212    DebugInfo,   ///< Debugging information
213    Diagnostics, ///< Diagnostics information
214
215    Miscellaneous, ///< Category for miscellaneous payloads (like Log/Lock)
216
217    Log,  ///< Log file
218    Lock, ///< Typically some kind of 'lock' file. Contents is typically not important.
219
220    PdbDebugInfo, ///< PDB debug info
221
222    SourceMap, ///< A source map
223
224    PostEmitMetadata, ///< Metadata from post emit (binding information)
225
226    CountOf,
227};
228
229/* Style.
230
231NOTE!
232New Styles must be added at the end. Values can be deprecated, or disabled
233but never removed, without breaking binary compatability.
234
235Any change requires a change to SLANG_ARTIFACT_STYLE
236*/
237enum class ArtifactStyle : uint8_t
238{
239    Invalid, ///< Invalid style (indicating an error)
240    Base,
241
242    None, ///< A style is not applicable
243
244    Unknown, ///< Unknown
245
246    CodeLike, ///< For styles that are 'code like' such as 'kernel' or 'host'.
247
248    Kernel,     ///< Compiled as `GPU kernel` style.
249    Host,       ///< Compiled in `host` style
250    Obfuscated, ///< Holds something specific to obfuscation, such as an obfuscated source map
251
252    CountOf,
253};
254
255typedef uint8_t ArtifactFlags;
256struct ArtifactFlag
257{
258    enum Enum : ArtifactFlags
259    {
260        // Don't currently have any flags
261    };
262};
263
264/**
265A value type to describe aspects of the contents of an Artifact.
266**/
267struct ArtifactDesc
268{
269public:
270    typedef ArtifactDesc ThisType;
271
272    typedef ArtifactKind Kind;
273    typedef ArtifactPayload Payload;
274    typedef ArtifactStyle Style;
275    typedef ArtifactFlags Flags;
276
277    typedef uint32_t PackedBacking;
278    enum class Packed : PackedBacking;
279
280    /// Get in packed format
281    inline Packed getPacked() const;
282
283    bool operator==(const ThisType& rhs) const
284    {
285        return kind == rhs.kind && payload == rhs.payload && style == rhs.style &&
286               flags == rhs.flags;
287    }
288    bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
289
290    /// Construct from the elements
291    static ThisType make(
292        Kind inKind,
293        Payload inPayload,
294        Style inStyle = Style::Unknown,
295        Flags flags = 0)
296    {
297        return ThisType{inKind, inPayload, inStyle, flags};
298    }
299    static ThisType make(Kind inKind, Payload inPayload, const ThisType& base)
300    {
301        return ThisType{inKind, inPayload, base.style, base.flags};
302    }
303
304    /// Construct from the packed format
305    inline static ThisType make(Packed inPacked);
306
307    Kind kind;
308    Payload payload;
309    Style style;
310    Flags flags;
311};
312
313// --------------------------------------------------------------------------
314inline ArtifactDesc::Packed ArtifactDesc::getPacked() const
315{
316    typedef PackedBacking IntType;
317    return Packed((IntType(kind) << 24) | (IntType(payload) << 16) | (IntType(style) << 8) | flags);
318}
319
320// --------------------------------------------------------------------------
321inline /* static */ ArtifactDesc ArtifactDesc::make(Packed inPacked)
322{
323    const PackedBacking packed = PackedBacking(inPacked);
324
325    ThisType r;
326    r.kind = Kind(packed >> 24);
327    r.payload = Payload(uint8_t(packed >> 16));
328    r.style = Style(uint8_t(packed >> 8));
329    r.flags = uint8_t(packed);
330
331    return r;
332}
333
334// Forward declare
335class IOSFileArtifactRepresentation;
336class IPathArtifactRepresentation;
337
338class IArtifactRepresentation;
339
340// Controls what items can be kept.
341enum class ArtifactKeep
342{
343    No,  ///< Don't keep the item
344    Yes, ///< Yes keep the final item
345    All, ///< Keep the final item and any intermediataries
346};
347
348/// True if can keep an intermediate item
349SLANG_INLINE bool canKeepIntermediate(ArtifactKeep keep)
350{
351    return keep == ArtifactKeep::All;
352}
353/// True if can keep
354SLANG_INLINE bool canKeep(ArtifactKeep keep)
355{
356    return Index(keep) >= Index(ArtifactKeep::Yes);
357}
358/// Returns the keep type for an intermediate
359SLANG_INLINE ArtifactKeep getIntermediateKeep(ArtifactKeep keep)
360{
361    return (keep == ArtifactKeep::All) ? ArtifactKeep::All : ArtifactKeep::No;
362}
363
364/* Forward define */
365class IArtifactHandler;
366
367/* The IArtifact interface is designed to represent some Artifact of compilation. It could be input
368to or output from a compilation.
369
370An abstraction is desirable here, because depending on the compiler the artifact/s could be
371
372* A file on the file system
373* A blob
374* Multiple files
375* Some other (perhaps multiple) in memory representations
376* A name
377
378The artifact uses the Blob as the canonical in memory representation.
379
380Some downstream compilers require the artifact to be available as a file system file, or to produce
381artifacts that are files. The IArtifact type allows to abstract away this difference, including the
382ability to turn an in memory representation into a temporary file on the file system.
383
384The mechanism also allows for 'Containers' which allow for Artifacts to contain other Artifacts
385(amongst other things). Those artifacts may be other files. For example a downstream compilation
386that produces results as well as temporary files could be a Container containing artifacts for
387
388* Diagnostics
389* Temporary files (of known and unknown types)
390* Files that contain known types
391* Callable interface (an ISlangSharedLibrary)
392
393There are several types of ways to associate data with an artifact:
394
395* A representation
396* An associated artifact
397* A child artifact
398
399A `representation` has to wholly represent the artifact. That representation could be a blob, a file
400on the file system, an in memory representation. There are two classes of `Representation` - ones
401that can be turned into blobs (and therefore derive from IArtifactRepresentation) and ones that are
402in of themselves a representation (such as a blob or or ISlangSharedLibrary).
403
404`Associated artifacts` hold information that is associated with the artifact. It could be part
405of the representation, or useful for the implementation of a representation. Could also be
406considered as a kind of side channel to associate arbitrary data including temporary data with an
407artifact.
408
409A `child artifact` belongs to the artifact, within the hierarchy of artifacts.
410
411This also uses the ICompileResult interface to more easily allow the Slang API to retrieve
412multiple associated artifacts in cases where both base and debug spirv are needed.
413*/
414class IArtifact : public slang::ICompileResult
415{
416public:
417    SLANG_COM_INTERFACE(
418        0xf90acdb0,
419        0x9a4a,
420        0x414e,
421        {0x85, 0x45, 0x8b, 0x26, 0xc9, 0x2d, 0x94, 0x42})
422
423    enum class ContainedKind
424    {
425        Representation,
426        Associated,
427        Children,
428    };
429
430    typedef ArtifactDesc Desc;
431
432    typedef ArtifactKind Kind;
433    typedef ArtifactPayload Payload;
434    typedef ArtifactStyle Style;
435    typedef ArtifactFlags Flags;
436
437    typedef ArtifactKeep Keep;
438
439    /// Get the Desc defining the contents of the artifact
440    virtual SLANG_NO_THROW Desc SLANG_MCALL getDesc() = 0;
441
442    /// Returns true if the artifact in principal exists
443    virtual SLANG_NO_THROW bool SLANG_MCALL exists() = 0;
444
445    /// Load as a blob
446    virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadBlob(Keep keep, ISlangBlob** outBlob) = 0;
447
448    /// Require artifact is available as a file.
449    /// NOTE! May need to serialize and write as a temporary file.
450    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
451    requireFile(Keep keep, IOSFileArtifactRepresentation** outFileRep) = 0;
452
453    /// Load the artifact as a shared library
454    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
455    loadSharedLibrary(ArtifactKeep keep, ISlangSharedLibrary** outSharedLibrary) = 0;
456
457    /// Get the name of the artifact. This can be empty.
458    virtual SLANG_NO_THROW const char* SLANG_MCALL getName() = 0;
459    /// Set the name associated with the artifact
460    virtual SLANG_NO_THROW void SLANG_MCALL setName(const char* name) = 0;
461
462    /// Add associated artifacts with this artifact
463    virtual SLANG_NO_THROW void SLANG_MCALL addAssociated(IArtifact* artifact) = 0;
464    /// Get the list of associated items
465    virtual SLANG_NO_THROW Slice<IArtifact*> SLANG_MCALL getAssociated() = 0;
466
467    /// Add a representation
468    virtual SLANG_NO_THROW void SLANG_MCALL addRepresentation(ICastable* castable) = 0;
469    /// Add a representation that doesn't derive from IArtifactRepresentation
470    virtual SLANG_NO_THROW void SLANG_MCALL addRepresentationUnknown(ISlangUnknown* rep) = 0;
471    /// Get all the representations
472    virtual SLANG_NO_THROW Slice<ICastable*> SLANG_MCALL getRepresentations() = 0;
473
474    /// Given a typeGuid representing the desired type get or create the representation.
475    /// If found outCastable holds an entity that *must* be castable to typeGuid
476    /// Use the keep parameter to determine if the representation should be cached on the artifact/s
477    /// or not.
478    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
479    getOrCreateRepresentation(const Guid& typeGuid, ArtifactKeep keep, ICastable** outCastable) = 0;
480
481    /// Get the handler used for this artifact. If nullptr means the default handler will be used.
482    virtual SLANG_NO_THROW IArtifactHandler* SLANG_MCALL getHandler() = 0;
483    /// Set the handler associated with this artifact. Setting nullptr will use the default handler.
484    virtual SLANG_NO_THROW void SLANG_MCALL setHandler(IArtifactHandler* handler) = 0;
485
486    /// Returns the result of expansion. Will return SLANG_E_UNINITIALIZED if expansion hasn't
487    /// happened
488    virtual SLANG_NO_THROW SlangResult SLANG_MCALL getExpandChildrenResult() = 0;
489    /// Sets all of the children, will set the expansion state to SLANG_OK
490    virtual SLANG_NO_THROW void SLANG_MCALL
491    setChildren(IArtifact* const* children, Count count) = 0;
492    /// Will be called implicitly on access to children
493    virtual SLANG_NO_THROW SlangResult SLANG_MCALL expandChildren() = 0;
494
495    /// Add the artifact to the list
496    virtual SLANG_NO_THROW void SLANG_MCALL addChild(IArtifact* artifact) = 0;
497    /// Get the children, will only remain valid if no mutation of children list
498    virtual SLANG_NO_THROW Slice<IArtifact*> SLANG_MCALL getChildren() = 0;
499
500    /// Find a represention from the specified list
501    virtual SLANG_NO_THROW void* SLANG_MCALL
502    findRepresentation(ContainedKind kind, const Guid& guid) = 0;
503    /// Clear all of the contained kind
504    virtual SLANG_NO_THROW void SLANG_MCALL clear(ContainedKind kind) = 0;
505    /// Remove entry at index for the specified kind
506    virtual SLANG_NO_THROW void SLANG_MCALL removeAt(ContainedKind kind, Index i) = 0;
507};
508
509template<typename T>
510SLANG_FORCE_INLINE T* findRepresentation(IArtifact* artifact)
511{
512    return reinterpret_cast<T*>(
513        artifact->findRepresentation(IArtifact::ContainedKind::Representation, T::getTypeGuid()));
514}
515
516template<typename T>
517SLANG_FORCE_INLINE T* findAssociatedRepresentation(IArtifact* artifact)
518{
519    return reinterpret_cast<T*>(
520        artifact->findRepresentation(IArtifact::ContainedKind::Associated, T::getTypeGuid()));
521}
522
523template<typename T>
524SLANG_FORCE_INLINE T* findChildRepresentation(IArtifact* artifact)
525{
526    return reinterpret_cast<T*>(
527        artifact->findRepresentation(IArtifact::ContainedKind::Children, T::getTypeGuid()));
528}
529
530/* The IArtifactRepresentation interface represents a single representation that can be part of an
531artifact. It's special in so far as
532
533* IArtifactRepresentation can be queried for it's underlying object class
534* Can determine if the representation exists (for example if it's on the file system)
535* Can optionally serialize into a blob
536*/
537class IArtifactRepresentation : public ICastable
538{
539    SLANG_COM_INTERFACE(0xa3790eb, 0x22b9, 0x430e, {0xbf, 0xc6, 0x24, 0x6c, 0x5b, 0x5c, 0xcd, 0x0})
540
541    /// Create a representation of the specified typeGuid interface.
542    /// Calling castAs on the castable will return the specific type
543    /// Returns SLANG_E_NOT_IMPLEMENTED if an implementation doesn't implement
544    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
545    createRepresentation(const Guid& typeGuid, ICastable** outCastable) = 0;
546
547    /// Returns true if this representation exists and is available for use.
548    virtual SLANG_NO_THROW bool SLANG_MCALL exists() = 0;
549};
550
551/* Handler provides functionality external to the artifact */
552class IArtifactHandler : public ICastable
553{
554    SLANG_COM_INTERFACE(
555        0x6a646f57,
556        0xb3ac,
557        0x4c6a,
558        {0xb6, 0xf1, 0x33, 0xb6, 0xef, 0x60, 0xa6, 0xae});
559
560    /// Given an artifact expands children
561    virtual SLANG_NO_THROW SlangResult SLANG_MCALL expandChildren(IArtifact* container) = 0;
562    /// Given an artifact gets or creates a representation.
563    virtual SLANG_NO_THROW SlangResult SLANG_MCALL getOrCreateRepresentation(
564        IArtifact* artifact,
565        const Guid& guid,
566        ArtifactKeep keep,
567        ICastable** outCastable) = 0;
568};
569
570} // namespace Slang
571
572#endif