diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-10-29 14:50:15 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-10-29 14:50:15 -0400 |
| commit | 066bc37f34ab4f72edef2b71fab50b45c3bb627e (patch) | |
| tree | 6880c04996a7c2b964983d6a006e77a55a75fcde /source | |
| parent | c27b7d91aaf6bc764807a8998a9c885e57c22a1b (diff) | |
Feature/riff improvements (#1099)
* Added RiffReadHelper
* Move type to fourCC in Chunk simplifies some code.
* Make MemoryArena able to track external blocks.
Allow ownership of Data to vary.
Changed IR serialization to use moved allocations to avoid copies.
As it turns out all of the array writes could use unowned data, but doing so requires the IRData to stay in scope longer than IRSerialData, which it does at the moment - but perhaps needs better naming or a control for the feature.
Diffstat (limited to 'source')
| -rw-r--r-- | source/core/slang-memory-arena.cpp | 36 | ||||
| -rw-r--r-- | source/core/slang-memory-arena.h | 3 | ||||
| -rw-r--r-- | source/core/slang-riff.cpp | 102 | ||||
| -rw-r--r-- | source/core/slang-riff.h | 116 | ||||
| -rw-r--r-- | source/slang/slang-ir-serialize.cpp | 78 |
5 files changed, 223 insertions, 112 deletions
diff --git a/source/core/slang-memory-arena.cpp b/source/core/slang-memory-arena.cpp index a9fc1ec19..6fc6c0c3f 100644 --- a/source/core/slang-memory-arena.cpp +++ b/source/core/slang-memory-arena.cpp @@ -258,6 +258,42 @@ MemoryArena::Block* MemoryArena::_newBlock(size_t allocSize, size_t alignment) return block; } +void MemoryArena::addExternalBlock(void* inData, size_t size) +{ + // Allocate block + Block* block = (Block*)m_blockFreeList.allocate(); + if (!block) + { + return; + } + + uint8_t* alloc = (uint8_t*)inData; + + const size_t alignMask = m_blockAlignment - 1; + + // Do the alignment on the allocation + uint8_t* const start = (uint8_t*)((size_t(alloc) + alignMask) & ~alignMask); + + // Setup the block + block->m_alloc = alloc; + block->m_start = start; + block->m_end = alloc + size; + block->m_next = nullptr; + + // We don't want to place at start, if there is any used blocks - as that is the one + // that is being split from and can be rewound. So we place just behind in that case + if (m_usedBlocks) + { + block->m_next = m_usedBlocks->m_next; + m_usedBlocks->m_next = block; + } + else + { + // There aren't any blocks, so just place at the front + m_usedBlocks = block; + } +} + void* MemoryArena::_allocateAlignedFromNewBlockAndZero(size_t sizeInBytes, size_t alignment) { void* mem = _allocateAlignedFromNewBlock(sizeInBytes, alignment); diff --git a/source/core/slang-memory-arena.h b/source/core/slang-memory-arena.h index 75744710f..d346197f0 100644 --- a/source/core/slang-memory-arena.h +++ b/source/core/slang-memory-arena.h @@ -138,6 +138,9 @@ public: /// Rewind (and effectively deallocate) all allocations *after* the cursor void rewindToCursor(const void* cursor); + /// Add a block such that it will be freed when everything else is freed. + void addExternalBlock(void* data, size_t size); + /// Default Ctor MemoryArena(); /// Construct with block size and alignment. Block alignment must be a power of 2. diff --git a/source/core/slang-riff.cpp b/source/core/slang-riff.cpp index 9849cfc26..3a3722bb8 100644 --- a/source/core/slang-riff.cpp +++ b/source/core/slang-riff.cpp @@ -175,7 +175,7 @@ struct DumpVisitor : public RiffContainer::Visitor // If it's the root it's 'riff' _dumpRiffType(list == m_rootChunk ? RiffFourCC::kRiff : RiffFourCC::kList); m_writer.put(" "); - _dumpRiffType(list->m_subType); + _dumpRiffType(list->getSubType()); m_writer.put("\n"); m_indent++; return SLANG_OK; @@ -184,10 +184,9 @@ struct DumpVisitor : public RiffContainer::Visitor { _dumpIndent(); // Write out the name - _dumpRiffType(data->m_type); + _dumpRiffType(data->m_fourCC); m_writer.put(" "); - int hash = data->calcHash(); // We don't know in general what the contents is or means... but we can display a hash @@ -250,7 +249,7 @@ struct DumpVisitor : public RiffContainer::Visitor listHeader.chunk.type = isRoot ? RiffFourCC::kRiff : RiffFourCC::kList; listHeader.chunk.size = uint32_t(list->m_payloadSize); - listHeader.subType = list->m_subType; + listHeader.subType = list->getSubType(); try { @@ -276,7 +275,7 @@ struct DumpVisitor : public RiffContainer::Visitor // Must be a regular chunk with data RiffHeader chunkHeader; - chunkHeader.type = dataChunk->m_type; + chunkHeader.type = dataChunk->m_fourCC; chunkHeader.size = uint32_t(dataChunk->m_payloadSize); stream->write(&chunkHeader, sizeof(chunkHeader)); @@ -386,7 +385,9 @@ struct DumpVisitor : public RiffContainer::Visitor else { ScopeChunk scopeChunk(&outContainer, Chunk::Kind::Data, header.chunk.type); - RiffContainer::Data* data = outContainer.addData(header.chunk.size); + RiffContainer::Data* data = outContainer.addData(); + + outContainer.setPayload(data, nullptr, header.chunk.size); size_t readSize; SLANG_RETURN_ON_FAIL(readPayload(stream, header.chunk.size, data->getPayload(), readSize)); @@ -520,15 +521,12 @@ size_t RiffContainer::ListChunk::calcPayloadSize() return size; } -RiffContainer::Chunk* RiffContainer::ListChunk::findContained(FourCC type) const +RiffContainer::Chunk* RiffContainer::ListChunk::findContained(FourCC fourCC) const { Chunk* chunk = m_containedChunks; while (chunk) { - const FourCC checkType = (chunk->m_kind == Chunk::Kind::Data) ? - static_cast<RiffContainer::DataChunk*>(chunk)->m_type : - static_cast<RiffContainer::ListChunk*>(chunk)->m_subType; - if (checkType == type) + if (chunk->m_fourCC == fourCC) { return chunk; } @@ -568,7 +566,7 @@ static RiffContainer::ListChunk* _findListRec(RiffContainer::ListChunk* list, Fo if (auto childList = as<RiffContainer::ListChunk>(chunk)) { // Test if the child is the subtype, if so we are done - if (childList->m_subType == subType) + if (childList->getSubType() == subType) { return childList; } @@ -585,23 +583,25 @@ static RiffContainer::ListChunk* _findListRec(RiffContainer::ListChunk* list, Fo /* static */RiffContainer::ListChunk* RiffContainer::ListChunk::findListRec(FourCC subType) { - return (m_subType == subType) ? this : _findListRec(this, subType); + return (getSubType() == subType) ? this : _findListRec(this, subType); } // !!!!!!!!!!!!!!!!!!!!!!!!!!! RiffContainer::DataChunk !!!!!!!!!!!!!!!!!!!!!! RiffContainer::Data* RiffContainer::DataChunk::getSingleData() const { - if (m_kind == Kind::Data) + Data* data = m_dataList; + return (data && data->m_next == nullptr) ? data : nullptr; +} + +RiffReadHelper RiffContainer::DataChunk::asReadHelper() const +{ + Data* data = getSingleData(); + if (data) { - auto dataChunk = static_cast<const DataChunk*>(this); - Data* data = dataChunk->m_dataList; - if (data && data->m_next == nullptr) - { - return data; - } + return RiffReadHelper((const uint8_t*)data->getPayload(), data->getSize()); } - return nullptr; + return RiffReadHelper(nullptr, 0); } int RiffContainer::DataChunk::calcHash() const @@ -765,19 +765,67 @@ void RiffContainer::endChunk() SLANG_ASSERT(isChunkOk(chunk)); } -RiffContainer::Data* RiffContainer::addData(size_t size) +void RiffContainer::setPayload(Data* data, const void* payload, size_t size) { - // We must be in a chunk + // We must be in a data chunk SLANG_ASSERT(m_dataChunk); + // The data shouldn't be set up + SLANG_ASSERT(data->m_ownership == Ownership::Uninitialized); // Add current chunks data m_dataChunk->m_payloadSize += size; - Data* data = (Data*)m_arena.allocate(sizeof(Data) + size); + data->m_ownership = Ownership::Arena; + data->m_size = size; + + data->m_payload = m_arena.allocate(size); + + if (payload) + { + ::memcpy(data->m_payload, payload, size); + } +} - data->m_next = nullptr; +void RiffContainer::moveOwned(Data* data, void* payload, size_t size) +{ + // We must be in a data chunk + SLANG_ASSERT(m_dataChunk); + // The data shouldn't be set up + SLANG_ASSERT(data->m_ownership == Ownership::Uninitialized); + + // Add current chunks data + m_dataChunk->m_payloadSize += size; + + data->m_ownership = Ownership::Owned; data->m_size = size; + // The area will manage this block + m_arena.addExternalBlock(payload, size); + data->m_payload = payload; +} + +void RiffContainer::setUnowned(Data* data, void* payload, size_t size) +{ + // We must be in a data chunk + SLANG_ASSERT(m_dataChunk); + // The data shouldn't be set up + SLANG_ASSERT(data->m_ownership == Ownership::Uninitialized); + // Add current chunks data + m_dataChunk->m_payloadSize += size; + + data->m_ownership = Ownership::NotOwned; + data->m_size = size; + data->m_payload = payload; +} + +RiffContainer::Data* RiffContainer::addData() +{ + // We must be in a chunk + SLANG_ASSERT(m_dataChunk); + + Data* data = (Data*)m_arena.allocate(sizeof(Data)); + data->init(); + Data*& next = m_dataChunk->m_endData ? m_dataChunk->m_endData->m_next : m_dataChunk->m_dataList; SLANG_ASSERT(next == nullptr); @@ -790,8 +838,8 @@ RiffContainer::Data* RiffContainer::addData(size_t size) void RiffContainer::write(const void* inData, size_t size) { - auto data = addData(size); - ::memcpy(data->getPayload(), inData, size); + auto data = addData(); + setPayload(data, inData, size); } static SlangResult _isChunkOk(RiffContainer::Chunk* chunk, void* data) diff --git a/source/core/slang-riff.h b/source/core/slang-riff.h index 9dfe655b6..4d23e45c1 100644 --- a/source/core/slang-riff.h +++ b/source/core/slang-riff.h @@ -98,6 +98,42 @@ struct RiffSemanticVersion RawType m_raw; }; +/* A helper class that makes reading data from a data block simpler */ +class RiffReadHelper +{ +public: + template <typename T> + SlangResult read(T& out) + { + if (m_cur + sizeof(T) > m_end) + { + return SLANG_FAIL; + } + // Make sure the alignment is plausible + SLANG_ASSERT((size_t(m_cur) & (SLANG_ALIGN_OF(T) - 1)) == 0); + ::memcpy(&out, m_cur, sizeof(T)); + m_cur += sizeof(T); + return SLANG_OK; + } + + /// Get the data + const uint8_t* getData() const { return m_cur; } + /// Get the remaining size + size_t getRemainingSize() const { return size_t(m_end - m_cur); } + + RiffReadHelper(const uint8_t* data, size_t size): + m_start(data), + m_end(data + size), + m_cur(data) + { + } + +protected: + const uint8_t* m_start; + const uint8_t* m_end; + const uint8_t* m_cur; +}; + /* A container for data in RIFF format. Holds the contents in memory. With the data held in memory allows for adding or removing chunks at will. @@ -112,24 +148,38 @@ class RiffContainer { public: + enum class Ownership + { + Uninitialized, ///< Doesn't contain anything + NotOwned, ///< It's not owned by the container + Arena, ///< It's owned and allocated on the arena + Owned, ///< It's owned, but wasn't allocated on the arena + }; + struct ListChunk; struct DataChunk; struct Data { - /// Get the payload - void* getPayload() { return (void*)(this + 1); } - size_t getSize() { return m_size; } + /// Get the payload + void* getPayload() { return m_payload; } + /// Get the size of the payload + size_t getSize() const { return m_size; } + /// Get the ownership of the data held in the payload + Ownership getOwnership() const { return m_ownership; } void init() { + m_ownership = Ownership::Uninitialized; m_size = 0; m_next = nullptr; + m_payload = nullptr; } - size_t m_size; - Data* m_next; - // Followed by the payload + Ownership m_ownership; ///< Stores the ownership of the payload + size_t m_size; ///< The size of the payload + void* m_payload; ///< The payload + Data* m_next; ///< The next Data block in the list }; struct Chunk; @@ -144,9 +194,10 @@ public: Data, }; - void init(Kind kind) + void init(Kind kind, FourCC fourCC) { m_kind = kind; + m_fourCC = fourCC; m_payloadSize = 0; m_next = nullptr; m_parent = nullptr; @@ -159,19 +210,14 @@ public: /// Returns a single data chunk Data* getSingleData() const; - /// Get the unique type - FourCC getUniqueType() const; - + /// Calculate the payload size size_t calcPayloadSize(); - // TODO(JS): - // We might want to consider moving subType/type to the Chunk, because in practice they typically - // mean the same thing, and as it has been arranged, for list chunk the overall chunk type is implied - // by the kind. - Kind m_kind; ///< Kind of chunk - size_t m_payloadSize; ///< The payload size (ie does NOT include RiffChunk header). - Chunk* m_next; ///< Next chunk in this list - ListChunk* m_parent; ///< The chunk this belongs to + Kind m_kind; ///< Kind of chunk + FourCC m_fourCC; ///< The chunk type for data, or the sub type for a List (riff/list) + size_t m_payloadSize; ///< The payload size (ie does NOT include RiffChunk header). + Chunk* m_next; ///< Next chunk in this list + ListChunk* m_parent; ///< The chunk this belongs to }; struct ListChunk : public Chunk @@ -181,8 +227,7 @@ public: void init(FourCC subType) { - Super::init(Kind::List); - m_subType = subType; + Super::init(Kind::List, subType); m_containedChunks = nullptr; m_endChunk = nullptr; @@ -206,7 +251,9 @@ public: /// NOTE! Assumes all contained chunks have correct payload sizes size_t calcPayloadSize(); - FourCC m_subType; ///< The subtype of this contained + /// Get the sub type + FourCC getSubType() const { return m_fourCC; } + Chunk* m_containedChunks; ///< The contained chunks Chunk* m_endChunk; ///< The last chunk (only set when pushed, and used when popped) }; @@ -224,15 +271,16 @@ public: /// Get single data payload. Data* getSingleData() const; - void init(FourCC type) + /// Return as read helper + RiffReadHelper asReadHelper() const; + + void init(FourCC fourCC) { - Super::init(Kind::Data); - m_type = type; + Super::init(Kind::Data, fourCC); m_dataList = nullptr; m_endData = nullptr; } - FourCC m_type; ///< Type of chunk Data* m_dataList; ///< List of 0 or more data items Data* m_endData; ///< The last data point }; @@ -266,7 +314,16 @@ public: /// Write data into a chunk (can only be inside a Kind::Data) void write(const void* data, size_t size); - Data* addData(size_t size); + + /// Adds an empty data block + Data* addData(); + /// Set the payload on a data. Payload can be passed as nullptr, if it is no memory will be copied. + void setPayload(Data* data, const void* payload, size_t size); + + /// Move ownership to + void moveOwned(Data* data, void* payload, size_t size); + /// Move unowned + void setUnowned(Data* data, void* payload, size_t size); /// End a chunk void endChunk(); @@ -299,15 +356,10 @@ protected: ListChunk* m_listChunk; DataChunk* m_dataChunk; - MemoryArena m_arena; + MemoryArena m_arena; ///< Can be used to use other owned blocks }; // ----------------------------------------------------------------------------- -SLANG_FORCE_INLINE FourCC RiffContainer::Chunk::getUniqueType() const -{ - return (m_kind == Kind::Data) ? static_cast<const DataChunk*>(this)->m_type : static_cast<const ListChunk*>(this)->m_subType; -} -// ----------------------------------------------------------------------------- template <typename T> T* as(RiffContainer::Chunk* chunk) { diff --git a/source/slang/slang-ir-serialize.cpp b/source/slang/slang-ir-serialize.cpp index ebbb792f4..97b070f9c 100644 --- a/source/slang/slang-ir-serialize.cpp +++ b/source/slang/slang-ir-serialize.cpp @@ -827,7 +827,9 @@ static Result _writeArrayChunk(IRSerialBinary::CompressionType compressionType, header.m_numCompressedEntries = uint32_t(numCompressedEntries); container->write(&header, sizeof(header)); - container->write(compressedPayload.begin(), compressedPayload.getCount()); + + const size_t compressedSize = compressedPayload.getCount(); + container->moveOwned(container->addData(), compressedPayload.detachBuffer(), compressedSize); break; } default: @@ -963,7 +965,10 @@ Result _writeInstArrayChunk(IRSerialBinary::CompressionType compressionType, uin header.m_numCompressedEntries = 0; container->write(&header, sizeof(header)); - container->write(compressedPayload.begin(), compressedPayload.getCount()); + + const size_t compressedPayloadSize = compressedPayload.getCount(); + container->moveOwned(container->addData(), compressedPayload.detachBuffer(), compressedPayloadSize); + return SLANG_OK; } default: break; @@ -1050,56 +1055,32 @@ static Result _readArrayChunk(IRSerialBinary::CompressionType compressionType, R { typedef IRSerialBinary Bin; - RiffContainer::Data* data = dataChunk->getSingleData(); - const uint8_t* cur = (const uint8_t*)data->getPayload(); - const uint8_t* end = cur + data->m_size; - + RiffReadHelper read = dataChunk->asReadHelper(); const size_t typeSize = listOut.getTypeSize(); switch (compressionType) { case Bin::CompressionType::VariableByteLite: { - if (cur + sizeof(Bin::CompressedArrayHeader) > end) - { - return SLANG_FAIL; - } + Bin::CompressedArrayHeader header; + SLANG_RETURN_ON_FAIL(read.read(header)); - // We have a compressed header - Bin::CompressedArrayHeader header = *(const Bin::CompressedArrayHeader*)cur; - cur += sizeof(Bin::CompressedArrayHeader); - void* dst = listOut.setSize(header.m_numEntries); - - // Need to read all the compressed data... - size_t payloadSize = size_t(end - cur); - - List<uint8_t> compressedPayload; - compressedPayload.setCount(payloadSize); - SLANG_ASSERT(header.m_numCompressedEntries == uint32_t((header.m_numEntries * typeSize) / sizeof(uint32_t))); // Decode.. - ByteEncodeUtil::decodeLiteUInt32(cur, header.m_numCompressedEntries, (uint32_t*)dst); + ByteEncodeUtil::decodeLiteUInt32(read.getData(), header.m_numCompressedEntries, (uint32_t*)dst); break; } case Bin::CompressionType::None: { // Read uncompressed - if (cur + sizeof(Bin::ArrayHeader) > end) - { - return SLANG_FAIL; - } - - Bin::ArrayHeader header = *(const Bin::ArrayHeader*)cur; - cur += sizeof(Bin::ArrayHeader); - + Bin::ArrayHeader header; + SLANG_RETURN_ON_FAIL(read.read(header)); const size_t payloadSize = header.m_numEntries * typeSize; - - SLANG_ASSERT(payloadSize == size_t(end - cur)); + SLANG_ASSERT(payloadSize == read.getRemainingSize()); void* dst = listOut.setSize(header.m_numEntries); - - ::memcpy(dst, cur, payloadSize); + ::memcpy(dst, read.getData(), payloadSize); break; } } @@ -1113,7 +1094,7 @@ static Result _readArrayChunk(const IRSerialBinary::ModuleHeader* header, RiffCo Bin::CompressionType compressionType = Bin::CompressionType::None; - if (dataChunk->m_type == SLANG_MAKE_COMPRESSED_FOUR_CC(dataChunk->m_type)) + if (dataChunk->m_fourCC == SLANG_MAKE_COMPRESSED_FOUR_CC(dataChunk->m_fourCC)) { // If it has compression, use the compression type set in the header compressionType = Bin::CompressionType(header->m_compressionType); @@ -1125,10 +1106,9 @@ static Result _readArrayChunk(const IRSerialBinary::ModuleHeader* header, RiffCo template <typename T> static Result _readArrayUncompressedChunk(const IRSerialBinary::ModuleHeader* header, RiffContainer::DataChunk* chunk, List<T>& arrayOut) { - typedef IRSerialBinary Bin; SLANG_UNUSED(header); ListResizerForType<T> resizer(arrayOut); - return _readArrayChunk(Bin::CompressionType::None, chunk, resizer); + return _readArrayChunk(IRSerialBinary::CompressionType::None, chunk, resizer); } static Result _decodeInsts(IRSerialBinary::CompressionType compressionType, const uint8_t* encodeCur, size_t encodeInSize, List<IRSerialData::Inst>& instsOut) @@ -1209,7 +1189,7 @@ static Result _readInstArrayChunk(const IRSerialBinary::ModuleHeader* moduleHead typedef IRSerialBinary Bin; Bin::CompressionType compressionType = Bin::CompressionType::None; - if (chunk->m_type == SLANG_MAKE_COMPRESSED_FOUR_CC(chunk->m_type)) + if (chunk->m_fourCC == SLANG_MAKE_COMPRESSED_FOUR_CC(chunk->m_fourCC)) { compressionType = Bin::CompressionType(moduleHeader->m_compressionType); } @@ -1223,22 +1203,14 @@ static Result _readInstArrayChunk(const IRSerialBinary::ModuleHeader* moduleHead } case Bin::CompressionType::VariableByteLite: { - RiffContainer::Data* data = chunk->getSingleData(); - const uint8_t* cur = (const uint8_t*)data->getPayload(); - const uint8_t* end = cur + data->m_size; - if (cur + sizeof(Bin::CompressedArrayHeader) <= end) - { - return SLANG_FAIL; - } - Bin::CompressedArrayHeader header = *(const Bin::CompressedArrayHeader*)cur; - cur += sizeof(Bin::CompressedArrayHeader); - - arrayOut.setCount(header.m_numEntries); + RiffReadHelper read = chunk->asReadHelper(); - // Need to read all the compressed data... - size_t payloadSize = size_t(end - cur); + Bin::CompressedArrayHeader header; + SLANG_RETURN_ON_FAIL(read.read(header)); + + arrayOut.setCount(header.m_numEntries); - SLANG_RETURN_ON_FAIL(_decodeInsts(compressionType, cur, payloadSize, arrayOut)); + SLANG_RETURN_ON_FAIL(_decodeInsts(compressionType, read.getData(), read.getRemainingSize(), arrayOut)); break; } default: @@ -1270,7 +1242,7 @@ static Result _readInstArrayChunk(const IRSerialBinary::ModuleHeader* moduleHead continue; } - switch (dataChunk->m_type) + switch (dataChunk->m_fourCC) { case SLANG_MAKE_COMPRESSED_FOUR_CC(Bin::kInstFourCc): case Bin::kInstFourCc: |
