diff --git a/src/dxvk/dxvk_state_cache.cpp b/src/dxvk/dxvk_state_cache.cpp index f712c6ff1..a9249a3a2 100644 --- a/src/dxvk/dxvk_state_cache.cpp +++ b/src/dxvk/dxvk_state_cache.cpp @@ -7,6 +7,20 @@ namespace dxvk { static const Sha1Hash g_nullHash = Sha1Hash::compute(nullptr, 0); static const DxvkShaderKey g_nullShaderKey = DxvkShaderKey(); + template + bool readCacheEntryTyped(std::istream& stream, T& entry) { + auto data = reinterpret_cast(&entry); + auto size = sizeof(entry); + + if (!stream.read(data, size)) + return false; + + Sha1Hash expectedHash = std::exchange(entry.hash, g_nullHash); + Sha1Hash computedHash = Sha1Hash::compute(entry); + return expectedHash == computedHash; + } + + bool DxvkStateCacheKey::eq(const DxvkStateCacheKey& key) const { return this->vs.eq(key.vs) && this->tcs.eq(key.tcs) @@ -282,8 +296,13 @@ namespace dxvk { return false; } - // Struct size hasn't changed between v2/v3 - if (curHeader.entrySize != newHeader.entrySize) { + // Struct size hasn't changed between v2 and v4 + size_t expectedSize = newHeader.entrySize; + + if (curHeader.version <= 4) + expectedSize = sizeof(DxvkStateCacheEntryV4); + + if (curHeader.entrySize != expectedSize) { Logger::warn("DXVK: State cache entry size changed"); return false; } @@ -306,11 +325,7 @@ namespace dxvk { while (ifile) { DxvkStateCacheEntry entry; - if (readCacheEntry(ifile, entry)) { - switch (curHeader.version) { - case 2: convertEntryV2(entry); /* fall through */ - } - + if (readCacheEntry(curHeader.version, ifile, entry)) { size_t entryId = m_entries.size(); m_entries.push_back(entry); @@ -364,17 +379,22 @@ namespace dxvk { bool DxvkStateCache::readCacheEntry( + uint32_t version, std::istream& stream, DxvkStateCacheEntry& entry) const { - auto data = reinterpret_cast(&entry); - auto size = sizeof(DxvkStateCacheEntry); + if (version <= 4) { + DxvkStateCacheEntryV4 v4; - if (!stream.read(data, size)) - return false; - - Sha1Hash expectedHash = std::exchange(entry.hash, g_nullHash); - Sha1Hash computedHash = Sha1Hash::compute(entry); - return expectedHash == computedHash; + if (!readCacheEntryTyped(stream, v4)) + return false; + + if (version == 2) + convertEntryV2(v4); + + return convertEntryV4(v4, entry); + } else { + return readCacheEntryTyped(stream, entry); + } } @@ -392,7 +412,7 @@ namespace dxvk { bool DxvkStateCache::convertEntryV2( - DxvkStateCacheEntry& entry) const { + DxvkStateCacheEntryV4& entry) const { // Semantics changed: // v2: rsDepthClampEnable // v3: rsDepthClipEnable @@ -405,6 +425,62 @@ namespace dxvk { } + bool DxvkStateCache::convertEntryV4( + const DxvkStateCacheEntryV4& in, + DxvkStateCacheEntry& out) const { + out.shaders = in.shaders; + out.cpState = in.cpState; + out.format = in.format; + out.hash = in.hash; + + out.gpState.bsBindingMask = in.gpState.bsBindingMask; + + out.gpState.iaPrimitiveTopology = in.gpState.iaPrimitiveTopology; + out.gpState.iaPrimitiveRestart = in.gpState.iaPrimitiveRestart; + out.gpState.iaPatchVertexCount = in.gpState.iaPatchVertexCount; + + out.gpState.ilAttributeCount = in.gpState.ilAttributeCount; + out.gpState.ilBindingCount = in.gpState.ilBindingCount; + + for (uint32_t i = 0; i < in.gpState.ilAttributeCount; i++) + out.gpState.ilAttributes[i] = in.gpState.ilAttributes[i]; + + for (uint32_t i = 0; i < in.gpState.ilBindingCount; i++) { + out.gpState.ilBindings[i] = in.gpState.ilBindings[i]; + out.gpState.ilDivisors[i] = in.gpState.ilDivisors[i]; + } + + out.gpState.rsDepthClipEnable = in.gpState.rsDepthClipEnable; + out.gpState.rsDepthBiasEnable = in.gpState.rsDepthBiasEnable; + out.gpState.rsPolygonMode = in.gpState.rsPolygonMode; + out.gpState.rsCullMode = in.gpState.rsCullMode; + out.gpState.rsFrontFace = in.gpState.rsFrontFace; + out.gpState.rsViewportCount = in.gpState.rsViewportCount; + out.gpState.rsSampleCount = in.gpState.rsSampleCount; + + out.gpState.msSampleCount = in.gpState.msSampleCount; + out.gpState.msSampleMask = in.gpState.msSampleMask; + out.gpState.msEnableAlphaToCoverage = in.gpState.msEnableAlphaToCoverage; + + out.gpState.dsEnableDepthTest = in.gpState.dsEnableDepthTest; + out.gpState.dsEnableDepthWrite = in.gpState.dsEnableDepthWrite; + out.gpState.dsEnableStencilTest = in.gpState.dsEnableStencilTest; + out.gpState.dsDepthCompareOp = in.gpState.dsDepthCompareOp; + out.gpState.dsStencilOpFront = in.gpState.dsStencilOpFront; + out.gpState.dsStencilOpBack = in.gpState.dsStencilOpBack; + + out.gpState.omEnableLogicOp = in.gpState.omEnableLogicOp; + out.gpState.omLogicOp = in.gpState.omLogicOp; + + for (uint32_t i = 0; i < MaxNumRenderTargets; i++) { + out.gpState.omBlendAttachments[i] = in.gpState.omBlendAttachments[i]; + out.gpState.omComponentMapping[i] = in.gpState.omComponentMapping[i]; + } + + return true; + } + + void DxvkStateCache::workerFunc() { env::setThreadName("dxvk-shader"); diff --git a/src/dxvk/dxvk_state_cache.h b/src/dxvk/dxvk_state_cache.h index 938f9c719..599e6df00 100644 --- a/src/dxvk/dxvk_state_cache.h +++ b/src/dxvk/dxvk_state_cache.h @@ -8,67 +8,12 @@ #include #include -#include "dxvk_pipemanager.h" -#include "dxvk_renderpass.h" +#include "dxvk_state_cache_types.h" namespace dxvk { class DxvkDevice; - /** - * \brief State cache entry key - * - * Stores the shader keys for all - * graphics shader stages. Used to - * look up cached state entries. - */ - struct DxvkStateCacheKey { - DxvkShaderKey vs; - DxvkShaderKey tcs; - DxvkShaderKey tes; - DxvkShaderKey gs; - DxvkShaderKey fs; - DxvkShaderKey cs; - - bool eq(const DxvkStateCacheKey& key) const; - - size_t hash() const; - }; - - - /** - * \brief State entry - * - * Stores the shaders used in a pipeline, as well - * as the full state vector, including its render - * pass format. This also includes a SHA-1 hash - * that is used as a check sum to verify integrity. - */ - struct DxvkStateCacheEntry { - DxvkStateCacheKey shaders; - DxvkGraphicsPipelineStateInfo gpState; - DxvkComputePipelineStateInfo cpState; - DxvkRenderPassFormat format; - Sha1Hash hash; - }; - - - /** - * \brief State cache header - * - * Stores the state cache format version. If an - * existing cache file is incompatible to the - * current version, it will be discarded. - */ - struct DxvkStateCacheHeader { - char magic[4] = { 'D', 'X', 'V', 'K' }; - uint32_t version = 4; - uint32_t entrySize = sizeof(DxvkStateCacheEntry); - }; - - static_assert(sizeof(DxvkStateCacheHeader) == 12); - - /** * \brief State cache * @@ -203,6 +148,7 @@ namespace dxvk { DxvkStateCacheHeader& header) const; bool readCacheEntry( + uint32_t version, std::istream& stream, DxvkStateCacheEntry& entry) const; @@ -211,7 +157,11 @@ namespace dxvk { DxvkStateCacheEntry& entry) const; bool convertEntryV2( - DxvkStateCacheEntry& entry) const; + DxvkStateCacheEntryV4& entry) const; + + bool convertEntryV4( + const DxvkStateCacheEntryV4& in, + DxvkStateCacheEntry& out) const; void workerFunc(); diff --git a/src/dxvk/dxvk_state_cache_types.h b/src/dxvk/dxvk_state_cache_types.h new file mode 100644 index 000000000..40f5850aa --- /dev/null +++ b/src/dxvk/dxvk_state_cache_types.h @@ -0,0 +1,117 @@ +#pragma once + +#include "dxvk_pipemanager.h" +#include "dxvk_renderpass.h" + +namespace dxvk { + + /** + * \brief State cache entry key + * + * Stores the shader keys for all + * graphics shader stages. Used to + * look up cached state entries. + */ + struct DxvkStateCacheKey { + DxvkShaderKey vs; + DxvkShaderKey tcs; + DxvkShaderKey tes; + DxvkShaderKey gs; + DxvkShaderKey fs; + DxvkShaderKey cs; + + bool eq(const DxvkStateCacheKey& key) const; + + size_t hash() const; + }; + + + /** + * \brief State entry + * + * Stores the shaders used in a pipeline, as well + * as the full state vector, including its render + * pass format. This also includes a SHA-1 hash + * that is used as a check sum to verify integrity. + */ + struct DxvkStateCacheEntry { + DxvkStateCacheKey shaders; + DxvkGraphicsPipelineStateInfo gpState; + DxvkComputePipelineStateInfo cpState; + DxvkRenderPassFormat format; + Sha1Hash hash; + }; + + + /** + * \brief State cache header + * + * Stores the state cache format version. If an + * existing cache file is incompatible to the + * current version, it will be discarded. + */ + struct DxvkStateCacheHeader { + char magic[4] = { 'D', 'X', 'V', 'K' }; + uint32_t version = 5; + uint32_t entrySize = sizeof(DxvkStateCacheEntry); + }; + + static_assert(sizeof(DxvkStateCacheHeader) == 12); + + + /** + * \brief Version 4 graphics pipeline state + */ + struct DxvkGraphicsPipelineStateInfoV4 { + DxvkBindingMask bsBindingMask; + + VkPrimitiveTopology iaPrimitiveTopology; + VkBool32 iaPrimitiveRestart; + uint32_t iaPatchVertexCount; + + uint32_t ilAttributeCount; + uint32_t ilBindingCount; + VkVertexInputAttributeDescription ilAttributes[DxvkLimits::MaxNumVertexAttributes]; + VkVertexInputBindingDescription ilBindings[DxvkLimits::MaxNumVertexBindings]; + uint32_t ilDivisors[DxvkLimits::MaxNumVertexBindings]; + + VkBool32 rsDepthClipEnable; + VkBool32 rsDepthBiasEnable; + VkPolygonMode rsPolygonMode; + VkCullModeFlags rsCullMode; + VkFrontFace rsFrontFace; + uint32_t rsViewportCount; + VkSampleCountFlags rsSampleCount; + + VkSampleCountFlags msSampleCount; + uint32_t msSampleMask; + VkBool32 msEnableAlphaToCoverage; + + VkCompareOp xsAlphaCompareOp; + + VkBool32 dsEnableDepthTest; + VkBool32 dsEnableDepthWrite; + VkBool32 dsEnableStencilTest; + VkCompareOp dsDepthCompareOp; + VkStencilOpState dsStencilOpFront; + VkStencilOpState dsStencilOpBack; + + VkBool32 omEnableLogicOp; + VkLogicOp omLogicOp; + VkPipelineColorBlendAttachmentState omBlendAttachments[MaxNumRenderTargets]; + VkComponentMapping omComponentMapping[MaxNumRenderTargets]; + }; + + + /** + * \brief Version 4 state cache entry + */ + struct DxvkStateCacheEntryV4 { + DxvkStateCacheKey shaders; + DxvkGraphicsPipelineStateInfoV4 gpState; + DxvkComputePipelineStateInfo cpState; + DxvkRenderPassFormat format; + Sha1Hash hash; + }; + +}