| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // DxilLibraryReflection.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. // |
| // // |
| // Defines shader reflection for runtime usage. // |
| // // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #pragma once |
| |
| #include "dxc/DXIL/DxilConstants.h" |
| |
| #include <cstddef> |
| |
| #define RDAT_NULL_REF ((uint32_t)0xFFFFFFFF) |
| |
| namespace hlsl { |
| namespace RDAT { |
| |
| // Data Layout: |
| // -start: |
| // RuntimeDataHeader header; |
| // uint32_t offsets[header.PartCount]; |
| // - for each i in header.PartCount: |
| // - at &header + offsets[i]: |
| // RuntimeDataPartHeader part; |
| // - if part.Type is a Table (Function or Resource): |
| // RuntimeDataTableHeader table; |
| // byte TableData[table.RecordCount][table.RecordStride]; |
| // - else if part.Type is String: |
| // byte UTF8Data[part.Size]; |
| // - else if part.Type is Index: |
| // uint32_t IndexData[part.Size / 4]; |
| |
| enum RuntimeDataVersion { |
| // Cannot be mistaken for part count from prerelease version |
| RDAT_Version_10 = 0x10, |
| }; |
| |
| enum class RuntimeDataGroup : uint32_t { |
| Core = 0, |
| PdbInfo = 1, |
| }; |
| |
| constexpr uint32_t RDAT_PART_ID_WITH_GROUP(RuntimeDataGroup group, |
| uint32_t id) { |
| return (((uint32_t)(group) << 16) | ((id)&0xFFFF)); |
| } |
| |
| enum class RuntimeDataPartType : uint32_t { |
| Invalid = 0, |
| StringBuffer = 1, |
| IndexArrays = 2, |
| ResourceTable = 3, |
| FunctionTable = 4, |
| Last_1_3 = FunctionTable, |
| RawBytes = 5, |
| SubobjectTable = 6, |
| Last_1_4 = SubobjectTable, |
| |
| NodeIDTable = 7, |
| NodeShaderIOAttribTable = 8, |
| NodeShaderFuncAttribTable = 9, |
| IONodeTable = 10, |
| NodeShaderInfoTable = 11, |
| Last_1_8 = NodeShaderInfoTable, |
| |
| // Insert experimental here. |
| SignatureElementTable, |
| VSInfoTable, |
| PSInfoTable, |
| HSInfoTable, |
| DSInfoTable, |
| GSInfoTable, |
| CSInfoTable, |
| MSInfoTable, |
| ASInfoTable, |
| |
| LastPlus1, |
| LastExperimental = LastPlus1 - 1, |
| |
| DxilPdbInfoTable = RDAT_PART_ID_WITH_GROUP(RuntimeDataGroup::PdbInfo, 1), |
| DxilPdbInfoSourceTable = |
| RDAT_PART_ID_WITH_GROUP(RuntimeDataGroup::PdbInfo, 2), |
| DxilPdbInfoLibraryTable = |
| RDAT_PART_ID_WITH_GROUP(RuntimeDataGroup::PdbInfo, 3), |
| }; |
| |
| inline RuntimeDataPartType MaxPartTypeForValVer(unsigned Major, |
| unsigned Minor) { |
| return DXIL::CompareVersions(Major, Minor, 1, 3) < 0 |
| ? RuntimeDataPartType::Invalid // No RDAT before 1.3 |
| : DXIL::CompareVersions(Major, Minor, 1, 4) < 0 |
| ? RuntimeDataPartType::Last_1_3 |
| : DXIL::CompareVersions(Major, Minor, 1, 8) < 0 |
| ? RuntimeDataPartType::Last_1_4 |
| : DXIL::CompareVersions(Major, Minor, 1, 8) == 0 |
| ? RuntimeDataPartType::Last_1_8 |
| : RuntimeDataPartType::LastExperimental; |
| } |
| |
| enum class RecordTableIndex : unsigned { |
| ResourceTable, |
| FunctionTable, |
| SubobjectTable, |
| |
| NodeIDTable, |
| NodeShaderIOAttribTable, |
| NodeShaderFuncAttribTable, |
| IONodeTable, |
| NodeShaderInfoTable, |
| |
| DxilPdbInfoTable, |
| DxilPdbInfoSourceTable, |
| DxilPdbInfoLibraryTable, |
| |
| SignatureElementTable, |
| VSInfoTable, |
| PSInfoTable, |
| HSInfoTable, |
| DSInfoTable, |
| GSInfoTable, |
| CSInfoTable, |
| MSInfoTable, |
| ASInfoTable, |
| |
| RecordTableCount |
| }; |
| |
| /////////////////////////////////////// |
| // Header Structures |
| |
| struct RuntimeDataHeader { |
| uint32_t Version; |
| uint32_t PartCount; |
| // Followed by uint32_t array of offsets to parts |
| // offsets are relative to the beginning of this header |
| // offsets must be 4-byte aligned |
| // uint32_t offsets[]; |
| }; |
| struct RuntimeDataPartHeader { |
| RuntimeDataPartType Type; |
| uint32_t Size; // Not including this header. Must be 4-byte aligned. |
| // Followed by part data |
| // byte Data[ALIGN4(Size)]; |
| }; |
| |
| // For tables of records, such as Function and Resource tables |
| // Stride allows for extending records, with forward and backward compatibility |
| struct RuntimeDataTableHeader { |
| uint32_t RecordCount; |
| uint32_t RecordStride; // Must be 4-byte aligned. |
| // Followed by recordCount records of recordStride size |
| // byte TableData[RecordCount * RecordStride]; |
| }; |
| |
| /////////////////////////////////////// |
| // Raw Reader Classes |
| |
| // General purpose strided table reader with casting Row() operation that |
| // returns nullptr if stride is smaller than type, for record expansion. |
| class TableReader { |
| const char *m_table; |
| uint32_t m_count; |
| uint32_t m_stride; |
| |
| public: |
| TableReader() : TableReader(nullptr, 0, 0) {} |
| TableReader(const char *table, uint32_t count, uint32_t stride) |
| : m_table(table), m_count(count), m_stride(stride) {} |
| void Init(const char *table, uint32_t count, uint32_t stride) { |
| m_table = table; |
| m_count = count; |
| m_stride = stride; |
| } |
| const char *Data() const { return m_table; } |
| uint32_t Count() const { return m_count; } |
| uint32_t Stride() const { return m_stride; } |
| |
| template <typename T> const T *Row(uint32_t index) const { |
| if (Valid() && index < m_count && sizeof(T) <= m_stride) |
| return reinterpret_cast<const T *>(m_table + (m_stride * index)); |
| return nullptr; |
| } |
| bool Valid() const { return m_table && m_count && m_stride; } |
| operator bool() { return Valid(); } |
| }; |
| |
| // Index table is a sequence of rows, where each row has a count as a first |
| // element followed by the count number of elements pre computing values |
| class IndexTableReader { |
| private: |
| const uint32_t *m_table; |
| uint32_t m_size; |
| |
| public: |
| class IndexRow { |
| private: |
| const uint32_t *m_values; |
| const uint32_t m_count; |
| |
| public: |
| IndexRow() : m_values(nullptr), m_count(0) {} |
| IndexRow(const uint32_t *values, uint32_t count) |
| : m_values(values), m_count(count) {} |
| uint32_t Count() const { return m_count; } |
| uint32_t At(uint32_t i) const { |
| return (m_values && i < m_count) ? m_values[i] : 0; |
| } |
| const uint32_t &operator[](uint32_t i) const { |
| if (m_values && i < m_count) |
| return m_values[i]; |
| return m_values[0]; // If null, we should AV if value is read |
| } |
| bool empty() const { return !(m_values && m_count > 0); } |
| operator bool() const { return !empty(); } |
| }; |
| |
| IndexTableReader() : IndexTableReader(nullptr, 0) {} |
| IndexTableReader(const uint32_t *table, uint32_t size) |
| : m_table(table), m_size(size) {} |
| void Init(const uint32_t *table, uint32_t size) { |
| m_table = table; |
| m_size = size; |
| } |
| IndexRow getRow(uint32_t i) const { |
| if (Valid() && i < m_size - 1 && m_table[i] + i < m_size) { |
| return IndexRow(&m_table[i] + 1, m_table[i]); |
| } |
| return {}; |
| } |
| const uint32_t *Data() const { return m_table; } |
| uint32_t Count() const { return Valid() ? m_size : 0; } |
| bool Valid() const { return m_table && m_size > 0; } |
| operator bool() const { return Valid(); } |
| }; |
| |
| class StringTableReader { |
| const char *m_table = nullptr; |
| uint32_t m_size = 0; |
| |
| public: |
| void Init(const char *table, uint32_t size) { |
| m_table = table; |
| m_size = size; |
| } |
| const char *Get(uint32_t offset) const { |
| (void)m_size; // avoid unused private warning if use above is ignored. |
| return m_table + offset; |
| } |
| uint32_t Size() const { return m_size; } |
| const char *Data() const { return m_table; } |
| }; |
| |
| class RawBytesReader { |
| const void *m_table; |
| uint32_t m_size; |
| |
| public: |
| RawBytesReader(const void *table, uint32_t size) |
| : m_table(table), m_size(size) {} |
| RawBytesReader() : RawBytesReader(nullptr, 0) {} |
| void Init(const void *table, uint32_t size) { |
| m_table = table; |
| m_size = size; |
| } |
| uint32_t Size() const { return m_size; } |
| const void *Get(uint32_t offset) const { |
| return (const void *)(((const char *)m_table) + offset); |
| } |
| }; |
| |
| /////////////////////////////////////// |
| // Record Traits |
| |
| template <typename _T> class RecordTraits { |
| public: |
| static constexpr const char *TypeName(); |
| |
| static constexpr RuntimeDataPartType PartType(); |
| |
| // If the following static assert is hit, it means a structure defined with |
| // RDAT_STRUCT is being used in ref type, which requires the struct to have |
| // a table and be defined with RDAT_STRUCT_TABLE instead. |
| static constexpr RecordTableIndex TableIndex(); |
| |
| // RecordSize() is defined in order to allow for use of forward decl type in |
| // RecordRef |
| static constexpr size_t RecordSize() { return sizeof(_T); } |
| static constexpr size_t MaxRecordSize() { |
| return RecordTraits<_T>::DerivedRecordSize(); |
| } |
| static constexpr size_t DerivedRecordSize() { return sizeof(_T); } |
| }; |
| |
| /////////////////////////////////////// |
| // RDATContext |
| |
| struct RDATContext { |
| StringTableReader StringBuffer; |
| IndexTableReader IndexTable; |
| RawBytesReader RawBytes; |
| TableReader Tables[(unsigned)RecordTableIndex::RecordTableCount]; |
| const TableReader &Table(RecordTableIndex idx) const { |
| if (idx < RecordTableIndex::RecordTableCount) |
| return Tables[(unsigned)idx]; |
| return Tables[0]; // TODO: assert |
| } |
| TableReader &Table(RecordTableIndex idx) { |
| return const_cast<TableReader &>(((const RDATContext *)this)->Table(idx)); |
| } |
| template <typename RecordType> const TableReader &Table() const { |
| static_assert(RecordTraits<RecordType>::TableIndex() < |
| RecordTableIndex::RecordTableCount, |
| ""); |
| return Table(RecordTraits<RecordType>::TableIndex()); |
| } |
| template <typename RecordType> TableReader &Table() { |
| return const_cast<TableReader &>( |
| ((const RDATContext *)this) |
| ->Table(RecordTraits<RecordType>::TableIndex())); |
| } |
| }; |
| |
| /////////////////////////////////////// |
| // Generic Reader Classes |
| |
| class BaseRecordReader { |
| protected: |
| const RDATContext *m_pContext = nullptr; |
| const void *m_pRecord = nullptr; |
| uint32_t m_Size = 0; |
| |
| template <typename _ReaderTy> const _ReaderTy asReader() const { |
| if (*this && |
| m_Size >= RecordTraits<typename _ReaderTy::RecordType>::RecordSize()) |
| return _ReaderTy(*this); |
| return {}; |
| } |
| |
| template <typename _T> const _T *asRecord() const { |
| return static_cast<const _T *>( |
| (*this && m_Size >= RecordTraits<_T>::RecordSize()) ? m_pRecord |
| : nullptr); |
| } |
| |
| void InvalidateReader() { |
| m_pContext = nullptr; |
| m_pRecord = nullptr; |
| m_Size = 0; |
| } |
| |
| public: |
| BaseRecordReader(const RDATContext *ctx, const void *record, uint32_t size) |
| : m_pContext(ctx), m_pRecord(record), m_Size(size) {} |
| BaseRecordReader() : BaseRecordReader(nullptr, nullptr, 0) {} |
| |
| // Is this a valid reader |
| operator bool() const { |
| return m_pContext != nullptr && m_pRecord != nullptr && m_Size != 0; |
| } |
| const RDATContext *GetContext() const { return m_pContext; } |
| }; |
| |
| template <typename _ReaderTy> class RecordArrayReader { |
| const RDATContext *m_pContext; |
| const uint32_t m_IndexOffset; |
| |
| public: |
| RecordArrayReader(const RDATContext *ctx, uint32_t indexOffset) |
| : m_pContext(ctx), m_IndexOffset(indexOffset) { |
| typedef typename _ReaderTy::RecordType RecordType; |
| const TableReader &Table = m_pContext->Table<RecordType>(); |
| // RecordArrays must be declared with the base record type, |
| // with element reader upcast as necessary. |
| if (Table.Stride() < RecordTraits<RecordType>::RecordSize()) |
| InvalidateReader(); |
| } |
| RecordArrayReader() : RecordArrayReader(nullptr, 0) {} |
| uint32_t Count() const { |
| return *this ? m_pContext->IndexTable.getRow(m_IndexOffset).Count() : 0; |
| } |
| const _ReaderTy operator[](uint32_t idx) const { |
| typedef typename _ReaderTy::RecordType RecordType; |
| if (*this) { |
| const TableReader &Table = m_pContext->Table<RecordType>(); |
| return _ReaderTy(BaseRecordReader( |
| m_pContext, |
| (const void *)Table.Row<RecordType>( |
| m_pContext->IndexTable.getRow(m_IndexOffset).At(idx)), |
| Table.Stride())); |
| } |
| return {}; |
| } |
| // Is this a valid reader |
| operator bool() const { |
| return m_pContext != nullptr && m_IndexOffset < RDAT_NULL_REF; |
| } |
| void InvalidateReader() { m_pContext = nullptr; } |
| const RDATContext *GetContext() const { return m_pContext; } |
| }; |
| |
| class StringArrayReader { |
| const RDATContext *m_pContext; |
| const uint32_t m_IndexOffset; |
| |
| public: |
| StringArrayReader(const RDATContext *pContext, uint32_t indexOffset) |
| : m_pContext(pContext), m_IndexOffset(indexOffset) {} |
| uint32_t Count() const { |
| return *this ? m_pContext->IndexTable.getRow(m_IndexOffset).Count() : 0; |
| } |
| const char *operator[](uint32_t idx) const { |
| return *this ? m_pContext->StringBuffer.Get( |
| m_pContext->IndexTable.getRow(m_IndexOffset).At(idx)) |
| : 0; |
| } |
| // Is this a valid reader |
| operator bool() const { |
| return m_pContext != nullptr && m_IndexOffset < RDAT_NULL_REF; |
| } |
| void InvalidateReader() { m_pContext = nullptr; } |
| const RDATContext *GetContext() const { return m_pContext; } |
| }; |
| |
| /////////////////////////////////////// |
| // Field Helpers |
| |
| template <typename _T> struct RecordRef { |
| uint32_t Index; |
| |
| template <typename RecordType = _T> |
| const _T *Get(const RDATContext &ctx) const { |
| return ctx.Table<_T>().template Row<RecordType>(Index); |
| } |
| RecordRef &operator=(uint32_t index) { |
| Index = index; |
| return *this; |
| } |
| operator uint32_t &() { return Index; } |
| operator const uint32_t &() const { return Index; } |
| uint32_t *operator&() { return &Index; } |
| }; |
| |
| template <typename _T> struct RecordArrayRef { |
| uint32_t Index; |
| |
| RecordArrayReader<_T> Get(const RDATContext &ctx) const { |
| return RecordArrayReader<_T>(ctx.IndexTable, ctx.Table<_T>(), Index); |
| } |
| RecordArrayRef &operator=(uint32_t index) { |
| Index = index; |
| return *this; |
| } |
| operator uint32_t &() { return Index; } |
| operator const uint32_t &() const { return Index; } |
| uint32_t *operator&() { return &Index; } |
| }; |
| |
| struct RDATString { |
| uint32_t Offset; |
| |
| const char *Get(const RDATContext &ctx) const { |
| return ctx.StringBuffer.Get(Offset); |
| } |
| RDATString &operator=(uint32_t offset) { |
| Offset = offset; |
| return *this; |
| } |
| operator uint32_t &() { return Offset; } |
| operator const uint32_t &() const { return Offset; } |
| uint32_t *operator&() { return &Offset; } |
| }; |
| |
| struct RDATStringArray { |
| uint32_t Index; |
| |
| StringArrayReader Get(const RDATContext &ctx) const { |
| return StringArrayReader(&ctx, Index); |
| } |
| operator bool() const { return Index == 0 ? false : true; } |
| RDATStringArray &operator=(uint32_t index) { |
| Index = index; |
| return *this; |
| } |
| operator uint32_t &() { return Index; } |
| operator const uint32_t &() const { return Index; } |
| uint32_t *operator&() { return &Index; } |
| }; |
| |
| struct IndexArrayRef { |
| uint32_t Index; |
| |
| IndexTableReader::IndexRow Get(const RDATContext &ctx) const { |
| return ctx.IndexTable.getRow(Index); |
| } |
| IndexArrayRef &operator=(uint32_t index) { |
| Index = index; |
| return *this; |
| } |
| operator uint32_t &() { return Index; } |
| operator const uint32_t &() const { return Index; } |
| uint32_t *operator&() { return &Index; } |
| }; |
| |
| struct BytesRef { |
| uint32_t Offset; |
| uint32_t Size; |
| |
| const void *GetBytes(const RDATContext &ctx) const { |
| return ctx.RawBytes.Get(Offset); |
| } |
| template <typename _T> const _T *GetAs(const RDATContext &ctx) const { |
| return (sizeof(_T) > Size) |
| ? nullptr |
| : reinterpret_cast<const _T *>(ctx.RawBytes.Get(Offset)); |
| } |
| uint32_t *operator&() { return &Offset; } |
| }; |
| |
| struct BytesPtr { |
| const void *Ptr = nullptr; |
| uint32_t Size = 0; |
| |
| BytesPtr(const void *ptr, uint32_t size) : Ptr(ptr), Size(size) {} |
| BytesPtr() : BytesPtr(nullptr, 0) {} |
| template <typename _T> const _T *GetAs() const { |
| return (sizeof(_T) > Size) ? nullptr : reinterpret_cast<const _T *>(Ptr); |
| } |
| }; |
| |
| /////////////////////////////////////// |
| // Record Helpers |
| |
| template <typename _RecordReader> class RecordReader : public BaseRecordReader { |
| public: |
| typedef _RecordReader ThisReaderType; |
| RecordReader(const BaseRecordReader &base) : BaseRecordReader(base) { |
| typedef typename _RecordReader::RecordType RecordType; |
| if ((m_pContext || m_pRecord) && |
| m_Size < RecordTraits<RecordType>::RecordSize()) |
| InvalidateReader(); |
| } |
| RecordReader() : BaseRecordReader() {} |
| template <typename _ReaderType> _ReaderType as() { _ReaderType(*this); } |
| |
| protected: |
| template <typename _FieldRecordReader> |
| _FieldRecordReader GetField_RecordValue(const void *pField) const { |
| if (*this) { |
| return _FieldRecordReader(BaseRecordReader( |
| m_pContext, pField, |
| (uint32_t)RecordTraits< |
| typename _FieldRecordReader::RecordType>::RecordSize())); |
| } |
| return {}; |
| } |
| template <typename _FieldRecordReader> |
| _FieldRecordReader GetField_RecordRef(const void *pIndex) const { |
| typedef typename _FieldRecordReader::RecordType RecordType; |
| if (*this) { |
| const TableReader &Table = m_pContext->Table<RecordType>(); |
| return _FieldRecordReader(BaseRecordReader( |
| m_pContext, |
| (const void *)Table.Row<RecordType>(*(const uint32_t *)pIndex), |
| Table.Stride())); |
| } |
| return {}; |
| } |
| template <typename _FieldRecordReader> |
| RecordArrayReader<_FieldRecordReader> |
| GetField_RecordArrayRef(const void *pIndex) const { |
| if (*this) { |
| return RecordArrayReader<_FieldRecordReader>(m_pContext, |
| *(const uint32_t *)pIndex); |
| } |
| return {}; |
| } |
| template <typename _T, typename _StorageTy> |
| _T GetField_Value(const _StorageTy *value) const { |
| _T result = {}; |
| if (*this) |
| result = (_T)*value; |
| return result; |
| } |
| IndexTableReader::IndexRow GetField_IndexArray(const void *pIndex) const { |
| if (*this) { |
| return m_pContext->IndexTable.getRow(*(const uint32_t *)pIndex); |
| } |
| return {}; |
| } |
| // Would use std::array, but don't want this header dependent on that. |
| // Array reference syntax is almost enough reason to abandon C++!!! |
| template <typename _T, size_t _ArraySize> |
| decltype(auto) GetField_ValueArray(_T const (&value)[_ArraySize]) const { |
| typedef _T ArrayType[_ArraySize]; |
| if (*this) |
| return value; |
| return *(const ArrayType *)nullptr; |
| } |
| const char *GetField_String(const void *pIndex) const { |
| return *this ? m_pContext->StringBuffer.Get(*(const uint32_t *)pIndex) |
| : nullptr; |
| } |
| StringArrayReader GetField_StringArray(const void *pIndex) const { |
| return *this ? StringArrayReader(m_pContext, *(const uint32_t *)pIndex) |
| : StringArrayReader(nullptr, 0); |
| } |
| const void *GetField_Bytes(const void *pIndex) const { |
| return *this ? m_pContext->RawBytes.Get(*(const uint32_t *)pIndex) |
| : nullptr; |
| } |
| uint32_t GetField_BytesSize(const void *pIndex) const { |
| return *this ? *(((const uint32_t *)pIndex) + 1) : 0; |
| } |
| }; |
| |
| template <typename _RecordReader> class RecordTableReader { |
| const RDATContext *m_pContext; |
| |
| public: |
| RecordTableReader(const RDATContext *pContext) : m_pContext(pContext) {} |
| template <typename RecordReaderType = _RecordReader> |
| RecordReaderType Row(uint32_t index) const { |
| typedef typename _RecordReader::RecordType RecordType; |
| const TableReader &Table = m_pContext->Table<RecordType>(); |
| return RecordReaderType(BaseRecordReader( |
| m_pContext, Table.Row<RecordType>(index), Table.Stride())); |
| } |
| uint32_t Count() const { |
| return m_pContext->Table<typename _RecordReader::RecordType>().Count(); |
| } |
| uint32_t size() const { return Count(); } |
| const _RecordReader operator[](uint32_t index) const { return Row(index); } |
| operator bool() { return m_pContext && Count(); } |
| }; |
| |
| ///////////////////////////// |
| // All RDAT enums and types |
| |
| #define DEF_RDAT_ENUMS DEF_RDAT_ENUM_CLASS |
| #define DEF_RDAT_TYPES DEF_RDAT_TYPES_FORWARD_DECL |
| #include "dxc/DxilContainer/RDAT_Macros.inl" |
| |
| #define DEF_RDAT_TYPES DEF_RDAT_TYPES_USE_HELPERS |
| #include "dxc/DxilContainer/RDAT_Macros.inl" |
| |
| #define DEF_RDAT_TYPES DEF_RDAT_TRAITS |
| #include "dxc/DxilContainer/RDAT_Macros.inl" |
| |
| #define DEF_RDAT_TYPES DEF_RDAT_READER_DECL |
| #include "dxc/DxilContainer/RDAT_Macros.inl" |
| |
| ///////////////////////////// |
| ///////////////////////////// |
| |
| class DxilRuntimeData { |
| private: |
| RDATContext m_Context; |
| size_t m_DataSize = 0; |
| |
| public: |
| DxilRuntimeData(); |
| DxilRuntimeData(const void *ptr, size_t size); |
| // initializing reader from RDAT. return true if no error has occured. |
| bool InitFromRDAT(const void *pRDAT, size_t size); |
| |
| // Make sure data is well formed. |
| bool Validate(); |
| |
| size_t GetDataSize() const { return m_DataSize; } |
| |
| RDATContext &GetContext() { return m_Context; } |
| const RDATContext &GetContext() const { return m_Context; } |
| |
| #define RDAT_STRUCT_TABLE(type, table) \ |
| RecordTableReader<type##_Reader> Get##table() const { \ |
| return RecordTableReader<type##_Reader>(&m_Context); \ |
| } |
| #define DEF_RDAT_TYPES DEF_RDAT_DEFAULTS |
| #include "dxc/DxilContainer/RDAT_Macros.inl" |
| }; |
| |
| } // namespace RDAT |
| } // namespace hlsl |