| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // DxilContainer.h // |
| // Copyright (C) Microsoft Corporation. All rights reserved. // |
| // This file is distributed under the University of Illinois Open Source // |
| // License. See LICENSE.TXT for details. // |
| // // |
| // Provides declarations for the DXIL container format. // |
| // // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #pragma once |
| |
| #ifndef __DXC_CONTAINER__ |
| #define __DXC_CONTAINER__ |
| |
| #include <stdint.h> |
| #include <iterator> |
| #include <functional> |
| #include "dxc/HLSL/DxilConstants.h" |
| |
| struct IDxcContainerReflection; |
| namespace llvm { class Module; } |
| |
| namespace hlsl { |
| |
| class AbstractMemoryStream; |
| class RootSignatureHandle; |
| class DxilModule; |
| |
| #pragma pack(push, 1) |
| |
| static const size_t DxilContainerHashSize = 16; |
| static const uint16_t DxilContainerVersionMajor = 1; // Current major version |
| static const uint16_t DxilContainerVersionMinor = 0; // Current minor version |
| static const uint32_t DxilContainerMaxSize = 0x80000000; // Max size for container. |
| |
| /// Use this type to represent the hash for the full container. |
| struct DxilContainerHash { |
| uint8_t Digest[DxilContainerHashSize]; |
| }; |
| |
| struct DxilContainerVersion { |
| uint16_t Major; |
| uint16_t Minor; |
| }; |
| |
| /// Use this type to describe a DXIL container of parts. |
| struct DxilContainerHeader { |
| uint32_t HeaderFourCC; |
| DxilContainerHash Hash; |
| DxilContainerVersion Version; |
| uint32_t ContainerSizeInBytes; // From start of this header |
| uint32_t PartCount; |
| // Structure is followed by uint32_t PartOffset[PartCount]; |
| // The offset is to a DxilPartHeader. |
| }; |
| |
| /// Use this type to describe the size and type of a DXIL container part. |
| struct DxilPartHeader { |
| uint32_t PartFourCC; // Four char code for part type. |
| uint32_t PartSize; // Byte count for PartData. |
| // Structure is followed by uint8_t PartData[PartSize]. |
| }; |
| |
| #define DXIL_FOURCC(ch0, ch1, ch2, ch3) ( \ |
| (uint32_t)(uint8_t)(ch0) | (uint32_t)(uint8_t)(ch1) << 8 | \ |
| (uint32_t)(uint8_t)(ch2) << 16 | (uint32_t)(uint8_t)(ch3) << 24 \ |
| ) |
| |
| enum DxilFourCC { |
| DFCC_Container = DXIL_FOURCC('D', 'X', 'B', 'C'), // for back-compat with tools that look for DXBC containers |
| DFCC_ResourceDef = DXIL_FOURCC('R', 'D', 'E', 'F'), |
| DFCC_InputSignature = DXIL_FOURCC('I', 'S', 'G', '1'), |
| DFCC_OutputSignature = DXIL_FOURCC('O', 'S', 'G', '1'), |
| DFCC_PatchConstantSignature = DXIL_FOURCC('P', 'S', 'G', '1'), |
| DFCC_ShaderStatistics = DXIL_FOURCC('S', 'T', 'A', 'T'), |
| DFCC_ShaderDebugInfoDXIL = DXIL_FOURCC('I', 'L', 'D', 'B'), |
| DFCC_ShaderDebugName = DXIL_FOURCC('I', 'L', 'D', 'N'), |
| DFCC_FeatureInfo = DXIL_FOURCC('S', 'F', 'I', '0'), |
| DFCC_PrivateData = DXIL_FOURCC('P', 'R', 'I', 'V'), |
| DFCC_RootSignature = DXIL_FOURCC('R', 'T', 'S', '0'), |
| DFCC_DXIL = DXIL_FOURCC('D', 'X', 'I', 'L'), |
| DFCC_PipelineStateValidation = DXIL_FOURCC('P', 'S', 'V', '0'), |
| }; |
| |
| #undef DXIL_FOURCC |
| |
| // DFCC_FeatureInfo is a uint64_t value with these flags. |
| static const uint64_t ShaderFeatureInfo_Doubles = 0x0001; |
| static const uint64_t ShaderFeatureInfo_ComputeShadersPlusRawAndStructuredBuffersViaShader4X = 0x0002; |
| static const uint64_t ShaderFeatureInfo_UAVsAtEveryStage = 0x0004; |
| static const uint64_t ShaderFeatureInfo_64UAVs = 0x0008; |
| static const uint64_t ShaderFeatureInfo_MinimumPrecision = 0x0010; |
| static const uint64_t ShaderFeatureInfo_11_1_DoubleExtensions = 0x0020; |
| static const uint64_t ShaderFeatureInfo_11_1_ShaderExtensions = 0x0040; |
| static const uint64_t ShaderFeatureInfo_LEVEL9ComparisonFiltering = 0x0080; |
| static const uint64_t ShaderFeatureInfo_TiledResources = 0x0100; |
| static const uint64_t ShaderFeatureInfo_StencilRef = 0x0200; |
| static const uint64_t ShaderFeatureInfo_InnerCoverage = 0x0400; |
| static const uint64_t ShaderFeatureInfo_TypedUAVLoadAdditionalFormats = 0x0800; |
| static const uint64_t ShaderFeatureInfo_ROVs = 0x1000; |
| static const uint64_t ShaderFeatureInfo_ViewportAndRTArrayIndexFromAnyShaderFeedingRasterizer = 0x2000; |
| static const uint64_t ShaderFeatureInfo_WaveOps = 0x4000; |
| static const uint64_t ShaderFeatureInfo_Int64Ops = 0x8000; |
| static const uint64_t ShaderFeatureInfo_ViewID = 0x10000; |
| static const uint64_t ShaderFeatureInfo_Barycentrics = 0x20000; |
| static const uint64_t ShaderFeatureInfo_NativeLowPrecision = 0x40000; |
| |
| static const unsigned ShaderFeatureInfoCount = 19; |
| |
| struct DxilShaderFeatureInfo { |
| uint64_t FeatureFlags; |
| }; |
| |
| // DXIL program information. |
| struct DxilBitcodeHeader { |
| uint32_t DxilMagic; // ACSII "DXIL". |
| uint32_t DxilVersion; // DXIL version. |
| uint32_t BitcodeOffset; // Offset to LLVM bitcode (from start of header). |
| uint32_t BitcodeSize; // Size of LLVM bitcode. |
| }; |
| static const uint32_t DxilMagicValue = 0x4C495844; // 'DXIL' |
| |
| struct DxilProgramHeader { |
| uint32_t ProgramVersion; /// Major and minor version, including type. |
| uint32_t SizeInUint32; /// Size in uint32_t units including this header. |
| DxilBitcodeHeader BitcodeHeader; /// Bitcode-specific header. |
| // Followed by uint8_t[BitcodeHeader.BitcodeOffset] |
| }; |
| |
| struct DxilProgramSignature { |
| uint32_t ParamCount; |
| uint32_t ParamOffset; |
| }; |
| |
| enum class DxilProgramSigMinPrecision : uint32_t { |
| Default = 0, |
| Float16 = 1, |
| Float2_8 = 2, |
| Reserved = 3, |
| SInt16 = 4, |
| UInt16 = 5, |
| Any16 = 0xf0, |
| Any10 = 0xf1 |
| }; |
| |
| // Corresponds to D3D_NAME and D3D10_SB_NAME |
| enum class DxilProgramSigSemantic : uint32_t { |
| Undefined = 0, |
| Position = 1, |
| ClipDistance = 2, |
| CullDistance = 3, |
| RenderTargetArrayIndex = 4, |
| ViewPortArrayIndex = 5, |
| VertexID = 6, |
| PrimitiveID = 7, |
| InstanceID = 8, |
| IsFrontFace = 9, |
| SampleIndex = 10, |
| FinalQuadEdgeTessfactor = 11, |
| FinalQuadInsideTessfactor = 12, |
| FinalTriEdgeTessfactor = 13, |
| FinalTriInsideTessfactor = 14, |
| FinalLineDetailTessfactor = 15, |
| FinalLineDensityTessfactor = 16, |
| Barycentrics = 23, |
| Target = 64, |
| Depth = 65, |
| Coverage = 66, |
| DepthGE = 67, |
| DepthLE = 68, |
| StencilRef = 69, |
| InnerCoverage = 70, |
| }; |
| |
| enum class DxilProgramSigCompType : uint32_t { |
| Unknown = 0, |
| UInt32 = 1, |
| SInt32 = 2, |
| Float32 = 3, |
| UInt16 = 4, |
| SInt16 = 5, |
| Float16 = 6, |
| UInt64 = 7, |
| SInt64 = 8, |
| Float64 = 9, |
| }; |
| |
| static const uint8_t DxilProgramSigMaskX = 1; |
| static const uint8_t DxilProgramSigMaskY = 2; |
| static const uint8_t DxilProgramSigMaskZ = 4; |
| static const uint8_t DxilProgramSigMaskW = 8; |
| |
| struct DxilProgramSignatureElement { |
| uint32_t Stream; // Stream index (parameters must appear in non-decreasing stream order) |
| uint32_t SemanticName; // Offset to LPCSTR from start of DxilProgramSignature. |
| uint32_t SemanticIndex; // Semantic Index |
| DxilProgramSigSemantic SystemValue; // Semantic type. Similar to DxilSemantic::Kind, but a serialized rather than processing rep. |
| DxilProgramSigCompType CompType; // Type of bits. |
| uint32_t Register; // Register Index (row index) |
| uint8_t Mask; // Mask (column allocation) |
| union // Unconditional cases useful for validation of shader linkage. |
| { |
| uint8_t NeverWrites_Mask; // For an output signature, the shader the signature belongs to never |
| // writes the masked components of the output register. |
| uint8_t AlwaysReads_Mask; // For an input signature, the shader the signature belongs to always |
| // reads the masked components of the input register. |
| }; |
| uint16_t Pad; |
| DxilProgramSigMinPrecision MinPrecision; // Minimum precision of input/output data |
| }; |
| |
| // Easy to get this wrong. Earlier assertions can help determine |
| static_assert(sizeof(DxilProgramSignatureElement) == 0x20, "else DxilProgramSignatureElement is misaligned"); |
| |
| struct DxilShaderDebugName { |
| uint16_t Flags; // Reserved, must be set to zero. |
| uint16_t NameLength; // Length of the debug name, without null terminator. |
| // Followed by NameLength bytes of the UTF-8-encoded name. |
| // Followed by a null terminator. |
| // Followed by [0-3] zero bytes to align to a 4-byte boundary. |
| }; |
| static const size_t MinDxilShaderDebugNameSize = sizeof(DxilShaderDebugName) + 4; |
| |
| #pragma pack(pop) |
| |
| /// Gets a part header by index. |
| inline const DxilPartHeader * |
| GetDxilContainerPart(const DxilContainerHeader *pHeader, uint32_t index) { |
| const uint8_t *pLinearContainer = reinterpret_cast<const uint8_t *>(pHeader); |
| const uint32_t *pPartOffsetTable = |
| reinterpret_cast<const uint32_t *>(pHeader + 1); |
| return reinterpret_cast<const DxilPartHeader *>( |
| pLinearContainer + pPartOffsetTable[index]); |
| } |
| |
| /// Gets a part header by index. |
| inline DxilPartHeader *GetDxilContainerPart(DxilContainerHeader *pHeader, |
| uint32_t index) { |
| return const_cast<DxilPartHeader *>(GetDxilContainerPart( |
| reinterpret_cast<const DxilContainerHeader *>(pHeader), index)); |
| } |
| |
| /// Gets the part data from the header. |
| inline const char *GetDxilPartData(const DxilPartHeader *pPart) { |
| return reinterpret_cast<const char *>(pPart + 1); |
| } |
| |
| /// Gets the part data from the header. |
| inline char *GetDxilPartData(DxilPartHeader *pPart) { |
| return reinterpret_cast<char *>(pPart + 1); |
| } |
| /// Gets a part header by fourCC |
| DxilPartHeader *GetDxilPartByType(DxilContainerHeader *pHeader, |
| DxilFourCC fourCC); |
| /// Gets a part header by fourCC |
| const DxilPartHeader * |
| GetDxilPartByType(const DxilContainerHeader *pHeader, |
| DxilFourCC fourCC); |
| |
| /// Returns valid DxilProgramHeader. nullptr if does not exist. |
| DxilProgramHeader *GetDxilProgramHeader(DxilContainerHeader *pHeader, DxilFourCC fourCC); |
| |
| /// Returns valid DxilProgramHeader. nullptr if does not exist. |
| const DxilProgramHeader * |
| GetDxilProgramHeader(const DxilContainerHeader *pHeader, DxilFourCC fourCC); |
| |
| /// Initializes container with the specified values. |
| void InitDxilContainer(_Out_ DxilContainerHeader *pHeader, uint32_t partCount, |
| uint32_t containerSizeInBytes); |
| |
| /// Checks whether pHeader claims by signature to be a DXIL container. |
| const DxilContainerHeader *IsDxilContainerLike(const void *ptr, size_t length); |
| |
| /// Checks whether the DXIL container is valid and in-bounds. |
| bool IsValidDxilContainer(const DxilContainerHeader *pHeader, size_t length); |
| |
| /// Use this type as a unary predicate functor. |
| struct DxilPartIsType { |
| uint32_t IsFourCC; |
| DxilPartIsType(uint32_t FourCC) : IsFourCC(FourCC) { } |
| bool operator()(const DxilPartHeader *pPart) const { |
| return pPart->PartFourCC == IsFourCC; |
| } |
| }; |
| |
| /// Use this type as an iterator over the part headers. |
| struct DxilPartIterator : public std::iterator<std::input_iterator_tag, |
| const DxilContainerHeader *> { |
| const DxilContainerHeader *pHeader; |
| uint32_t index; |
| |
| DxilPartIterator(const DxilContainerHeader *h, uint32_t i) |
| : pHeader(h), index(i) {} |
| |
| // increment |
| DxilPartIterator &operator++() { |
| ++index; |
| return *this; |
| } |
| DxilPartIterator operator++(int) { |
| DxilPartIterator result(pHeader, index); |
| ++index; |
| return result; |
| } |
| |
| // input iterator - compare and deref |
| bool operator==(const DxilPartIterator &other) const { |
| return index == other.index && pHeader == other.pHeader; |
| } |
| bool operator!=(const DxilPartIterator &other) const { |
| return index != other.index || pHeader != other.pHeader; |
| } |
| const DxilPartHeader *operator*() const { |
| return GetDxilContainerPart(pHeader, index); |
| } |
| }; |
| |
| DxilPartIterator begin(const DxilContainerHeader *pHeader); |
| DxilPartIterator end(const DxilContainerHeader *pHeader); |
| |
| inline bool IsValidDxilBitcodeHeader(const DxilBitcodeHeader *pHeader, |
| uint32_t length) { |
| return length > sizeof(DxilBitcodeHeader) && |
| pHeader->BitcodeOffset + pHeader->BitcodeSize > |
| pHeader->BitcodeOffset && |
| length >= pHeader->BitcodeOffset + pHeader->BitcodeSize && |
| pHeader->DxilMagic == DxilMagicValue; |
| } |
| |
| inline void InitBitcodeHeader(DxilBitcodeHeader &header, |
| uint32_t dxilVersion, |
| uint32_t bitcodeSize) { |
| header.DxilMagic = DxilMagicValue; |
| header.DxilVersion = dxilVersion; |
| header.BitcodeOffset = sizeof(DxilBitcodeHeader); |
| header.BitcodeSize = bitcodeSize; |
| } |
| |
| inline void GetDxilProgramBitcode(const DxilProgramHeader *pHeader, |
| const char **pBitcode, |
| uint32_t *pBitcodeLength) { |
| *pBitcode = reinterpret_cast<const char *>(&pHeader->BitcodeHeader) + |
| pHeader->BitcodeHeader.BitcodeOffset; |
| *pBitcodeLength = pHeader->BitcodeHeader.BitcodeSize; |
| } |
| |
| inline bool IsValidDxilProgramHeader(const DxilProgramHeader *pHeader, |
| uint32_t length) { |
| return length >= sizeof(DxilProgramHeader) && |
| length >= (pHeader->SizeInUint32 * sizeof(uint32_t)) && |
| IsValidDxilBitcodeHeader( |
| &pHeader->BitcodeHeader, |
| length - offsetof(DxilProgramHeader, BitcodeHeader)); |
| } |
| |
| inline void InitProgramHeader(DxilProgramHeader &header, uint32_t shaderVersion, |
| uint32_t dxilVersion, |
| uint32_t bitcodeSize) { |
| header.ProgramVersion = shaderVersion; |
| header.SizeInUint32 = |
| sizeof(DxilProgramHeader) / sizeof(uint32_t) + |
| bitcodeSize / sizeof(uint32_t) + ((bitcodeSize % 4) ? 1 : 0); |
| InitBitcodeHeader(header.BitcodeHeader, dxilVersion, bitcodeSize); |
| } |
| |
| inline const char *GetDxilBitcodeData(const DxilProgramHeader *pHeader) { |
| const DxilBitcodeHeader *pBCHdr = &(pHeader->BitcodeHeader); |
| return (const char *)pBCHdr + pBCHdr->BitcodeOffset; |
| } |
| |
| inline uint32_t GetDxilBitcodeSize(const DxilProgramHeader *pHeader) { |
| return pHeader->BitcodeHeader.BitcodeSize; |
| } |
| |
| /// Extract the shader type from the program version value. |
| inline DXIL::ShaderKind GetVersionShaderType(uint32_t programVersion) { |
| return (DXIL::ShaderKind)((programVersion & 0xffff0000) >> 16); |
| } |
| inline uint32_t GetVersionMajor(uint32_t programVersion) { |
| return (programVersion & 0xf0) >> 4; |
| } |
| inline uint32_t GetVersionMinor(uint32_t programVersion) { |
| return (programVersion & 0xf); |
| } |
| inline uint32_t EncodeVersion(DXIL::ShaderKind shaderType, uint32_t major, |
| uint32_t minor) { |
| return ((unsigned)shaderType << 16) | (major << 4) | minor; |
| } |
| |
| inline bool IsDxilShaderDebugNameValid(const DxilPartHeader *pPart) { |
| if (pPart->PartFourCC != DFCC_ShaderDebugName) return false; |
| if (pPart->PartSize < MinDxilShaderDebugNameSize) return false; |
| const DxilShaderDebugName *pDebugNameContent = reinterpret_cast<const DxilShaderDebugName *>(GetDxilPartData(pPart)); |
| uint16_t ExpectedSize = sizeof(DxilShaderDebugName) + pDebugNameContent->NameLength + 1; |
| if (ExpectedSize & 0x3) { |
| ExpectedSize += 0x4; |
| ExpectedSize &= ~(0x3); |
| } |
| if (pPart->PartSize != ExpectedSize) return false; |
| return true; |
| } |
| |
| inline bool GetDxilShaderDebugName(const DxilPartHeader *pDebugNamePart, |
| const char **ppUtf8Name, _Out_opt_ uint16_t *pUtf8NameLen) { |
| *ppUtf8Name = nullptr; |
| if (!IsDxilShaderDebugNameValid(pDebugNamePart)) { |
| return false; |
| } |
| const DxilShaderDebugName *pDebugNameContent = reinterpret_cast<const DxilShaderDebugName *>(GetDxilPartData(pDebugNamePart)); |
| if (pUtf8NameLen) { |
| *pUtf8NameLen = pDebugNameContent->NameLength; |
| } |
| *ppUtf8Name = (const char *)(pDebugNameContent + 1); |
| return true; |
| } |
| |
| class DxilPartWriter { |
| public: |
| virtual ~DxilPartWriter() {} |
| virtual uint32_t size() const = 0; |
| virtual void write(AbstractMemoryStream *pStream) = 0; |
| }; |
| |
| DxilPartWriter *NewProgramSignatureWriter(const DxilModule &M, DXIL::SignatureKind Kind); |
| DxilPartWriter *NewRootSignatureWriter(const RootSignatureHandle &S); |
| DxilPartWriter *NewFeatureInfoWriter(const DxilModule &M); |
| DxilPartWriter *NewPSVWriter(const DxilModule &M, uint32_t PSVVersion = 0); |
| |
| class DxilContainerWriter : public DxilPartWriter { |
| public: |
| typedef std::function<void(AbstractMemoryStream*)> WriteFn; |
| virtual ~DxilContainerWriter() {} |
| virtual void AddPart(uint32_t FourCC, uint32_t Size, WriteFn Write) = 0; |
| }; |
| |
| DxilContainerWriter *NewDxilContainerWriter(); |
| |
| enum class SerializeDxilFlags : uint32_t { |
| None = 0, // No flags defined. |
| IncludeDebugInfoPart = 1, // Include the debug info part in the container. |
| IncludeDebugNamePart = 2, // Include the debug name part in the container. |
| DebugNameDependOnSource = 4 // Make the debug name depend on source (and not just final module). |
| }; |
| inline SerializeDxilFlags& operator |=(SerializeDxilFlags& l, const SerializeDxilFlags& r) { |
| l = static_cast<SerializeDxilFlags>(static_cast<int>(l) | static_cast<int>(r)); |
| return l; |
| } |
| inline SerializeDxilFlags& operator &=(SerializeDxilFlags& l, const SerializeDxilFlags& r) { |
| l = static_cast<SerializeDxilFlags>(static_cast<int>(l) & static_cast<int>(r)); |
| return l; |
| } |
| inline int operator&(SerializeDxilFlags l, SerializeDxilFlags r) { |
| return static_cast<int>(l) & static_cast<int>(r); |
| } |
| inline SerializeDxilFlags operator~(SerializeDxilFlags l) { |
| return static_cast<SerializeDxilFlags>(~static_cast<uint32_t>(l)); |
| } |
| |
| void SerializeDxilContainerForModule(hlsl::DxilModule *pModule, |
| AbstractMemoryStream *pModuleBitcode, |
| AbstractMemoryStream *pStream, |
| SerializeDxilFlags Flags); |
| void SerializeDxilContainerForRootSignature(hlsl::RootSignatureHandle *pRootSigHandle, |
| AbstractMemoryStream *pStream); |
| |
| void CreateDxcContainerReflection(IDxcContainerReflection **ppResult); |
| |
| // Converts uint32_t partKind to char array object. |
| inline char * PartKindToCharArray(uint32_t partKind, _Out_writes_(5) char* pText) { |
| pText[0] = (char)((partKind & 0x000000FF) >> 0); |
| pText[1] = (char)((partKind & 0x0000FF00) >> 8); |
| pText[2] = (char)((partKind & 0x00FF0000) >> 16); |
| pText[3] = (char)((partKind & 0xFF000000) >> 24); |
| pText[4] = '\0'; |
| return pText; |
| } |
| |
| inline size_t GetOffsetTableSize(uint32_t partCount) { |
| return sizeof(uint32_t) * partCount; |
| } |
| // Compute total size of the dxil container from parts information |
| inline size_t GetDxilContainerSizeFromParts(uint32_t partCount, uint32_t partsSize) { |
| return partsSize + (uint32_t)sizeof(DxilContainerHeader) + |
| GetOffsetTableSize(partCount) + |
| (uint32_t)sizeof(DxilPartHeader) * partCount; |
| } |
| |
| } // namespace hlsl |
| |
| #endif // __DXC_CONTAINER__ |