// Copyright 2023 The Khronos Group Inc. // Copyright 2023 Valve Corporation // Copyright 2023 LunarG, Inc. // // SPDX-License-Identifier: Apache-2.0 // #include #include #include #include TEST(safe_struct, basic) { vku::safe_VkInstanceCreateInfo safe_info; { VkApplicationInfo app = vku::InitStructHelper(); app.pApplicationName = "test"; app.applicationVersion = 42; VkDebugUtilsMessengerCreateInfoEXT debug_ci = vku::InitStructHelper(); debug_ci.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; VkInstanceCreateInfo info = vku::InitStructHelper(); info.pApplicationInfo = &app; info.pNext = &debug_ci; safe_info.initialize(&info); memset(&info, 0x11, sizeof(info)); memset(&app, 0x22, sizeof(app)); memset(&debug_ci, 0x33, sizeof(debug_ci)); } ASSERT_EQ(VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, safe_info.sType); ASSERT_EQ(0, strcmp("test", safe_info.pApplicationInfo->pApplicationName)); ASSERT_EQ(42u, safe_info.pApplicationInfo->applicationVersion); auto debug_ci = vku::FindStructInPNextChain(safe_info.pNext); ASSERT_NE(nullptr, debug_ci); ASSERT_EQ(static_cast(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT), debug_ci->messageSeverity); } TEST(safe_struct, safe_void_pointer_copies) { // vku::safe_VkSpecializationInfo, constructor { std::vector data(20, std::byte{0b11110000}); VkSpecializationInfo info = {}; info.dataSize = uint32_t(data.size()); info.pData = data.data(); vku::safe_VkSpecializationInfo safe(&info); ASSERT_TRUE(safe.pData != info.pData); ASSERT_TRUE(safe.dataSize == info.dataSize); data.clear(); // Invalidate any references, pointers, or iterators referring to contained elements. auto copied_bytes = reinterpret_cast(safe.pData); ASSERT_TRUE(copied_bytes[19] == std::byte{0b11110000}); } // vku::safe_VkPipelineExecutableInternalRepresentationKHR, initialize { std::vector data(11, std::byte{0b01001001}); VkPipelineExecutableInternalRepresentationKHR info = {}; info.dataSize = uint32_t(data.size()); info.pData = data.data(); vku::safe_VkPipelineExecutableInternalRepresentationKHR safe; safe.initialize(&info); ASSERT_TRUE(safe.dataSize == info.dataSize); ASSERT_TRUE(safe.pData != info.pData); data.clear(); // Invalidate any references, pointers, or iterators referring to contained elements. auto copied_bytes = reinterpret_cast(safe.pData); ASSERT_TRUE(copied_bytes[10] == std::byte{0b01001001}); } } TEST(safe_struct, custom_safe_pnext_copy) { // This tests an additional "copy_state" parameter in the SafePNextCopy function that allows "customizing" safe_* struct // construction.. This is required for structs such as VkPipelineRenderingCreateInfo (which extend VkGraphicsPipelineCreateInfo) // whose members must be partially ignored depending on the graphics sub-state present. VkFormat format = VK_FORMAT_B8G8R8A8_UNORM; VkPipelineRenderingCreateInfo pri = vku::InitStructHelper(); pri.colorAttachmentCount = 1; pri.pColorAttachmentFormats = &format; bool ignore_default_construction = true; vku::PNextCopyState copy_state = { [&ignore_default_construction](VkBaseOutStructure *safe_struct, [[maybe_unused]] const VkBaseOutStructure *in_struct) -> bool { if (ignore_default_construction) { auto tmp = reinterpret_cast(safe_struct); tmp->colorAttachmentCount = 0; tmp->pColorAttachmentFormats = nullptr; return true; } return false; }, }; { VkGraphicsPipelineCreateInfo gpci = vku::InitStructHelper(&pri); vku::safe_VkGraphicsPipelineCreateInfo safe_gpci(&gpci, false, false, ©_state); auto safe_pri = reinterpret_cast(safe_gpci.pNext); // Ensure original input struct was not modified ASSERT_EQ(pri.colorAttachmentCount, 1u); ASSERT_EQ(pri.pColorAttachmentFormats, &format); // Ensure safe struct was modified ASSERT_EQ(safe_pri->colorAttachmentCount, 0u); ASSERT_EQ(safe_pri->pColorAttachmentFormats, nullptr); } // Ensure PNextCopyState::init is also applied when there is more than one element in the pNext chain { VkGraphicsPipelineLibraryCreateInfoEXT gpl_info = vku::InitStructHelper(&pri); VkGraphicsPipelineCreateInfo gpci = vku::InitStructHelper(&gpl_info); vku::safe_VkGraphicsPipelineCreateInfo safe_gpci(&gpci, false, false, ©_state); auto safe_gpl_info = reinterpret_cast(safe_gpci.pNext); auto safe_pri = reinterpret_cast(safe_gpl_info->pNext); // Ensure original input struct was not modified ASSERT_EQ(pri.colorAttachmentCount, 1u); ASSERT_EQ(pri.pColorAttachmentFormats, &format); // Ensure safe struct was modified ASSERT_EQ(safe_pri->colorAttachmentCount, 0u); ASSERT_EQ(safe_pri->pColorAttachmentFormats, nullptr); } // Check that signaling to use the default constructor works { pri.colorAttachmentCount = 1; pri.pColorAttachmentFormats = &format; ignore_default_construction = false; VkGraphicsPipelineCreateInfo gpci = vku::InitStructHelper(&pri); vku::safe_VkGraphicsPipelineCreateInfo safe_gpci(&gpci, false, false, ©_state); auto safe_pri = reinterpret_cast(safe_gpci.pNext); // Ensure original input struct was not modified ASSERT_EQ(pri.colorAttachmentCount, 1u); ASSERT_EQ(pri.pColorAttachmentFormats, &format); // Ensure safe struct was modified ASSERT_EQ(safe_pri->colorAttachmentCount, 1u); ASSERT_EQ(*safe_pri->pColorAttachmentFormats, format); } } TEST(safe_struct, extension_add_remove) { std::array extensions{ "VK_KHR_maintenance1", "VK_KHR_maintenance2", "VK_KHR_maintenance3", "VK_KHR_maintenance4", "VK_KHR_maintenance5", "VK_KHR_maintenance6", }; VkDeviceCreateInfo ci = vku::InitStructHelper(); ci.ppEnabledExtensionNames = extensions.data(); ci.enabledExtensionCount = static_cast(extensions.size()); vku::safe_VkDeviceCreateInfo safe_ci(&ci); ASSERT_EQ(3u, vku::FindExtension(safe_ci, "VK_KHR_maintenance4")); ASSERT_EQ(safe_ci.enabledExtensionCount, vku::FindExtension(safe_ci, "VK_KHR_maintenance0")); ASSERT_EQ(false, vku::RemoveExtension(safe_ci, "VK_KHR_maintenance0")); ASSERT_EQ(true, vku::AddExtension(safe_ci, "VK_KHR_maintenance0")); ASSERT_EQ(true, vku::RemoveExtension(safe_ci, "VK_KHR_maintenance0")); for (const auto &ext : extensions) { ASSERT_EQ(true, vku::RemoveExtension(safe_ci, ext)); } ASSERT_EQ(false, vku::RemoveExtension(safe_ci, "VK_KHR_maintenance0")); ASSERT_EQ(0u, safe_ci.enabledExtensionCount); ASSERT_EQ(nullptr, safe_ci.ppEnabledExtensionNames); for (const auto &ext : extensions) { ASSERT_EQ(true, vku::AddExtension(safe_ci, ext)); } ASSERT_EQ(extensions.size(), safe_ci.enabledExtensionCount); } TEST(safe_struct, pnext_add_remove) { VkPhysicalDeviceRayTracingPipelineFeaturesKHR rtp = vku::InitStructHelper(); VkPhysicalDeviceRayQueryFeaturesKHR rtq = vku::InitStructHelper(&rtp); VkPhysicalDeviceMeshShaderFeaturesEXT mesh = vku::InitStructHelper(&rtq); VkPhysicalDeviceFeatures2 features = vku::InitStructHelper(&mesh); vku::safe_VkPhysicalDeviceFeatures2 sf(&features); // unlink the structs so they can be added one at a time. rtp.pNext = nullptr; rtq.pNext = nullptr; mesh.pNext = nullptr; ASSERT_EQ(true, vku::RemoveFromPnext(sf, rtq.sType)); ASSERT_EQ(nullptr, vku::FindStructInPNextChain(sf.pNext)); ASSERT_EQ(false, vku::RemoveFromPnext(sf, rtq.sType)); ASSERT_EQ(true, vku::RemoveFromPnext(sf, rtp.sType)); ASSERT_EQ(nullptr, vku::FindStructInPNextChain(sf.pNext)); ASSERT_EQ(true, vku::RemoveFromPnext(sf, mesh.sType)); ASSERT_EQ(nullptr, vku::FindStructInPNextChain(sf.pNext)); ASSERT_EQ(nullptr, sf.pNext); ASSERT_EQ(true, vku::AddToPnext(sf, mesh)); ASSERT_EQ(false, vku::AddToPnext(sf, mesh)); ASSERT_NE(nullptr, vku::FindStructInPNextChain(sf.pNext)); ASSERT_EQ(true, vku::AddToPnext(sf, rtq)); ASSERT_EQ(false, vku::AddToPnext(sf, rtq)); ASSERT_NE(nullptr, vku::FindStructInPNextChain(sf.pNext)); ASSERT_EQ(true, vku::AddToPnext(sf, rtp)); ASSERT_EQ(false, vku::AddToPnext(sf, rtp)); ASSERT_NE(nullptr, vku::FindStructInPNextChain(sf.pNext)); ASSERT_EQ(true, vku::RemoveFromPnext(sf, mesh.sType)); ASSERT_EQ(true, vku::RemoveFromPnext(sf, rtp.sType)); ASSERT_EQ(true, vku::RemoveFromPnext(sf, rtq.sType)); // relink the structs so they can be added all at once rtq.pNext = &rtp; mesh.pNext = &rtq; ASSERT_EQ(true, vku::AddToPnext(sf, mesh)); ASSERT_NE(nullptr, vku::FindStructInPNextChain(sf.pNext)); ASSERT_NE(nullptr, vku::FindStructInPNextChain(sf.pNext)); ASSERT_NE(nullptr, vku::FindStructInPNextChain(sf.pNext)); }