mirror of
https://github.com/doitsujin/dxvk.git
synced 2024-11-29 19:24:10 +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 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();
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user