mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 18:31:50 +01:00
Add pipeline (de)serialisation support to bundle
See comments in code for details on the on-disk format.
This commit is contained in:
parent
937eff392f
commit
755f7c75af
@ -30,7 +30,7 @@ namespace skyline::gpu::interconnect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PipelineStateBundle::AddTextureType(u32 index, Shader::TextureType type) {
|
void PipelineStateBundle::AddTextureType(u32 index, Shader::TextureType type) {
|
||||||
textureTypes.emplace_back(index, type);
|
textureTypes.push_back({index, type});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipelineStateBundle::AddConstantBufferValue(u32 shaderStage, u32 index, u32 offset, u32 value) {
|
void PipelineStateBundle::AddConstantBufferValue(u32 shaderStage, u32 index, u32 offset, u32 value) {
|
||||||
@ -46,19 +46,156 @@ namespace skyline::gpu::interconnect {
|
|||||||
return {stageInfo.binary, stageInfo.binaryBaseOffset};
|
return {stageInfo.binary, stageInfo.binaryBaseOffset};
|
||||||
}
|
}
|
||||||
|
|
||||||
Shader::TextureType PipelineStateBundle::LookupTextureType(u32 offset) {
|
Shader::TextureType PipelineStateBundle::LookupTextureType(u32 index) {
|
||||||
auto it{ranges::find_if(textureTypes, [offset](auto &pair) { return pair.first == offset; })};
|
auto it{ranges::find_if(textureTypes, [index](const auto &entry) { return entry.index == index; })};
|
||||||
if (it == textureTypes.end())
|
if (it == textureTypes.end())
|
||||||
throw exception("Failed to find texture type for offset: 0x{:X}", offset);
|
throw exception("Failed to find texture type for index: 0x{:X}", index);
|
||||||
|
|
||||||
return it->second;
|
return it->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 PipelineStateBundle::LookupConstantBufferValue(u32 shaderStage, u32 index, u32 offset) {
|
u32 PipelineStateBundle::LookupConstantBufferValue(u32 shaderStage, u32 index, u32 offset) {
|
||||||
auto it{ranges::find_if(constantBufferValues, [index, offset, shaderStage](auto &val) { return val.index == index && val.offset == offset && val.shaderStage == shaderStage; })};
|
auto it{ranges::find_if(constantBufferValues, [index, offset, shaderStage](const auto &val) { return val.index == index && val.offset == offset && val.shaderStage == shaderStage; })};
|
||||||
if (it == constantBufferValues.end())
|
if (it == constantBufferValues.end())
|
||||||
throw exception("Failed to find constant buffer value for offset: 0x{:X}", offset);
|
throw exception("Failed to find constant buffer value for offset: 0x{:X}", offset);
|
||||||
|
|
||||||
return it->value;
|
return it->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr u32 MaxSerialisedBundleSize{1 << 20}; ///< The maximum size of a serialised bundle (1 MiB)
|
||||||
|
|
||||||
|
/* Bundle header format pseudocode:
|
||||||
|
u64 hash
|
||||||
|
u32 bundleSize
|
||||||
|
u32 keySize;
|
||||||
|
u32 constantBufferValueCount
|
||||||
|
u32 textureTypeCount
|
||||||
|
u32 pipelineStageCount
|
||||||
|
u8 key[keySize];
|
||||||
|
|
||||||
|
struct ConstantBufferValue {
|
||||||
|
u32 shaderStage;
|
||||||
|
u32 index;
|
||||||
|
u32 offset;
|
||||||
|
u32 value;
|
||||||
|
} constantBufferValues[constantBufferValueCount];
|
||||||
|
|
||||||
|
struct TextureType {
|
||||||
|
u32 index;
|
||||||
|
u32 (Shader::TextureType) type;
|
||||||
|
} textureType[textureTypeCount];
|
||||||
|
|
||||||
|
struct PipelineStage {
|
||||||
|
u32 binaryBaseOffset
|
||||||
|
u32 binarySize
|
||||||
|
u8 binary[binarySize]
|
||||||
|
} pipelineStages[pipelineStageCount];
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct BundleDataHeader {
|
||||||
|
u32 keySize;
|
||||||
|
u32 constantBufferValueCount;
|
||||||
|
u32 textureTypeCount;
|
||||||
|
u32 pipelineStageCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PipelineBinaryDataHeader {
|
||||||
|
u32 binaryBaseOffset;
|
||||||
|
u32 binarySize;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool PipelineStateBundle::Deserialise(std::ifstream &stream) {
|
||||||
|
if (stream.peek() == EOF)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
u64 hash{};
|
||||||
|
stream.read(reinterpret_cast<char *>(&hash), sizeof(hash));
|
||||||
|
|
||||||
|
u32 bundleSize{};
|
||||||
|
stream.read(reinterpret_cast<char *>(&bundleSize), sizeof(bundleSize));
|
||||||
|
if (bundleSize > MaxSerialisedBundleSize)
|
||||||
|
throw exception("Pipeline state bundle is too large: 0x{:X}", bundleSize);
|
||||||
|
|
||||||
|
fileBuffer.resize(static_cast<size_t>(bundleSize));
|
||||||
|
stream.read(reinterpret_cast<char *>(fileBuffer.data()), static_cast<std::streamsize>(bundleSize));
|
||||||
|
|
||||||
|
if (XXH64(fileBuffer.data(), bundleSize, 0) != hash)
|
||||||
|
throw exception("Pipeline state bundle hash mismatch");
|
||||||
|
|
||||||
|
auto data{span(fileBuffer)};
|
||||||
|
const auto &header{data.as<BundleDataHeader>()};
|
||||||
|
size_t offset{sizeof(BundleDataHeader)};
|
||||||
|
|
||||||
|
Reset(data.subspan(offset, header.keySize));
|
||||||
|
offset += header.keySize;
|
||||||
|
|
||||||
|
auto readConstantBufferValues{data.subspan(offset, header.constantBufferValueCount * sizeof(ConstantBufferValue)).cast<ConstantBufferValue>()};
|
||||||
|
textureTypes.reserve(header.textureTypeCount);
|
||||||
|
constantBufferValues.insert(constantBufferValues.end(), readConstantBufferValues.begin(), readConstantBufferValues.end());
|
||||||
|
offset += header.constantBufferValueCount * sizeof(ConstantBufferValue);
|
||||||
|
|
||||||
|
auto readTextureTypes{data.subspan(offset, header.textureTypeCount * sizeof(TextureTypeEntry)).cast<TextureTypeEntry>()};
|
||||||
|
textureTypes.reserve(header.textureTypeCount);
|
||||||
|
textureTypes.insert(textureTypes.end(), readTextureTypes.begin(), readTextureTypes.end());
|
||||||
|
offset += header.textureTypeCount * sizeof(TextureTypeEntry);
|
||||||
|
|
||||||
|
pipelineStages.resize(header.pipelineStageCount);
|
||||||
|
for (u32 i{}; i < header.pipelineStageCount; i++) {
|
||||||
|
const auto &pipelineHeader{data.subspan(offset).as<PipelineBinaryDataHeader>()};
|
||||||
|
offset += sizeof(PipelineBinaryDataHeader);
|
||||||
|
|
||||||
|
pipelineStages[i].binaryBaseOffset = pipelineHeader.binaryBaseOffset;
|
||||||
|
pipelineStages[i].binary.resize(pipelineHeader.binarySize);
|
||||||
|
span(pipelineStages[i].binary).copy_from(data.subspan(offset, pipelineHeader.binarySize));
|
||||||
|
offset += pipelineHeader.binarySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipelineStateBundle::Serialise(std::ofstream &stream) {
|
||||||
|
u32 bundleSize{static_cast<u32>(sizeof(BundleDataHeader) +
|
||||||
|
key.size() +
|
||||||
|
constantBufferValues.size() * sizeof(ConstantBufferValue) +
|
||||||
|
textureTypes.size() * sizeof(TextureTypeEntry) +
|
||||||
|
std::accumulate(pipelineStages.begin(), pipelineStages.end(), 0UL, [](size_t acc, const auto &stage) {
|
||||||
|
return acc + sizeof(PipelineBinaryDataHeader) + stage.binary.size();
|
||||||
|
}))};
|
||||||
|
|
||||||
|
fileBuffer.resize(bundleSize);
|
||||||
|
|
||||||
|
auto data{span(fileBuffer)};
|
||||||
|
auto &header{data.as<BundleDataHeader>()};
|
||||||
|
size_t offset{sizeof(BundleDataHeader)};
|
||||||
|
|
||||||
|
header.keySize = static_cast<u32>(key.size());
|
||||||
|
header.constantBufferValueCount = static_cast<u32>(constantBufferValues.size());
|
||||||
|
header.textureTypeCount = static_cast<u32>(textureTypes.size());
|
||||||
|
header.pipelineStageCount = static_cast<u32>(pipelineStages.size());
|
||||||
|
|
||||||
|
data.subspan(offset, header.keySize).copy_from(key);
|
||||||
|
offset += header.keySize;
|
||||||
|
|
||||||
|
data.subspan(offset, header.constantBufferValueCount * sizeof(ConstantBufferValue)).copy_from(constantBufferValues);
|
||||||
|
offset += header.constantBufferValueCount * sizeof(ConstantBufferValue);
|
||||||
|
|
||||||
|
data.subspan(offset, header.textureTypeCount * sizeof(TextureTypeEntry)).copy_from(textureTypes);
|
||||||
|
offset += header.textureTypeCount * sizeof(TextureTypeEntry);
|
||||||
|
|
||||||
|
for (const auto &stage : pipelineStages) {
|
||||||
|
auto &pipelineHeader{data.subspan(offset).as<PipelineBinaryDataHeader>()};
|
||||||
|
offset += sizeof(PipelineBinaryDataHeader);
|
||||||
|
|
||||||
|
pipelineHeader.binaryBaseOffset = stage.binaryBaseOffset;
|
||||||
|
pipelineHeader.binarySize = static_cast<u32>(stage.binary.size());
|
||||||
|
|
||||||
|
data.subspan(offset, pipelineHeader.binarySize).copy_from(stage.binary);
|
||||||
|
offset += pipelineHeader.binarySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 hash{XXH64(fileBuffer.data(), bundleSize, 0)};
|
||||||
|
stream.write(reinterpret_cast<const char *>(&hash), sizeof(hash));
|
||||||
|
stream.write(reinterpret_cast<const char *>(&bundleSize), sizeof(bundleSize));
|
||||||
|
stream.write(reinterpret_cast<const char *>(fileBuffer.data()), static_cast<std::streamsize>(bundleSize));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ namespace skyline::gpu::interconnect {
|
|||||||
class PipelineStateBundle {
|
class PipelineStateBundle {
|
||||||
private:
|
private:
|
||||||
std::vector<u8> key; //!< Byte array containing the pipeline key, this is interpreted by the the user and two different keys might refer to the same pipeline
|
std::vector<u8> key; //!< Byte array containing the pipeline key, this is interpreted by the the user and two different keys might refer to the same pipeline
|
||||||
|
std::vector<u8> fileBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Holds the raw binary and associated info for a pipeline stage
|
* @brief Holds the raw binary and associated info for a pipeline stage
|
||||||
@ -26,6 +27,7 @@ namespace skyline::gpu::interconnect {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Holds a value of a constant buffer read from memory at pipeline creation time
|
* @brief Holds a value of a constant buffer read from memory at pipeline creation time
|
||||||
|
* @note This struct *MUST* not be modified without a pipeline cache version bump
|
||||||
*/
|
*/
|
||||||
struct ConstantBufferValue {
|
struct ConstantBufferValue {
|
||||||
u32 shaderStage;
|
u32 shaderStage;
|
||||||
@ -33,9 +35,20 @@ namespace skyline::gpu::interconnect {
|
|||||||
u32 offset;
|
u32 offset;
|
||||||
u32 value;
|
u32 value;
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(ConstantBufferValue) == 0x10);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Holds a the texture type of a TIC entry read at pipeline creation time
|
||||||
|
* @note This struct *MUST* not be modified without a pipeline cache version bump
|
||||||
|
*/
|
||||||
|
struct TextureTypeEntry {
|
||||||
|
u32 index;
|
||||||
|
Shader::TextureType type;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(TextureTypeEntry) == 0x8);
|
||||||
|
|
||||||
boost::container::small_vector<ConstantBufferValue, 4> constantBufferValues;
|
boost::container::small_vector<ConstantBufferValue, 4> constantBufferValues;
|
||||||
boost::container::small_vector<std::pair<u32, Shader::TextureType>, 4> textureTypes;
|
boost::container::small_vector<TextureTypeEntry, 4> textureTypes;
|
||||||
|
|
||||||
std::vector<PipelineStage> pipelineStages{};
|
std::vector<PipelineStage> pipelineStages{};
|
||||||
|
|
||||||
@ -47,7 +60,7 @@ namespace skyline::gpu::interconnect {
|
|||||||
*/
|
*/
|
||||||
void Reset(span<const u8> newKey);
|
void Reset(span<const u8> newKey);
|
||||||
|
|
||||||
template<typename T> requires std::is_trivially_copyable_v<T>
|
template<typename T> requires std::is_trivially_copyable_v<T> && (!requires (T t){ t.size(); })
|
||||||
void Reset(const T &value) {
|
void Reset(const T &value) {
|
||||||
Reset(span<const u8>(reinterpret_cast<const u8 *>(&value), sizeof(T)));
|
Reset(span<const u8>(reinterpret_cast<const u8 *>(&value), sizeof(T)));
|
||||||
}
|
}
|
||||||
@ -74,7 +87,7 @@ namespace skyline::gpu::interconnect {
|
|||||||
|
|
||||||
template<typename T> requires std::is_trivially_copyable_v<T>
|
template<typename T> requires std::is_trivially_copyable_v<T>
|
||||||
const T &GetKey() {
|
const T &GetKey() {
|
||||||
return *reinterpret_cast<const T *>(key.data());
|
return GetKey().as<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,11 +98,15 @@ namespace skyline::gpu::interconnect {
|
|||||||
/**
|
/**
|
||||||
* @brief Returns the texture type for a given offset
|
* @brief Returns the texture type for a given offset
|
||||||
*/
|
*/
|
||||||
Shader::TextureType LookupTextureType(u32 offset);
|
Shader::TextureType LookupTextureType(u32 index);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the constant buffer value for a given offset and shader stage
|
* @brief Returns the constant buffer value for a given offset and shader stage
|
||||||
*/
|
*/
|
||||||
u32 LookupConstantBufferValue(u32 shaderStage, u32 index, u32 offset);
|
u32 LookupConstantBufferValue(u32 shaderStage, u32 index, u32 offset);
|
||||||
|
|
||||||
|
bool Deserialise(std::ifstream &stream);
|
||||||
|
|
||||||
|
void Serialise(std::ofstream &stream);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user