diff --git a/fossilize.cpp b/fossilize.cpp index d9856497..b4948bde 100644 --- a/fossilize.cpp +++ b/fossilize.cpp @@ -47,6 +47,7 @@ #include "rapidjson/writer.h" using namespace rapidjson; +using Allocator = RAPIDJSON_DEFAULT_ALLOCATOR; #ifdef PRETTY_WRITER using CustomWriter = PrettyWriter; @@ -102,7 +103,6 @@ struct HashedInfo T info; }; -template static Value uint64_string(uint64_t value, Allocator &alloc) { char str[17]; // 16 digits + null @@ -9494,11 +9494,10 @@ static std::string encode_base64(const void *data_, size_t size) return ret; } -template +template static bool pnext_chain_add_json_value(Value &base, const T &t, Allocator &alloc, const DynamicStateInfo *dynamic_state_info); -template static bool json_value(const VkSamplerCreateInfo& sampler, Allocator& alloc, Value *out_value) { Value s(kObjectType); @@ -9526,7 +9525,6 @@ static bool json_value(const VkSamplerCreateInfo& sampler, Allocator& alloc, Val return true; } -template static bool json_value(const VkPipelineLayoutCreateInfo& layout, Allocator& alloc, Value *out_value) { Value p(kObjectType); @@ -9551,7 +9549,6 @@ static bool json_value(const VkPipelineLayoutCreateInfo& layout, Allocator& allo return true; } -template static bool json_value(const VkShaderModuleCreateInfo& module, Allocator& alloc, Value *out_value) { Value m(kObjectType); @@ -9563,7 +9560,6 @@ static bool json_value(const VkShaderModuleCreateInfo& module, Allocator& alloc, return true; } -template static bool json_value(const VkPipelineTessellationDomainOriginStateCreateInfo &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9574,7 +9570,6 @@ static bool json_value(const VkPipelineTessellationDomainOriginStateCreateInfo & return true; } -template static bool json_value(const VkPipelineVertexInputDivisorStateCreateInfoKHR &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9598,7 +9593,6 @@ static bool json_value(const VkPipelineVertexInputDivisorStateCreateInfoKHR &cre return true; } -template static bool json_value(const VkPipelineRasterizationDepthClipStateCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9610,7 +9604,6 @@ static bool json_value(const VkPipelineRasterizationDepthClipStateCreateInfoEXT return true; } -template static bool json_value(const VkPipelineRasterizationStateStreamCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9622,7 +9615,6 @@ static bool json_value(const VkPipelineRasterizationStateStreamCreateInfoEXT &cr return true; } -template static bool json_value(const VkRenderPassMultiviewCreateInfo &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9656,7 +9648,6 @@ static bool json_value(const VkRenderPassMultiviewCreateInfo &create_info, Alloc return true; } -template static bool json_value(const VkDescriptorSetLayoutBindingFlagsCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9674,7 +9665,6 @@ static bool json_value(const VkDescriptorSetLayoutBindingFlagsCreateInfoEXT &cre return true; } -template static bool json_value(const VkPipelineColorBlendAdvancedStateCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9688,7 +9678,6 @@ static bool json_value(const VkPipelineColorBlendAdvancedStateCreateInfoEXT &cre return true; } -template static bool json_value(const VkPipelineRasterizationConservativeStateCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9702,7 +9691,6 @@ static bool json_value(const VkPipelineRasterizationConservativeStateCreateInfoE return true; } -template static bool json_value(const VkPipelineRasterizationLineStateCreateInfoKHR &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9717,7 +9705,6 @@ static bool json_value(const VkPipelineRasterizationLineStateCreateInfoKHR &crea return true; } -template static bool json_value(const VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9728,7 +9715,6 @@ static bool json_value(const VkPipelineShaderStageRequiredSubgroupSizeCreateInfo return true; } -template static bool json_value(const VkMutableDescriptorTypeCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9749,7 +9735,6 @@ static bool json_value(const VkMutableDescriptorTypeCreateInfoEXT &create_info, return true; } -template static bool json_value(const VkAttachmentDescriptionStencilLayout &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9760,7 +9745,6 @@ static bool json_value(const VkAttachmentDescriptionStencilLayout &create_info, return true; } -template static bool json_value(const VkAttachmentReferenceStencilLayout &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9770,7 +9754,6 @@ static bool json_value(const VkAttachmentReferenceStencilLayout &create_info, Al return true; } -template static bool json_value(const VkPipelineRenderingCreateInfoKHR &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9791,7 +9774,6 @@ static bool json_value(const VkPipelineRenderingCreateInfoKHR &create_info, Allo return true; } -template static bool json_value(const VkPipelineColorWriteCreateInfoEXT &create_info, Allocator &alloc, Value *out_value, const DynamicStateInfo *dynamic_state_info) { @@ -9814,7 +9796,6 @@ static bool json_value(const VkPipelineColorWriteCreateInfoEXT &create_info, All return true; } -template static bool json_value(const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9825,7 +9806,6 @@ static bool json_value(const VkPipelineRasterizationProvokingVertexStateCreateIn return true; } -template static bool json_value(const VkSamplerCustomBorderColorCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9841,7 +9821,6 @@ static bool json_value(const VkSamplerCustomBorderColorCreateInfoEXT &create_inf return true; } -template static bool json_value(const VkSamplerReductionModeCreateInfo &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9852,7 +9831,6 @@ static bool json_value(const VkSamplerReductionModeCreateInfo &create_info, Allo return true; } -template static bool json_value(const VkRenderPassInputAttachmentAspectCreateInfo &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9873,7 +9851,6 @@ static bool json_value(const VkRenderPassInputAttachmentAspectCreateInfo &create return true; } -template static bool json_value(const VkPipelineDiscardRectangleStateCreateInfoEXT &create_info, Allocator &alloc, Value *out_value, const DynamicStateInfo *dynamic_state_info) { @@ -9902,7 +9879,6 @@ static bool json_value(const VkPipelineDiscardRectangleStateCreateInfoEXT &creat return true; } -template static bool json_value(const VkMemoryBarrier2KHR &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9916,7 +9892,6 @@ static bool json_value(const VkMemoryBarrier2KHR &create_info, Allocator &alloc, return true; } -template static bool json_value(const VkGraphicsPipelineLibraryCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9926,7 +9901,6 @@ static bool json_value(const VkGraphicsPipelineLibraryCreateInfoEXT &create_info return true; } -template static bool json_value(const VkPipelineFragmentShadingRateStateCreateInfoKHR &create_info, Allocator &alloc, Value *out_value, const DynamicStateInfo *dynamic_state_info) { @@ -9950,7 +9924,6 @@ static bool json_value(const VkPipelineFragmentShadingRateStateCreateInfoKHR &cr return true; } -template static bool json_value(const VkSamplerYcbcrConversionCreateInfo &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -9974,7 +9947,6 @@ static bool json_value(const VkSamplerYcbcrConversionCreateInfo &create_info, Al return true; } -template static bool json_value(const VkPipelineLibraryCreateInfoKHR &create_info, Allocator &alloc, bool in_pnext_chain, Value *out_value) { @@ -9998,7 +9970,6 @@ static bool json_value(const VkPipelineLibraryCreateInfoKHR &create_info, Alloca return true; } -template static bool json_value(const VkPipelineViewportDepthClipControlCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { @@ -10009,7 +9980,6 @@ static bool json_value(const VkPipelineViewportDepthClipControlCreateInfoEXT &cr return true; } -template static bool json_value(const VkRenderPassCreationControlEXT &create_info, Allocator &alloc, Value *out_value) { @@ -10020,7 +9990,6 @@ static bool json_value(const VkRenderPassCreationControlEXT &create_info, return true; } -template static bool json_value(const VkSamplerBorderColorComponentMappingCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { @@ -10037,7 +10006,6 @@ static bool json_value(const VkSamplerBorderColorComponentMappingCreateInfoEXT & return true; } -template static bool json_value(const VkMultisampledRenderToSingleSampledInfoEXT &create_info, Allocator &alloc, Value *out_value) { @@ -10049,7 +10017,6 @@ static bool json_value(const VkMultisampledRenderToSingleSampledInfoEXT &create_ return true; } -template static bool json_value(const VkDepthBiasRepresentationInfoEXT &create_info, Allocator &alloc, Value *out_value) { @@ -10061,7 +10028,6 @@ static bool json_value(const VkDepthBiasRepresentationInfoEXT &create_info, return true; } -template static bool json_value(const VkRenderPassFragmentDensityMapCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { @@ -10075,7 +10041,6 @@ static bool json_value(const VkRenderPassFragmentDensityMapCreateInfoEXT &create return true; } -template static bool json_value(const VkSampleLocationsInfoEXT &create_info, Allocator &alloc, Value *out_value) { @@ -10107,7 +10072,6 @@ static bool json_value(const VkSampleLocationsInfoEXT &create_info, return true; } -template static bool json_value(const VkPipelineRobustnessCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { @@ -10121,7 +10085,6 @@ static bool json_value(const VkPipelineRobustnessCreateInfoEXT &create_info, return true; } -template static bool json_value(const VkPipelineCreateFlags2CreateInfoKHR &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -10131,7 +10094,6 @@ static bool json_value(const VkPipelineCreateFlags2CreateInfoKHR &create_info, A return true; } -template static bool json_value(const VkPipelineViewportDepthClampControlCreateInfoEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -10150,7 +10112,6 @@ static bool json_value(const VkPipelineViewportDepthClampControlCreateInfoEXT &c return true; } -template static bool json_value(const VkRenderingAttachmentLocationInfoKHR &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -10169,7 +10130,6 @@ static bool json_value(const VkRenderingAttachmentLocationInfoKHR &create_info, return true; } -template static bool json_value(const VkRenderingInputAttachmentIndexInfoKHR &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -10193,7 +10153,6 @@ static bool json_value(const VkRenderingInputAttachmentIndexInfoKHR &create_info return true; } -template static bool json_value(const VkPipelineFragmentDensityMapLayeredCreateInfoVALVE &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -10203,7 +10162,6 @@ static bool json_value(const VkPipelineFragmentDensityMapLayeredCreateInfoVALVE return true; } -template static bool json_value(const VkRayTracingPipelineClusterAccelerationStructureCreateInfoNV &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -10213,7 +10171,6 @@ static bool json_value(const VkRayTracingPipelineClusterAccelerationStructureCre return true; } -template static bool json_value(const VkShaderDescriptorSetAndBindingMappingInfoEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -10387,12 +10344,11 @@ static bool json_value(const VkShaderDescriptorSetAndBindingMappingInfoEXT &crea return true; } -template static bool json_value(const VkSubpassDescriptionDepthStencilResolve &create_info, Allocator &alloc, Value *out_value); -template static bool json_value(const VkFragmentShadingRateAttachmentInfoKHR &create_info, Allocator &alloc, Value *out_value); +static bool json_value(const VkPipelineSampleLocationsStateCreateInfoEXT &create_info, Allocator &alloc, Value *out_value, + const DynamicStateInfo *dynamic_state_info); -template static bool pnext_chain_json_value(const void *pNext, Allocator &alloc, Value *out_value, const DynamicStateInfo *dynamic_state_info) { @@ -10641,7 +10597,7 @@ static bool pnext_chain_json_value(const void *pNext, Allocator &alloc, Value *o return true; } -template +template static bool pnext_chain_add_json_value(Value &base, const T &t, Allocator &alloc, const DynamicStateInfo *dynamic_state_info) { @@ -10655,7 +10611,6 @@ static bool pnext_chain_add_json_value(Value &base, const T &t, Allocator &alloc return true; } -template static bool json_value(const VkAttachmentReference2 &att, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -10669,7 +10624,6 @@ static bool json_value(const VkAttachmentReference2 &att, Allocator &alloc, Valu return true; } -template static bool json_value(const VkSubpassDescriptionDepthStencilResolve &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -10687,7 +10641,6 @@ static bool json_value(const VkSubpassDescriptionDepthStencilResolve &create_inf return true; } -template static bool json_value(const VkFragmentShadingRateAttachmentInfoKHR &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -10708,7 +10661,6 @@ static bool json_value(const VkFragmentShadingRateAttachmentInfoKHR &create_info return true; } -template static bool json_value(const VkPipelineSampleLocationsStateCreateInfoEXT &create_info, Allocator &alloc, Value *out_value, const DynamicStateInfo *dynamic_state_info) { @@ -10753,7 +10705,6 @@ static bool json_value(const VkPipelineSampleLocationsStateCreateInfoEXT &create return true; } -template static bool json_value(const VkComputePipelineCreateInfo& pipe, Allocator& alloc, Value *out_value) { Value p(kObjectType); @@ -10798,7 +10749,6 @@ static bool json_value(const VkComputePipelineCreateInfo& pipe, Allocator& alloc return true; } -template static bool json_value(const VkDescriptorSetLayoutCreateInfo& layout, Allocator& alloc, Value *out_value) { Value l(kObjectType); @@ -10831,7 +10781,6 @@ static bool json_value(const VkDescriptorSetLayoutCreateInfo& layout, Allocator& return true; } -template static bool json_value(const VkRenderPassCreateInfo& pass, Allocator& alloc, Value *out_value) { Value json_object(kObjectType); @@ -10957,7 +10906,6 @@ static bool json_value(const VkRenderPassCreateInfo& pass, Allocator& alloc, Val return true; } -template static bool json_value(const VkRenderPassCreateInfo2 &pass, Allocator &alloc, Value *out_value) { Value json_object(kObjectType); @@ -11095,7 +11043,6 @@ static bool json_value(const VkRenderPassCreateInfo2 &pass, Allocator &alloc, Va return true; } -template static bool json_value(const VkPipelineShaderStageCreateInfo *pStages, uint32_t stageCount, Allocator &alloc, Value *out_value) { @@ -11138,7 +11085,6 @@ static bool json_value(const VkPipelineShaderStageCreateInfo *pStages, uint32_t return true; } -template static bool json_value(const VkPipelineDynamicStateCreateInfo &dynamic, Allocator &alloc, Value *out_value) { Value dyn(kObjectType); @@ -11152,7 +11098,6 @@ static bool json_value(const VkPipelineDynamicStateCreateInfo &dynamic, Allocato return true; } -template static bool json_value(const VkRayTracingPipelineCreateInfoKHR &pipe, Allocator &alloc, Value *out_value) { Value p(kObjectType); @@ -11215,7 +11160,6 @@ static bool json_value(const VkRayTracingPipelineCreateInfoKHR &pipe, Allocator return true; } -template static bool json_value(const VkGraphicsPipelineCreateInfo &pipe, const StateRecorder::SubpassMeta &subpass_meta, Allocator &alloc, Value *out_value) @@ -11497,8 +11441,7 @@ static bool json_value(const VkGraphicsPipelineCreateInfo &pipe, return true; } -template -static void serialize_application_info_inline(Value &value, const VkApplicationInfo &info, AllocType &alloc) +static void serialize_application_info_inline(Value &value, const VkApplicationInfo &info, Allocator &alloc) { if (info.pApplicationName) value.AddMember("applicationName", StringRef(info.pApplicationName), alloc); @@ -11509,7 +11452,6 @@ static void serialize_application_info_inline(Value &value, const VkApplicationI value.AddMember("apiVersion", info.apiVersion, alloc); } -template static bool json_value(const VkPhysicalDeviceRobustness2FeaturesEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -11521,7 +11463,6 @@ static bool json_value(const VkPhysicalDeviceRobustness2FeaturesEXT &create_info return true; } -template static bool json_value(const VkPhysicalDeviceImageRobustnessFeaturesEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -11531,7 +11472,6 @@ static bool json_value(const VkPhysicalDeviceImageRobustnessFeaturesEXT &create_ return true; } -template static bool json_value(const VkPhysicalDeviceFragmentShadingRateEnumsFeaturesNV &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -11543,7 +11483,6 @@ static bool json_value(const VkPhysicalDeviceFragmentShadingRateEnumsFeaturesNV return true; } -template static bool json_value(const VkPhysicalDeviceFragmentShadingRateFeaturesKHR &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -11555,7 +11494,6 @@ static bool json_value(const VkPhysicalDeviceFragmentShadingRateFeaturesKHR &cre return true; } -template static bool json_value(const VkPhysicalDeviceMeshShaderFeaturesEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -11569,7 +11507,6 @@ static bool json_value(const VkPhysicalDeviceMeshShaderFeaturesEXT &create_info, return true; } -template static bool json_value(const VkPhysicalDeviceMeshShaderFeaturesNV &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -11580,7 +11517,6 @@ static bool json_value(const VkPhysicalDeviceMeshShaderFeaturesNV &create_info, return true; } -template static bool json_value(const VkPhysicalDeviceDescriptorBufferFeaturesEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -11593,7 +11529,6 @@ static bool json_value(const VkPhysicalDeviceDescriptorBufferFeaturesEXT &create return true; } -template static bool json_value(const VkPhysicalDeviceShaderObjectFeaturesEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -11603,7 +11538,6 @@ static bool json_value(const VkPhysicalDeviceShaderObjectFeaturesEXT &create_inf return true; } -template static bool json_value(const VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -11615,7 +11549,6 @@ static bool json_value(const VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT return true; } -template static bool json_value(const VkPhysicalDeviceImage2DViewOf3DFeaturesEXT &create_info, Allocator &alloc, Value *out_value) { Value value(kObjectType); @@ -11626,7 +11559,6 @@ static bool json_value(const VkPhysicalDeviceImage2DViewOf3DFeaturesEXT &create_ return true; } -template static bool pnext_chain_pdf2_json_value(const void *pNext, Allocator &alloc, Value *out_value) { Value nexts(kArrayType); @@ -11700,7 +11632,7 @@ static bool pnext_chain_pdf2_json_value(const void *pNext, Allocator &alloc, Val return true; } -template +template static bool pnext_chain_pdf2_add_json_value(Value &base, const T &t, Allocator &alloc) { if (t.pNext) @@ -11713,8 +11645,7 @@ static bool pnext_chain_pdf2_add_json_value(Value &base, const T &t, Allocator & return true; } -template -static bool serialize_physical_device_features_inline(Value &value, const VkPhysicalDeviceFeatures2 &features, AllocType &alloc) +static bool serialize_physical_device_features_inline(Value &value, const VkPhysicalDeviceFeatures2 &features, Allocator &alloc) { value.AddMember("robustBufferAccess", features.features.robustBufferAccess, alloc); if (!pnext_chain_pdf2_add_json_value(value, features, alloc)) diff --git a/fossilize_application_filter.cpp b/fossilize_application_filter.cpp index cb7c22aa..88434112 100644 --- a/fossilize_application_filter.cpp +++ b/fossilize_application_filter.cpp @@ -36,6 +36,8 @@ #include "rapidjson/writer.h" using namespace rapidjson; +using Allocator = RAPIDJSON_DEFAULT_ALLOCATOR; + namespace Fossilize { enum { FOSSILIZE_APPLICATION_INFO_FILTER_VERSION = 2 }; @@ -141,7 +143,13 @@ struct ApplicationInfoFilter::Impl bool needs_buckets(const VkApplicationInfo *info); Hash get_bucket_hash(const VkPhysicalDeviceProperties2 *props, const VkApplicationInfo *info, - const void *device_pnext); + const void *device_pnext, + Value *json, Allocator *json_allocator); + + std::string get_bucket_json_description( + const VkPhysicalDeviceProperties2 *props2, + const VkApplicationInfo *info, + const void *device_pnext); const char *(*getenv_wrapper)(const char *, void *) = nullptr; void *getenv_userdata = nullptr; @@ -225,7 +233,8 @@ static void hash_variant(Hasher &h, VariantDependency dep, const VkPhysicalDeviceProperties2 *props, const VkApplicationInfo *info, const void *device_pnext, - bool feature_hash) + bool feature_hash, + Value *json, Allocator *json_allocator) { const VkApplicationInfo default_app_info = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; bool hash_enabled = true; @@ -236,8 +245,13 @@ static void hash_variant(Hasher &h, VariantDependency dep, switch (dep) { case VariantDependency::VendorID: - h.u32(props ? props->properties.vendorID : 0); + { + uint32_t vendorID = props ? props->properties.vendorID : 0; + if (json) + json->AddMember("VendorID", vendorID, *json_allocator); + h.u32(vendorID); break; + } case VariantDependency::MutableDescriptorType: { @@ -246,6 +260,8 @@ static void hash_variant(Hasher &h, VariantDependency dep, device_pnext); bool enabled = mut && mut->mutableDescriptorType; + if (json) + json->AddMember("MutableDescriptorType", int(enabled), *json_allocator); if (feature_hash) { @@ -271,6 +287,9 @@ static void hash_variant(Hasher &h, VariantDependency dep, bool enabled = (bda && bda->bufferDeviceAddress) || (features12 && features12->bufferDeviceAddress); + if (json) + json->AddMember("BufferDeviceAddress", int(enabled), *json_allocator); + if (feature_hash) { hash_enabled = enabled; @@ -292,6 +311,9 @@ static void hash_variant(Hasher &h, VariantDependency dep, vrs->pipelineFragmentShadingRate || vrs->primitiveFragmentShadingRate); + if (json) + json->AddMember("FragmentShadingRate", int(enabled), *json_allocator); + if (feature_hash) { hash_enabled = enabled; @@ -304,6 +326,21 @@ static void hash_variant(Hasher &h, VariantDependency dep, h.u32(uint32_t(vrs && vrs->attachmentFragmentShadingRate)); h.u32(uint32_t(vrs && vrs->pipelineFragmentShadingRate)); h.u32(uint32_t(vrs && vrs->primitiveFragmentShadingRate)); + + if (json) + { + json->AddMember("attachmentFragmentShadingRate", + int(vrs && vrs->attachmentFragmentShadingRate), + *json_allocator); + + json->AddMember("pipelineFragmentShadingRate", + int(vrs && vrs->pipelineFragmentShadingRate), + *json_allocator); + + json->AddMember("primitiveFragmentShadingRate", + int(vrs && vrs->primitiveFragmentShadingRate), + *json_allocator); + } } break; } @@ -317,6 +354,9 @@ static void hash_variant(Hasher &h, VariantDependency dep, bool enabled = (dynamic_rendering && dynamic_rendering->dynamicRendering) || (features13 && features13->dynamicRendering); + if (json) + json->AddMember("DynamicRendering", int(enabled), *json_allocator); + if (feature_hash) { hash_enabled = enabled; @@ -340,6 +380,9 @@ static void hash_variant(Hasher &h, VariantDependency dep, bool enabled = (indexing && indexing->descriptorBindingUniformBufferUpdateAfterBind) || (features12 && features12->descriptorBindingUniformBufferUpdateAfterBind); + if (json) + json->AddMember("BindlessUBO", int(enabled), *json_allocator); + if (feature_hash) { hash_enabled = enabled; @@ -366,6 +409,9 @@ static void hash_variant(Hasher &h, VariantDependency dep, bool enabled = descriptor_buffer && (descriptor_buffer->descriptorBuffer || descriptor_buffer->descriptorBufferPushDescriptors); + if (json) + json->AddMember("DescriptorBuffer", int(enabled), *json_allocator); + if (feature_hash) { hash_enabled = enabled; @@ -379,6 +425,16 @@ static void hash_variant(Hasher &h, VariantDependency dep, h.u32(uint32_t(descriptor_buffer && descriptor_buffer->descriptorBufferPushDescriptors)); // The other feature bits are highly unlikely to ever affect // pipeline construction in application in any meaningful way. + if (json) + { + json->AddMember("descriptorBuffer", + int(descriptor_buffer && descriptor_buffer->descriptorBuffer), + *json_allocator); + + json->AddMember("descriptorBufferPushDescriptors", + int(descriptor_buffer && descriptor_buffer->descriptorBufferPushDescriptors), + *json_allocator); + } } break; } @@ -391,6 +447,9 @@ static void hash_variant(Hasher &h, VariantDependency dep, bool enabled = descriptor_heap && descriptor_heap->descriptorHeap; + if (json) + json->AddMember("DescriptorHeap", int(enabled), *json_allocator); + if (feature_hash) { hash_enabled = enabled; @@ -405,42 +464,62 @@ static void hash_variant(Hasher &h, VariantDependency dep, } case VariantDependency::ApplicationVersion: + if (json) + json->AddMember("ApplicationVersion", info->applicationVersion, *json_allocator); h.u32(info->applicationVersion); break; case VariantDependency::ApplicationVersionMajor: + if (json) + json->AddMember("ApplicationVersionMajor", VK_VERSION_MAJOR(info->applicationVersion), *json_allocator); h.u32(VK_VERSION_MAJOR(info->applicationVersion)); break; case VariantDependency::ApplicationVersionMinor: + if (json) + json->AddMember("ApplicationVersionMinor", VK_VERSION_MINOR(info->applicationVersion), *json_allocator); h.u32(VK_VERSION_MINOR(info->applicationVersion)); break; case VariantDependency::ApplicationVersionPatch: + if (json) + json->AddMember("ApplicationVersionPatch", VK_VERSION_PATCH(info->applicationVersion), *json_allocator); h.u32(VK_VERSION_PATCH(info->applicationVersion)); break; case VariantDependency::EngineVersion: + if (json) + json->AddMember("EngineVersion", info->engineVersion, *json_allocator); h.u32(info->engineVersion); break; case VariantDependency::EngineVersionMajor: + if (json) + json->AddMember("EngineVersionMajor", VK_VERSION_MAJOR(info->engineVersion), *json_allocator); h.u32(VK_VERSION_MAJOR(info->engineVersion)); break; case VariantDependency::EngineVersionMinor: + if (json) + json->AddMember("EngineVersionMinor", VK_VERSION_MINOR(info->engineVersion), *json_allocator); h.u32(VK_VERSION_MINOR(info->engineVersion)); break; case VariantDependency::EngineVersionPatch: + if (json) + json->AddMember("EngineVersionPatch", VK_VERSION_PATCH(info->engineVersion), *json_allocator); h.u32(VK_VERSION_PATCH(info->engineVersion)); break; case VariantDependency::ApplicationName: + if (json) + json->AddMember("ApplicationName", StringRef(info->pApplicationName ? info->pApplicationName : ""), *json_allocator); h.string(info->pApplicationName ? info->pApplicationName : ""); break; case VariantDependency::EngineName: + if (json) + json->AddMember("EngineName", StringRef(info->pEngineName ? info->pEngineName : ""), *json_allocator); h.string(info->pEngineName ? info->pEngineName : ""); break; @@ -449,9 +528,28 @@ static void hash_variant(Hasher &h, VariantDependency dep, } } +std::string ApplicationInfoFilter::Impl::get_bucket_json_description( + const VkPhysicalDeviceProperties2 *props2, const VkApplicationInfo *info, + const void *device_pnext) +{ + Document doc; + doc.SetObject(); + Value bucket(kObjectType); + get_bucket_hash(props2, info, device_pnext, &bucket, &doc.GetAllocator()); + doc.AddMember("bucket", bucket, doc.GetAllocator()); + + StringBuffer buf; + PrettyWriter writer(buf); + doc.Accept(writer); + std::string str; + str.insert(str.end(), buf.GetString(), buf.GetString() + buf.GetLength()); + return str; +} + Hash ApplicationInfoFilter::Impl::get_bucket_hash(const VkPhysicalDeviceProperties2 *props, const VkApplicationInfo *info, - const void *device_pnext) + const void *device_pnext, + Value *json, Allocator *json_allocator) { Hasher h; bool use_default_variant = true; @@ -464,9 +562,9 @@ Hash ApplicationInfoFilter::Impl::get_bucket_hash(const VkPhysicalDeviceProperti { use_default_variant = false; for (auto &dep : itr->second.variant_dependencies) - hash_variant(h, dep, props, info, device_pnext, false); + hash_variant(h, dep, props, info, device_pnext, false, json, json_allocator); for (auto &dep : itr->second.variant_dependencies_feature) - hash_variant(h, dep, props, info, device_pnext, true); + hash_variant(h, dep, props, info, device_pnext, true, json, json_allocator); } } @@ -478,24 +576,47 @@ Hash ApplicationInfoFilter::Impl::get_bucket_hash(const VkPhysicalDeviceProperti { use_default_variant = false; for (auto &dep : itr->second.variant_dependencies) - hash_variant(h, dep, props, info, device_pnext, false); + hash_variant(h, dep, props, info, device_pnext, false, json, json_allocator); for (auto &dep : itr->second.variant_dependencies_feature) - hash_variant(h, dep, props, info, device_pnext, true); + hash_variant(h, dep, props, info, device_pnext, true, json, json_allocator); + + Value applicationDeltas(kArrayType); + Value engineDeltas(kArrayType); + for (auto &dep : itr->second.application_version_deltas) + { if (info->applicationVersion >= dep) + { h.u32(dep ^ 0xabba); + if (json) + applicationDeltas.PushBack(dep, *json_allocator); + } + } + for (auto &dep : itr->second.engine_version_deltas) + { if (info->engineVersion >= dep) + { h.u32(dep ^ 0xcafe); + if (json) + engineDeltas.PushBack(dep, *json_allocator); + } + } + + if (json) + { + json->AddMember("applicationVersionBucketDeltas", applicationDeltas, *json_allocator); + json->AddMember("engineVersionBucketDeltas", engineDeltas, *json_allocator); + } } } if (use_default_variant) { for (auto &dep : default_variant_dependencies) - hash_variant(h, dep, props, info, device_pnext, false); + hash_variant(h, dep, props, info, device_pnext, false, json, json_allocator); for (auto &dep : default_variant_dependencies_feature) - hash_variant(h, dep, props, info, device_pnext, true); + hash_variant(h, dep, props, info, device_pnext, true, json, json_allocator); } return h.get(); @@ -924,7 +1045,13 @@ Hash ApplicationInfoFilter::get_bucket_hash(const VkPhysicalDeviceProperties2 *p const VkApplicationInfo *info, const void *device_pnext) { - return impl->get_bucket_hash(props, info, device_pnext); + return impl->get_bucket_hash(props, info, device_pnext, nullptr, nullptr); +} + +std::string ApplicationInfoFilter::get_bucket_json_description( + const VkPhysicalDeviceProperties2 *props, const VkApplicationInfo *info, const void *device_pnext) +{ + return impl->get_bucket_json_description(props, info, device_pnext); } bool ApplicationInfoFilter::should_record_immutable_samplers(const VkApplicationInfo *info) diff --git a/fossilize_application_filter.hpp b/fossilize_application_filter.hpp index 0e2c88eb..4b688bc1 100644 --- a/fossilize_application_filter.hpp +++ b/fossilize_application_filter.hpp @@ -22,6 +22,7 @@ #pragma once #include "fossilize_types.hpp" +#include struct VkApplicationInfo; struct VkPhysicalDeviceFeatures2; @@ -53,6 +54,10 @@ class ApplicationInfoFilter const void *device_pnext); bool should_record_immutable_samplers(const VkApplicationInfo *info); + std::string get_bucket_json_description(const VkPhysicalDeviceProperties2 *props, + const VkApplicationInfo *info, + const void *device_pnext); + private: ApplicationInfoFilter(); struct Impl; diff --git a/fossilize_db.cpp b/fossilize_db.cpp index edc6e464..d4996d4c 100644 --- a/fossilize_db.cpp +++ b/fossilize_db.cpp @@ -427,6 +427,10 @@ bool DatabaseInterface::set_bucket_path(const char *, const char *) return false; } +void DatabaseInterface::set_bucket_info(const char *) +{ +} + static size_t deduce_imported_size(const void *mapped, size_t maximum_size) { size_t total_size = 0; @@ -1933,6 +1937,23 @@ struct ConcurrentDatabase : DatabaseInterface } base_path += "/"; + + if (!bucket_info.empty()) + { + auto info_path = base_path + "bucket.json"; + FILE *file = fopen(info_path.c_str(), "w"); + if (file) + { + fputs(bucket_info.c_str(), file); + fclose(file); + } + else + { + LOGE("Failed to create file \"%s\".\n", info_path.c_str()); + return false; + } + } + if (!Path::touch(base_path + "TOUCH")) LOGW("Failed to touch last access in %s.\n", base_path.c_str()); base_path += "/" + bucket_basename; @@ -2378,9 +2399,15 @@ struct ConcurrentDatabase : DatabaseInterface return true; } + void set_bucket_info(const char *json) override + { + bucket_info = json; + } + std::string base_path; std::string bucket_dirname; std::string bucket_basename; + std::string bucket_info; DatabaseMode mode; std::unique_ptr readonly_interface; std::unique_ptr writeonly_interface; diff --git a/fossilize_db.hpp b/fossilize_db.hpp index 1230ec96..7b39b2ed 100644 --- a/fossilize_db.hpp +++ b/fossilize_db.hpp @@ -197,6 +197,11 @@ class DatabaseInterface // See further comments after create_concurrent_database(). virtual bool set_bucket_path(const char *bucket_dirname, const char *bucket_basename); + // When using buckets, it's possible to add a bucket.json to the bucket to make it easy + // to check which configuration the bucket belongs to. + // This info does not have semantic meaning for parsing of an archive and can be discarded. + virtual void set_bucket_info(const char *json); + // Request termination of prepare() which can take a long time for very // large archives. This is useful if running prepare() on a thread and the need // arises to stop loading the database. diff --git a/layer/bucket-reverse.py b/layer/bucket-reverse.py new file mode 100644 index 00000000..bdb260a1 --- /dev/null +++ b/layer/bucket-reverse.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 + +import sys +import argparse +import json + +class Hasher: + def __init__(self): + self._h = 0xcbf29ce484222325 + pass + + def _iterate_value(self, value : int): + self._h = (self._h * 0x100000001b3) ^ value + self._h = self._h & 0xffffffffffffffff + + def u32(self, value : int): + self._iterate_value(value & 0xffffffff) + + def boolean(self, value : bool): + self._iterate_value(int(value)) + + def string(self, value : str): + self._iterate_value(0xff) + for c in value: + self._iterate_value(ord(c) & 0xff) + + def get(self): + return self._h + +def version_major(version : int) -> int: + return (version >> 22) & 0x3ff +def version_minor(version : int) -> int: + return (version >> 12) & 0x3ff +def version_patch(version : int) -> int: + return (version >> 0) & 0xfff + +def make_version(major : int, minor : int) -> int: + return (major << 22) | (minor << 12) + +# VendorIDs we expect to see. +VendorIDs = [ + 0x1002, # AMD + 0x8086, # Intel + 0x10de, # NV + 0x5143, # QCOM +] + +def dep_to_stype(dep : str) -> int: + match dep: + case 'MutableDescriptorType': return 1000351000 + case 'BufferDeviceAddress': return 1000257000 + case 'DynamicRendering': return 1000044003 + case 'BindlessUBO': return 1000161001 + case 'DescriptorHeap': return 1000135009 + case 'DescriptorBuffer': return 1000316002 + case _: return 0 + +def reverse_engine_version(filter : dict, hash : int, engine_name : str, engine_version) -> Optional[dict]: + num_variant_deps = len(filter['bucketVariantDependencies']) if 'bucketVariantDependencies' in filter else 0 + num_variant_feature_deps = len(filter['bucketVariantFeatureDependencies']) if 'bucketVariantFeatureDependencies' in filter else 0 + num_bucket_deltas = len(filter['engineVersionBucketDeltas']) if 'engineVersionBucketDeltas' in filter else 0 + num_variants = 1 << (num_variant_deps + num_variant_feature_deps + num_bucket_deltas) + + if num_variant_deps != 0: + # VendorID consumes 2 bits instead of 1. + if 'VendorID' in filter['bucketVariantDependencies']: + num_variants <<= 1 + + # Implied by outer arguments. + if 'EngineVersionMajor' in filter['bucketVariantDependencies']: + num_variants >>= 1 + if 'EngineVersionMinor' in filter['bucketVariantDependencies']: + num_variants >>= 1 + if 'EngineName' in filter['bucketVariantDependencies']: + num_variants >>= 1 + + for variant in range(num_variants): + h = Hasher() + + # Per app hashing + h.u32(0) + # Per engine hashing + h.u32(0) + + candidate = {} + bit_index = 0 + + # Hash in the same way that fossilize application filter would do. + if num_variant_deps != 0: + for dep in filter['bucketVariantDependencies']: + if dep == 'EngineVersionMajor': + candidate[dep] = engine_version[0] + h.u32(engine_version[0]) + elif dep == 'EngineVersionMinor': + candidate[dep] = engine_version[1] + h.u32(engine_version[1]) + elif dep == 'EngineName': + candidate[dep] = engine_name + h.string(engine_name) + elif 'Engine' in dep: + return None + elif 'Application' in dep: + return None + elif dep == 'VendorID': + vendor_id = VendorIDs[(variant >> bit_index) & 3] + candidate[dep] = vendor_id + h.u32(vendor_id) + bit_index += 2 + else: + state_bit = (variant >> bit_index) & 1 + candidate[dep] = state_bit + h.u32(state_bit) + + # Subfeature hashing + if dep == 'FragmentShadingRate': + h.u32(1) # attachmentFragmentShadingRate + h.u32(1) # pipelineFragmentShadingRate + h.u32(1) # primitiveFragmentShadingRate + elif dep == 'DescriptorBuffer': + h.u32(1) # descriptorBuffer + h.u32(1 if engine_name == 'vkd3d' else 0) # descriptorBufferPushDescriptor + + bit_index += 1 + + if num_variant_feature_deps != 0: + for dep in filter['bucketVariantFeatureDependencies']: + state_bit = (variant >> bit_index) & 1 + candidate[dep] = state_bit + if state_bit: + h.u32(dep_to_stype(dep)) + if dep == 'BindlessUBO': + h.u32(10) + + # Subfeature hashing + if dep == 'FragmentShadingRate': + h.u32(1) # attachmentFragmentShadingRate + h.u32(1) # pipelineFragmentShadingRate + h.u32(1) # primitiveFragmentShadingRate + elif dep == 'DescriptorBuffer': + h.u32(1) # descriptorBuffer + h.u32(1 if engine_name == 'vkd3d' else 0) # descriptorBufferPushDescriptor + else: + h.u32(1) # generic + + bit_index += 1 + + if num_bucket_deltas != 0: + deltas = [] + for dep in filter['engineVersionBucketDeltas']: + if make_version(engine_version[0], engine_version[1]) >= dep: + h.u32(dep ^ 0xcafe) + deltas.append(dep) + bit_index += 1 + candidate['engineVersionBucketDeltas'] = deltas + + #print(f'Engine {engine_name}, version {engine_version[0]}.{engine_version[1]}, candidate {candidate} yields hash {hex(h.get())}') + + if h.get() == hash: + return { 'bucket' : candidate } + return None + +def reverse_engine_version_range(filter : dict, hash : int, engine_name : str, + minimum_version, maximum_version, maximum_minor_version) -> Optional[dict]: + # Iterate over all possible versions of the engine in the hope we'll strike gold. + maximum_major_version = maximum_version[0] + while minimum_version[0] <= maximum_major_version: + while minimum_version[1] <= maximum_minor_version: + if minimum_version[0] == maximum_version[0] and minimum_version[1] > maximum_version[1]: + return None + res = reverse_engine_version(filter, hash, engine_name, minimum_version) + if res: + return res + minimum_version = (minimum_version[0], minimum_version[1] + 1) + minimum_version = (minimum_version[0] + 1, 0) + + return None + +def reverse_bucket_hash_vkd3d(filter : dict, hash : int) -> Optional[dict]: + # The Steam filter uses these. That's what we care about. + if 'minimumEngineVersion' not in filter: + return None + + minimum_version = (version_major(filter['minimumEngineVersion']), version_minor(filter['minimumEngineVersion'])) + return reverse_engine_version_range(filter, hash, 'vkd3d', minimum_version, (3, 1), 14) + +def reverse_bucket_hash_dxvk(filter : dict, hash : int) -> Optional[dict]: + # The Steam filter uses these. That's what we care about. + if 'minimumEngineVersion' not in filter: + return None + + minimum_version = (version_major(filter['minimumEngineVersion']), version_minor(filter['minimumEngineVersion'])) + # The minor version is not relevant for DXVK, but there are bucket delta hashes. + return reverse_engine_version_range(filter, hash, 'DXVK', minimum_version, (3, 1), 0) + +# filter is a loaded dict of the fossilize_engine_filters.json. +def reverse_bucket_hash(filter : dict, hash : int) -> Optional[dict]: + if 'asset' not in filter: + return None + if filter['asset'] != 'FossilizeApplicationInfoFilter': + return None + if filter['version'] != 1: + return None + if 'engineFilters' not in filter: + return None + + engine_filters = filter['engineFilters'] + for engine_filter in engine_filters: + if engine_filter == 'DXVK': + result = reverse_bucket_hash_dxvk(engine_filters[engine_filter], hash) + if result: + return result + elif engine_filter == 'vkd3d': + result = reverse_bucket_hash_vkd3d(engine_filters[engine_filter], hash) + if result: + return result + else: + # We only know how to reverse these two. We don't use buckets for anything else right now. + pass + +def main(): + parser = argparse.ArgumentParser(description = 'Tool for reversing a bucket hash back to its bucket.json') + parser.add_argument('--filter', type = str, help = 'Path to fossilize_engine_filters.json') + parser.add_argument('--hash', type = str, help = 'Bucket hash') + args = parser.parse_args() + + if not args.filter: + raise ArgumentError('Must define --filter') + if not args.hash: + raise ArgumentError('Must define --hash') + + hash = int(args.hash, 16) + with open(args.filter, 'r') as f: + filter_dict = json.loads(f.read()) + + bucket = reverse_bucket_hash(filter_dict, hash) + if bucket: + print(json.dumps(bucket, indent = 4)) + else: + print('Could not reverse the bucket') + sys.exit(1) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/layer/instance.cpp b/layer/instance.cpp index 11522329..f74d0a31 100644 --- a/layer/instance.cpp +++ b/layer/instance.cpp @@ -432,6 +432,8 @@ StateRecorder *Instance::getStateRecorderForDevice(const VkPhysicalDevicePropert Hash bucketHash = infoFilter->get_bucket_hash(props, appInfo, device_pnext); sprintf(bucketPath, "%016" PRIx64, bucketHash); + std::string bucketInfo = infoFilter->get_bucket_json_description(props, appInfo, device_pnext); + // For convenience. Makes filenames similar in top-level directory and bucket directories. auto basename = Path::basename(serializationPath); auto prefix = basename; @@ -440,6 +442,8 @@ StateRecorder *Instance::getStateRecorderForDevice(const VkPhysicalDevicePropert prefix += hashString; entry.interface->set_bucket_path(bucketPath, prefix.c_str()); + if (!bucketInfo.empty()) + entry.interface->set_bucket_info(bucketInfo.c_str()); if (entry.last_use_interface) { diff --git a/test/application_info_filter_test.cpp b/test/application_info_filter_test.cpp index 67313dd8..591cbb8d 100644 --- a/test/application_info_filter_test.cpp +++ b/test/application_info_filter_test.cpp @@ -136,7 +136,7 @@ R"delim( std::unique_ptr filter_handle; filter_handle.reset(Fossilize::ApplicationInfoFilter::parse(".__test_appinfo.json", - getenv_wrapper, &data)); + getenv_wrapper, &data)); if (!filter_handle) { @@ -388,12 +388,19 @@ R"delim( if (hash14 != hash15) return EXIT_FAILURE; + std::string json15 = filter.get_bucket_json_description(&props2, &appinfo, &features2); + // Spot check for ApplicationName. appinfo.pApplicationName = "foo"; auto hash16 = filter.get_bucket_hash(&props2, &appinfo, &features2); if (hash16 == hash15) return EXIT_FAILURE; + // Check that the JSON changed too. + std::string json16 = filter.get_bucket_json_description(&props2, &appinfo, &features2); + if (json15 == json16) + return EXIT_FAILURE; + // Check that the default variant hash is used. appinfo.pApplicationName = "blah"; appinfo.pEngineName = "blah2"; @@ -439,34 +446,58 @@ R"delim( features2.pNext = &descriptor_buffer_features; if (filter.get_bucket_hash(&props2, &appinfo, &features2) != hash1) return EXIT_FAILURE; + std::string json1 = filter.get_bucket_json_description(&props2, &appinfo, &features2); features2.pNext = &descriptor_heap_features; if (filter.get_bucket_hash(&props2, &appinfo, &features2) != hash1) return EXIT_FAILURE; + // Check for JSON invariance. Not sure if rapidjson guarantees this, but seems like it. + std::string json2 = filter.get_bucket_json_description(&props2, &appinfo, &features2); + if (json2 != json1) + return EXIT_FAILURE; + features2.pNext = &bda_features; if (filter.get_bucket_hash(&props2, &appinfo, &features2) != hash1) return EXIT_FAILURE; + std::string json3 = filter.get_bucket_json_description(&props2, &appinfo, &features2); + if (json3 != json1) + return EXIT_FAILURE; features2.pNext = &vulkan12_features; if (filter.get_bucket_hash(&props2, &appinfo, &features2) != hash1) return EXIT_FAILURE; + std::string json4 = filter.get_bucket_json_description(&props2, &appinfo, &features2); + if (json4 != json1) + return EXIT_FAILURE; features2.pNext = &vulkan13_features; if (filter.get_bucket_hash(&props2, &appinfo, &features2) != hash1) return EXIT_FAILURE; + std::string json5 = filter.get_bucket_json_description(&props2, &appinfo, &features2); + if (json5 != json1) + return EXIT_FAILURE; features2.pNext = &indexing_features; if (filter.get_bucket_hash(&props2, &appinfo, &features2) != hash1) return EXIT_FAILURE; + std::string json6 = filter.get_bucket_json_description(&props2, &appinfo, &features2); + if (json6 != json1) + return EXIT_FAILURE; features2.pNext = &mutable_features; if (filter.get_bucket_hash(&props2, &appinfo, &features2) != hash1) return EXIT_FAILURE; + std::string json7 = filter.get_bucket_json_description(&props2, &appinfo, &features2); + if (json7 != json1) + return EXIT_FAILURE; features2.pNext = &vrs_features; if (filter.get_bucket_hash(&props2, &appinfo, &features2) != hash1) return EXIT_FAILURE; + std::string json8 = filter.get_bucket_json_description(&props2, &appinfo, &features2); + if (json8 != json1) + return EXIT_FAILURE; features2.pNext = &descriptor_buffer_features; descriptor_buffer_features.descriptorBuffer = VK_TRUE;