226 строки
6.6 KiB
C++
226 строки
6.6 KiB
C++
|
#include "dxc/Support/Global.h"
|
||
|
#include "dxc/DxilContainer/DxilRDATBuilder.h"
|
||
|
#include "dxc/DxilContainer/DxilPipelineStateValidation.h"
|
||
|
#include "dxc/Support/FileIOHelper.h"
|
||
|
|
||
|
using namespace llvm;
|
||
|
using namespace hlsl;
|
||
|
using namespace RDAT;
|
||
|
|
||
|
void RDATTable::SetRecordStride(size_t RecordStride) {
|
||
|
DXASSERT(m_rows.empty(), "record stride is fixed for the entire table");
|
||
|
m_RecordStride = RecordStride;
|
||
|
}
|
||
|
|
||
|
uint32_t RDATTable::InsertImpl(const void *ptr, size_t size) {
|
||
|
IFTBOOL(m_RecordStride <= size, DXC_E_GENERAL_INTERNAL_ERROR);
|
||
|
size_t count = m_rows.size();
|
||
|
if (count < (UINT32_MAX - 1)) {
|
||
|
const char *pData = (const char*)ptr;
|
||
|
auto result = m_map.insert(std::make_pair(std::string(pData, pData + m_RecordStride), count));
|
||
|
if (!m_bDeduplicationEnabled || result.second) {
|
||
|
m_rows.emplace_back(result.first->first);
|
||
|
return count;
|
||
|
} else {
|
||
|
return result.first->second;
|
||
|
}
|
||
|
}
|
||
|
return RDAT_NULL_REF;
|
||
|
}
|
||
|
|
||
|
void RDATTable::Write(void *ptr) {
|
||
|
char *pCur = (char*)ptr;
|
||
|
RuntimeDataTableHeader &header = *reinterpret_cast<RuntimeDataTableHeader*>(pCur);
|
||
|
header.RecordCount = m_rows.size();
|
||
|
header.RecordStride = m_RecordStride;
|
||
|
pCur += sizeof(RuntimeDataTableHeader);
|
||
|
for (auto &record : m_rows) {
|
||
|
DXASSERT_NOMSG(record.size() == m_RecordStride);
|
||
|
memcpy(pCur, record.data(), m_RecordStride);
|
||
|
pCur += m_RecordStride;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
uint32_t RDATTable::GetPartSize() const {
|
||
|
if (m_rows.empty())
|
||
|
return 0;
|
||
|
return sizeof(RuntimeDataTableHeader) + m_rows.size() * m_RecordStride;
|
||
|
}
|
||
|
|
||
|
uint32_t RawBytesPart::Insert(const void *pData, size_t dataSize) {
|
||
|
auto result = m_Map.insert(std::make_pair(std::string((const char *)pData, (const char *)pData + dataSize), m_Size));
|
||
|
auto iterator = result.first;
|
||
|
if (result.second) {
|
||
|
const std::string &key = iterator->first;
|
||
|
m_List.push_back(llvm::StringRef(key.data(), key.size()));
|
||
|
m_Size += key.size();
|
||
|
}
|
||
|
return iterator->second;
|
||
|
}
|
||
|
|
||
|
void RawBytesPart::Write(void *ptr) {
|
||
|
for (llvm::StringRef &entry : m_List) {
|
||
|
memcpy(ptr, entry.data(), entry.size());
|
||
|
ptr = (char *)ptr + entry.size();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DxilRDATBuilder::DxilRDATBuilder(bool allowRecordDuplication) :
|
||
|
m_bRecordDeduplicationEnabled(allowRecordDuplication)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
DxilRDATBuilder::SizeInfo DxilRDATBuilder::ComputeSize() const {
|
||
|
uint32_t totalSizeOfNonEmptyParts = 0;
|
||
|
uint32_t numNonEmptyParts = 0;
|
||
|
for (auto &part : m_Parts) {
|
||
|
if (part->GetPartSize() == 0)
|
||
|
continue;
|
||
|
numNonEmptyParts++;
|
||
|
totalSizeOfNonEmptyParts += sizeof(RuntimeDataPartHeader) + PSVALIGN4(part->GetPartSize());
|
||
|
}
|
||
|
|
||
|
uint32_t total =
|
||
|
sizeof(RuntimeDataHeader) + // Header
|
||
|
numNonEmptyParts * sizeof(uint32_t) + // Offset array
|
||
|
totalSizeOfNonEmptyParts; // Parts contents
|
||
|
|
||
|
SizeInfo ret = {};
|
||
|
ret.numParts = numNonEmptyParts;
|
||
|
ret.sizeInBytes = total;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
namespace {
|
||
|
// Size-checked writer
|
||
|
// on overrun: throw buffer_overrun{};
|
||
|
// on overlap: throw buffer_overlap{};
|
||
|
class CheckedWriter {
|
||
|
char *Ptr;
|
||
|
size_t Size;
|
||
|
size_t Offset;
|
||
|
|
||
|
public:
|
||
|
class exception : public std::exception {};
|
||
|
class buffer_overrun : public exception {
|
||
|
public:
|
||
|
buffer_overrun() noexcept {}
|
||
|
virtual const char * what() const noexcept override {
|
||
|
return ("buffer_overrun");
|
||
|
}
|
||
|
};
|
||
|
class buffer_overlap : public exception {
|
||
|
public:
|
||
|
buffer_overlap() noexcept {}
|
||
|
virtual const char * what() const noexcept override {
|
||
|
return ("buffer_overlap");
|
||
|
}
|
||
|
};
|
||
|
|
||
|
CheckedWriter(void *ptr, size_t size) :
|
||
|
Ptr(reinterpret_cast<char*>(ptr)), Size(size), Offset(0) {}
|
||
|
|
||
|
size_t GetOffset() const { return Offset; }
|
||
|
void Reset(size_t offset = 0) {
|
||
|
if (offset >= Size) throw buffer_overrun{};
|
||
|
Offset = offset;
|
||
|
}
|
||
|
// offset is absolute, ensure offset is >= current offset
|
||
|
void Advance(size_t offset = 0) {
|
||
|
if (offset < Offset) throw buffer_overlap{};
|
||
|
if (offset >= Size) throw buffer_overrun{};
|
||
|
Offset = offset;
|
||
|
}
|
||
|
void CheckBounds(size_t size) const {
|
||
|
assert(Offset <= Size && "otherwise, offset larger than size");
|
||
|
if (size > Size - Offset)
|
||
|
throw buffer_overrun{};
|
||
|
}
|
||
|
template <typename T>
|
||
|
T *Cast(size_t size = 0) {
|
||
|
if (0 == size) size = sizeof(T);
|
||
|
CheckBounds(size);
|
||
|
return reinterpret_cast<T*>(Ptr + Offset);
|
||
|
}
|
||
|
|
||
|
// Map and Write advance Offset:
|
||
|
template <typename T>
|
||
|
T &Map() {
|
||
|
const size_t size = sizeof(T);
|
||
|
T * p = Cast<T>(size);
|
||
|
Offset += size;
|
||
|
return *p;
|
||
|
}
|
||
|
template <typename T>
|
||
|
T *MapArray(size_t count = 1) {
|
||
|
const size_t size = sizeof(T) * count;
|
||
|
T *p = Cast<T>(size);
|
||
|
Offset += size;
|
||
|
return p;
|
||
|
}
|
||
|
template <typename T>
|
||
|
void Write(const T &obj) {
|
||
|
const size_t size = sizeof(T);
|
||
|
*Cast<T>(size) = obj;
|
||
|
Offset += size;
|
||
|
}
|
||
|
template <typename T>
|
||
|
void WriteArray(const T *pArray, size_t count = 1) {
|
||
|
const size_t size = sizeof(T) * count;
|
||
|
memcpy(Cast<T>(size), pArray, size);
|
||
|
Offset += size;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// returns the offset of the name inserted
|
||
|
uint32_t StringBufferPart::Insert(llvm::StringRef str) {
|
||
|
auto result = m_Map.insert(std::make_pair(std::string(str.data(), str.data() + str.size()), m_Size));
|
||
|
|
||
|
auto iterator = result.first;
|
||
|
if (result.second) {
|
||
|
const std::string &key = iterator->first;
|
||
|
m_List.push_back(llvm::StringRef(key.data(), key.size()));
|
||
|
m_Size += key.size() + 1 /*null terminator*/;
|
||
|
}
|
||
|
return iterator->second;
|
||
|
}
|
||
|
|
||
|
void StringBufferPart::Write(void *ptr) {
|
||
|
for (llvm::StringRef &entry : m_List) {
|
||
|
memcpy(ptr, entry.data(), entry.size()+1/*null terminator*/);
|
||
|
ptr = (char *)ptr + entry.size()+1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
StringRef DxilRDATBuilder::FinalizeAndGetData() {
|
||
|
try {
|
||
|
m_RDATBuffer.resize(size(), 0);
|
||
|
CheckedWriter W(m_RDATBuffer.data(), m_RDATBuffer.size());
|
||
|
// write RDAT header
|
||
|
RuntimeDataHeader &header = W.Map<RuntimeDataHeader>();
|
||
|
header.Version = RDAT_Version_10;
|
||
|
header.PartCount = ComputeSize().numParts;
|
||
|
// map offsets
|
||
|
uint32_t *offsets = W.MapArray<uint32_t>(header.PartCount);
|
||
|
// write parts
|
||
|
unsigned i = 0;
|
||
|
for (auto &part : m_Parts) {
|
||
|
if (part->GetPartSize() == 0)
|
||
|
continue;
|
||
|
offsets[i++] = W.GetOffset();
|
||
|
RuntimeDataPartHeader &partHeader = W.Map<RuntimeDataPartHeader>();
|
||
|
partHeader.Type = part->GetType();
|
||
|
partHeader.Size = PSVALIGN4(part->GetPartSize());
|
||
|
DXASSERT(partHeader.Size, "otherwise, failed to remove empty part");
|
||
|
char *bytes = W.MapArray<char>(partHeader.Size);
|
||
|
part->Write(bytes);
|
||
|
}
|
||
|
}
|
||
|
catch (CheckedWriter::exception e) {
|
||
|
throw hlsl::Exception(DXC_E_GENERAL_INTERNAL_ERROR, e.what());
|
||
|
}
|
||
|
return llvm::StringRef(m_RDATBuffer.data(), m_RDATBuffer.size());
|
||
|
}
|
||
|
|