1
0
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:
Philip Rebohle 2019-10-18 00:33:50 +02:00
parent 0ac89ccd9e
commit b756c229a9
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
3 changed files with 282 additions and 7 deletions

View File

@ -7,6 +7,79 @@ namespace dxvk {
static const Sha1Hash g_nullHash = Sha1Hash::compute(nullptr, 0);
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>
bool readCacheEntryTyped(std::istream& stream, T& entry) {
auto data = reinterpret_cast<char*>(&entry);
@ -304,6 +377,8 @@ namespace dxvk {
expectedSize = sizeof(DxvkStateCacheEntryV5);
else if (curHeader.version <= 6)
expectedSize = sizeof(DxvkStateCacheEntryV6);
else if (curHeader.version <= 7)
expectedSize = sizeof(DxvkStateCacheEntry);
if (curHeader.entrySize != expectedSize) {
Logger::warn("DXVK: State cache entry size changed");
@ -381,7 +456,7 @@ namespace dxvk {
}
bool DxvkStateCache::readCacheEntry(
bool DxvkStateCache::readCacheEntryV7(
uint32_t version,
std::istream& stream,
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(
std::ostream& stream,
DxvkStateCacheEntry& entry) const {
entry.hash = Sha1Hash::compute(entry);
DxvkStateCacheEntryData data;
VkShaderStageFlags stageMask = 0;
auto data = reinterpret_cast<const char*>(&entry);
auto size = sizeof(DxvkStateCacheEntry);
// Write shader hashes
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();
}

View File

@ -143,6 +143,11 @@ namespace dxvk {
std::istream& stream,
DxvkStateCacheHeader& header) const;
bool readCacheEntryV7(
uint32_t version,
std::istream& stream,
DxvkStateCacheEntry& entry) const;
bool readCacheEntry(
uint32_t version,
std::istream& stream,

View File

@ -52,8 +52,8 @@ namespace dxvk {
*/
struct DxvkStateCacheHeader {
char magic[4] = { 'D', 'X', 'V', 'K' };
uint32_t version = 7;
uint32_t entrySize = sizeof(DxvkStateCacheEntry);
uint32_t version = 8;
uint32_t entrySize = 0; /* no longer meaningful */
};
static_assert(sizeof(DxvkStateCacheHeader) == 12);