1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
|
// Copyright 2023 The Khronos Group Inc.
// Copyright 2023 Valve Corporation
// Copyright 2023 LunarG, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
#include <vulkan/utility/vk_safe_struct.hpp>
#include <vulkan/utility/vk_struct_helper.hpp>
#include <gtest/gtest.h>
#include <array>
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<VkDebugUtilsMessengerCreateInfoEXT>(safe_info.pNext);
ASSERT_NE(nullptr, debug_ci);
ASSERT_EQ(static_cast<VkDebugUtilsMessageSeverityFlagsEXT>(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT),
debug_ci->messageSeverity);
}
TEST(safe_struct, safe_void_pointer_copies) {
// vku::safe_VkSpecializationInfo, constructor
{
std::vector<std::byte> 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<const std::byte *>(safe.pData);
ASSERT_TRUE(copied_bytes[19] == std::byte{0b11110000});
}
// vku::safe_VkPipelineExecutableInternalRepresentationKHR, initialize
{
std::vector<std::byte> 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<const std::byte *>(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<vku::safe_VkPipelineRenderingCreateInfo *>(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<const vku::safe_VkPipelineRenderingCreateInfo *>(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<const vku::safe_VkGraphicsPipelineLibraryCreateInfoEXT *>(safe_gpci.pNext);
auto safe_pri = reinterpret_cast<const vku::safe_VkPipelineRenderingCreateInfo *>(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<const vku::safe_VkPipelineRenderingCreateInfo *>(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<const char *, 6> 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<uint32_t>(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<VkPhysicalDeviceRayQueryFeaturesKHR>(sf.pNext));
ASSERT_EQ(false, vku::RemoveFromPnext(sf, rtq.sType));
ASSERT_EQ(true, vku::RemoveFromPnext(sf, rtp.sType));
ASSERT_EQ(nullptr, vku::FindStructInPNextChain<VkPhysicalDeviceRayTracingPipelineFeaturesKHR>(sf.pNext));
ASSERT_EQ(true, vku::RemoveFromPnext(sf, mesh.sType));
ASSERT_EQ(nullptr, vku::FindStructInPNextChain<VkPhysicalDeviceMeshShaderFeaturesEXT>(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<VkPhysicalDeviceMeshShaderFeaturesEXT>(sf.pNext));
ASSERT_EQ(true, vku::AddToPnext(sf, rtq));
ASSERT_EQ(false, vku::AddToPnext(sf, rtq));
ASSERT_NE(nullptr, vku::FindStructInPNextChain<VkPhysicalDeviceRayQueryFeaturesKHR>(sf.pNext));
ASSERT_EQ(true, vku::AddToPnext(sf, rtp));
ASSERT_EQ(false, vku::AddToPnext(sf, rtp));
ASSERT_NE(nullptr, vku::FindStructInPNextChain<VkPhysicalDeviceRayTracingPipelineFeaturesKHR>(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<VkPhysicalDeviceRayTracingPipelineFeaturesKHR>(sf.pNext));
ASSERT_NE(nullptr, vku::FindStructInPNextChain<VkPhysicalDeviceMeshShaderFeaturesEXT>(sf.pNext));
ASSERT_NE(nullptr, vku::FindStructInPNextChain<VkPhysicalDeviceRayQueryFeaturesKHR>(sf.pNext));
}
|