mirror of
https://github.com/doitsujin/dxvk.git
synced 2024-11-30 04:24:11 +01:00
[dxvk] Implement more compact state cache data format (v8)
Omits a lot of unnecessary data and considerably reduces the state cache file size, often by over 80%.
This commit is contained in:
parent
0ac89ccd9e
commit
b756c229a9
@ -7,6 +7,79 @@ namespace dxvk {
|
|||||||
static const Sha1Hash g_nullHash = Sha1Hash::compute(nullptr, 0);
|
static const Sha1Hash g_nullHash = Sha1Hash::compute(nullptr, 0);
|
||||||
static const DxvkShaderKey g_nullShaderKey = DxvkShaderKey();
|
static const DxvkShaderKey g_nullShaderKey = DxvkShaderKey();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Packed entry header
|
||||||
|
*/
|
||||||
|
struct DxvkStateCacheEntryHeader {
|
||||||
|
uint32_t stageMask : 8;
|
||||||
|
uint32_t entrySize : 24;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief State cache entry data
|
||||||
|
*
|
||||||
|
* Stores data for a single cache entry and
|
||||||
|
* provides convenience methods to access it.
|
||||||
|
*/
|
||||||
|
class DxvkStateCacheEntryData {
|
||||||
|
constexpr static size_t MaxSize = 1024;
|
||||||
|
public:
|
||||||
|
|
||||||
|
size_t size() const {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* data() const {
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sha1Hash computeHash() const {
|
||||||
|
return Sha1Hash::compute(m_data, m_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool read(T& data) {
|
||||||
|
if (m_read + sizeof(T) > m_size)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::memcpy(&data, &m_data[m_read], sizeof(T));
|
||||||
|
m_read += sizeof(T);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool write(const T& data) {
|
||||||
|
if (m_size + sizeof(T) > MaxSize)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::memcpy(&m_data[m_size], &data, sizeof(T));
|
||||||
|
m_size += sizeof(T);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readFromStream(std::istream& stream, size_t size) {
|
||||||
|
if (size > MaxSize)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stream.read(m_data, size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_size = size;
|
||||||
|
m_read = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
size_t m_size = 0;
|
||||||
|
size_t m_read = 0;
|
||||||
|
char m_data[MaxSize];
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool readCacheEntryTyped(std::istream& stream, T& entry) {
|
bool readCacheEntryTyped(std::istream& stream, T& entry) {
|
||||||
auto data = reinterpret_cast<char*>(&entry);
|
auto data = reinterpret_cast<char*>(&entry);
|
||||||
@ -304,6 +377,8 @@ namespace dxvk {
|
|||||||
expectedSize = sizeof(DxvkStateCacheEntryV5);
|
expectedSize = sizeof(DxvkStateCacheEntryV5);
|
||||||
else if (curHeader.version <= 6)
|
else if (curHeader.version <= 6)
|
||||||
expectedSize = sizeof(DxvkStateCacheEntryV6);
|
expectedSize = sizeof(DxvkStateCacheEntryV6);
|
||||||
|
else if (curHeader.version <= 7)
|
||||||
|
expectedSize = sizeof(DxvkStateCacheEntry);
|
||||||
|
|
||||||
if (curHeader.entrySize != expectedSize) {
|
if (curHeader.entrySize != expectedSize) {
|
||||||
Logger::warn("DXVK: State cache entry size changed");
|
Logger::warn("DXVK: State cache entry size changed");
|
||||||
@ -381,7 +456,7 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool DxvkStateCache::readCacheEntry(
|
bool DxvkStateCache::readCacheEntryV7(
|
||||||
uint32_t version,
|
uint32_t version,
|
||||||
std::istream& stream,
|
std::istream& stream,
|
||||||
DxvkStateCacheEntry& entry) const {
|
DxvkStateCacheEntry& entry) const {
|
||||||
@ -419,15 +494,210 @@ namespace dxvk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DxvkStateCache::readCacheEntry(
|
||||||
|
uint32_t version,
|
||||||
|
std::istream& stream,
|
||||||
|
DxvkStateCacheEntry& entry) const {
|
||||||
|
if (version < 8)
|
||||||
|
return readCacheEntryV7(version, stream, entry);
|
||||||
|
|
||||||
|
// Read entry metadata and actual data
|
||||||
|
DxvkStateCacheEntryHeader header;
|
||||||
|
DxvkStateCacheEntryData data;
|
||||||
|
Sha1Hash hash;
|
||||||
|
|
||||||
|
if (!stream.read(reinterpret_cast<char*>(&header), sizeof(header))
|
||||||
|
|| !stream.read(reinterpret_cast<char*>(&hash), sizeof(hash))
|
||||||
|
|| !data.readFromStream(stream, header.entrySize))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Validate hash, skip entry if invalid
|
||||||
|
if (hash != data.computeHash())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Read shader hashes
|
||||||
|
VkShaderStageFlags stageMask = VkShaderStageFlags(header.stageMask);
|
||||||
|
auto keys = &entry.shaders.vs;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < 6; i++) {
|
||||||
|
if (stageMask & VkShaderStageFlagBits(1 << i))
|
||||||
|
data.read(keys[i]);
|
||||||
|
else
|
||||||
|
keys[i] = g_nullShaderKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stageMask & VK_SHADER_STAGE_COMPUTE_BIT) {
|
||||||
|
if (!data.read(entry.cpState.bsBindingMask))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// Read packed render pass format
|
||||||
|
uint8_t sampleCount = 0;
|
||||||
|
uint8_t imageFormat = 0;
|
||||||
|
uint8_t imageLayout = 0;
|
||||||
|
|
||||||
|
if (!data.read(sampleCount)
|
||||||
|
|| !data.read(imageFormat)
|
||||||
|
|| !data.read(imageLayout))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
entry.format.sampleCount = VkSampleCountFlagBits(sampleCount);
|
||||||
|
entry.format.depth.format = VkFormat(imageFormat);
|
||||||
|
entry.format.depth.layout = VkImageLayout(imageLayout);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
|
||||||
|
if (!data.read(imageFormat)
|
||||||
|
|| !data.read(imageLayout))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
entry.format.color[i].format = VkFormat(imageFormat);
|
||||||
|
entry.format.color[i].layout = VkImageLayout(imageLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read common pipeline state
|
||||||
|
if (!data.read(entry.gpState.bsBindingMask)
|
||||||
|
|| !data.read(entry.gpState.ia)
|
||||||
|
|| !data.read(entry.gpState.il)
|
||||||
|
|| !data.read(entry.gpState.rs)
|
||||||
|
|| !data.read(entry.gpState.ms)
|
||||||
|
|| !data.read(entry.gpState.ds)
|
||||||
|
|| !data.read(entry.gpState.om)
|
||||||
|
|| !data.read(entry.gpState.dsFront)
|
||||||
|
|| !data.read(entry.gpState.dsBack))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (entry.gpState.il.attributeCount() > MaxNumVertexAttributes
|
||||||
|
|| entry.gpState.il.bindingCount() > MaxNumVertexBindings)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Read render target swizzles
|
||||||
|
for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
|
||||||
|
if (!data.read(entry.gpState.omSwizzle[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read render target blend info
|
||||||
|
for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
|
||||||
|
if (!data.read(entry.gpState.omBlend[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read defined vertex attributes
|
||||||
|
for (uint32_t i = 0; i < entry.gpState.il.attributeCount(); i++) {
|
||||||
|
if (!data.read(entry.gpState.ilAttributes[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read defined vertex bindings
|
||||||
|
for (uint32_t i = 0; i < entry.gpState.il.bindingCount(); i++) {
|
||||||
|
if (!data.read(entry.gpState.ilBindings[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read non-zero spec constants
|
||||||
|
auto& sc = (stageMask & VK_SHADER_STAGE_COMPUTE_BIT)
|
||||||
|
? entry.cpState.sc
|
||||||
|
: entry.gpState.sc;
|
||||||
|
|
||||||
|
uint32_t specConstantMask = 0;
|
||||||
|
|
||||||
|
if (!data.read(specConstantMask))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < MaxNumSpecConstants; i++) {
|
||||||
|
if (specConstantMask & (1 << i)) {
|
||||||
|
if (!data.read(sc.specConstants[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DxvkStateCache::writeCacheEntry(
|
void DxvkStateCache::writeCacheEntry(
|
||||||
std::ostream& stream,
|
std::ostream& stream,
|
||||||
DxvkStateCacheEntry& entry) const {
|
DxvkStateCacheEntry& entry) const {
|
||||||
entry.hash = Sha1Hash::compute(entry);
|
DxvkStateCacheEntryData data;
|
||||||
|
VkShaderStageFlags stageMask = 0;
|
||||||
|
|
||||||
auto data = reinterpret_cast<const char*>(&entry);
|
// Write shader hashes
|
||||||
auto size = sizeof(DxvkStateCacheEntry);
|
auto keys = &entry.shaders.vs;
|
||||||
|
|
||||||
stream.write(data, size);
|
for (uint32_t i = 0; i < 6; i++) {
|
||||||
|
if (!keys[i].eq(g_nullShaderKey)) {
|
||||||
|
stageMask |= VkShaderStageFlagBits(1 << i);
|
||||||
|
data.write(keys[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stageMask & VK_SHADER_STAGE_COMPUTE_BIT) {
|
||||||
|
// Nothing else here to write out
|
||||||
|
data.write(entry.cpState.bsBindingMask);
|
||||||
|
} else {
|
||||||
|
// Pack render pass format
|
||||||
|
data.write(uint8_t(entry.format.sampleCount));
|
||||||
|
data.write(uint8_t(entry.format.depth.format));
|
||||||
|
data.write(uint8_t(entry.format.depth.layout));
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
|
||||||
|
data.write(uint8_t(entry.format.color[i].format));
|
||||||
|
data.write(uint8_t(entry.format.color[i].layout));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out common pipeline state
|
||||||
|
data.write(entry.gpState.bsBindingMask);
|
||||||
|
data.write(entry.gpState.ia);
|
||||||
|
data.write(entry.gpState.il);
|
||||||
|
data.write(entry.gpState.rs);
|
||||||
|
data.write(entry.gpState.ms);
|
||||||
|
data.write(entry.gpState.ds);
|
||||||
|
data.write(entry.gpState.om);
|
||||||
|
data.write(entry.gpState.dsFront);
|
||||||
|
data.write(entry.gpState.dsBack);
|
||||||
|
|
||||||
|
// Write out render target swizzles and blend info
|
||||||
|
for (uint32_t i = 0; i < MaxNumRenderTargets; i++)
|
||||||
|
data.write(entry.gpState.omSwizzle[i]);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < MaxNumRenderTargets; i++)
|
||||||
|
data.write(entry.gpState.omBlend[i]);
|
||||||
|
|
||||||
|
// Write out input layout for defined attributes
|
||||||
|
for (uint32_t i = 0; i < entry.gpState.il.attributeCount(); i++)
|
||||||
|
data.write(entry.gpState.ilAttributes[i]);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < entry.gpState.il.bindingCount(); i++)
|
||||||
|
data.write(entry.gpState.ilBindings[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out all non-zero spec constants
|
||||||
|
auto& sc = (stageMask & VK_SHADER_STAGE_COMPUTE_BIT)
|
||||||
|
? entry.cpState.sc
|
||||||
|
: entry.gpState.sc;
|
||||||
|
|
||||||
|
uint32_t specConstantMask = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < MaxNumSpecConstants; i++)
|
||||||
|
specConstantMask |= sc.specConstants[i] ? (1 << i) : 0;
|
||||||
|
|
||||||
|
data.write(specConstantMask);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < MaxNumSpecConstants; i++) {
|
||||||
|
if (specConstantMask & (1 << i))
|
||||||
|
data.write(sc.specConstants[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// General layout: header -> hash -> data
|
||||||
|
DxvkStateCacheEntryHeader header;
|
||||||
|
header.stageMask = uint8_t(stageMask);
|
||||||
|
header.entrySize = data.size();
|
||||||
|
|
||||||
|
Sha1Hash hash = data.computeHash();
|
||||||
|
|
||||||
|
stream.write(reinterpret_cast<char*>(&header), sizeof(header));
|
||||||
|
stream.write(reinterpret_cast<char*>(&hash), sizeof(hash));
|
||||||
|
stream.write(data.data(), data.size());
|
||||||
stream.flush();
|
stream.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,6 +143,11 @@ namespace dxvk {
|
|||||||
std::istream& stream,
|
std::istream& stream,
|
||||||
DxvkStateCacheHeader& header) const;
|
DxvkStateCacheHeader& header) const;
|
||||||
|
|
||||||
|
bool readCacheEntryV7(
|
||||||
|
uint32_t version,
|
||||||
|
std::istream& stream,
|
||||||
|
DxvkStateCacheEntry& entry) const;
|
||||||
|
|
||||||
bool readCacheEntry(
|
bool readCacheEntry(
|
||||||
uint32_t version,
|
uint32_t version,
|
||||||
std::istream& stream,
|
std::istream& stream,
|
||||||
|
@ -52,8 +52,8 @@ namespace dxvk {
|
|||||||
*/
|
*/
|
||||||
struct DxvkStateCacheHeader {
|
struct DxvkStateCacheHeader {
|
||||||
char magic[4] = { 'D', 'X', 'V', 'K' };
|
char magic[4] = { 'D', 'X', 'V', 'K' };
|
||||||
uint32_t version = 7;
|
uint32_t version = 8;
|
||||||
uint32_t entrySize = sizeof(DxvkStateCacheEntry);
|
uint32_t entrySize = 0; /* no longer meaningful */
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(DxvkStateCacheHeader) == 12);
|
static_assert(sizeof(DxvkStateCacheHeader) == 12);
|
||||||
|
Loading…
Reference in New Issue
Block a user