1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2023 The Khronos Group Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \brief Extension duplicates tests
21 *//*--------------------------------------------------------------------*/
22
23 #include "vktTestCase.hpp"
24 #include "vktTestCaseUtil.hpp"
25 #include "vktTestGroupUtil.hpp"
26 #include "vktCustomInstancesDevices.hpp"
27 #include "vkPlatform.hpp"
28 #include "vkCmdUtil.hpp"
29 #include "tcuCommandLine.hpp"
30 #include "vkObjUtil.hpp"
31 #include "vkMemUtil.hpp"
32 #include "vkQueryUtil.hpp"
33 #include "vkRefUtil.hpp"
34 #include "vktApiExtensionDuplicatesTests.hpp"
35 #include "vkSafetyCriticalUtil.hpp"
36 #include "vkDeviceUtil.hpp"
37
38 #include <set>
39 #include <sstream>
40 #include <string>
41 #include <type_traits>
42 #include <vector>
43 #include <iostream>
44
45 namespace vkt
46 {
47 namespace api
48 {
49 namespace
50 {
51 using namespace vk;
52
53 namespace ut
54 {
distinct(const std::vector<const char * > & src)55 std::set<const char *> distinct(const std::vector<const char *> &src)
56 {
57 std::set<const char *> result;
58 for (const char *const p : src)
59 {
60 result.insert(p);
61 }
62 return result;
63 }
64
65 struct StringDuplicator
66 {
StringDuplicatorvkt::api::__anonebe1f38b0111::ut::StringDuplicator67 StringDuplicator(const std::vector<const char *> &src) : m_source(distinct(src)), m_strings()
68 {
69 }
70 auto duplicatePointers() -> std::vector<const char *>;
71 // NOTE: Use carefully, realtime counterparts for returned pointers are held in m_strings
72 auto duplicateStrings() -> std::vector<const char *>;
getInputCountvkt::api::__anonebe1f38b0111::ut::StringDuplicator73 auto getInputCount() const -> typename std::vector<const char *>::size_type
74 {
75 return m_source.size();
76 }
77
78 private:
79 const std::set<const char *> m_source;
80 std::vector<std::string> m_strings;
81 };
82
duplicatePointers()83 std::vector<const char *> StringDuplicator::duplicatePointers()
84 {
85 uint32_t i{};
86 std::vector<const char *> r{};
87
88 for (const char *const p : m_source)
89 {
90 if ((i % 2) == 0)
91 {
92 r.push_back(p);
93 r.push_back(p);
94 }
95 else if ((i % 3) == 0)
96 {
97 r.push_back(p);
98 r.push_back(p);
99 r.push_back(p);
100 }
101 else
102 {
103 r.push_back(p);
104 r.push_back(p);
105 r.push_back(p);
106 r.push_back(p);
107 }
108
109 i = i + 1u;
110 }
111
112 return r;
113 }
duplicateStrings()114 std::vector<const char *> StringDuplicator::duplicateStrings()
115 {
116 uint32_t i{};
117 std::vector<const char *> r{};
118
119 m_strings.clear();
120
121 for (const char *p : m_source)
122 {
123 if ((i % 2) == 0)
124 {
125 m_strings.push_back(std::string(p));
126 m_strings.push_back(std::string(p));
127 }
128 else if ((i % 3) == 0)
129 {
130 m_strings.push_back(std::string(p));
131 m_strings.push_back(std::string(p));
132 m_strings.push_back(std::string(p));
133 }
134 else
135 {
136 m_strings.push_back(std::string(p));
137 m_strings.push_back(std::string(p));
138 m_strings.push_back(std::string(p));
139 m_strings.push_back(std::string(p));
140 }
141
142 i = i + 1u;
143 }
144
145 for (std::string &s : m_strings)
146 {
147 r.push_back(s.c_str());
148 }
149
150 return r;
151 }
152 } // namespace ut
153
154 class InstanceExtensionDuplicatesInstance : public TestInstance
155 {
156 public:
InstanceExtensionDuplicatesInstance(Context & ctx,bool byPointersOrNames)157 InstanceExtensionDuplicatesInstance(Context &ctx, bool byPointersOrNames)
158 : TestInstance(ctx)
159 , m_byPointersOrNames(byPointersOrNames)
160 {
161 }
162 virtual tcu::TestStatus iterate(void) override;
163
164 private:
165 const bool m_byPointersOrNames;
166 };
167
168 class DeviceExtensionDuplicatesInstance : public TestInstance
169 {
170 public:
DeviceExtensionDuplicatesInstance(Context & ctx,bool byPointersOrNames)171 DeviceExtensionDuplicatesInstance(Context &ctx, bool byPointersOrNames)
172 : TestInstance(ctx)
173 , m_byPointersOrNames(byPointersOrNames)
174 {
175 }
176 virtual tcu::TestStatus iterate(void) override;
177
178 private:
179 const bool m_byPointersOrNames;
180 };
181
182 class ExtensionDuplicatesCase : public TestCase
183 {
184 public:
ExtensionDuplicatesCase(tcu::TestContext & testCtx,const std::string & name,bool instanceOrDevice,bool byPointersOrNames)185 ExtensionDuplicatesCase(tcu::TestContext &testCtx, const std::string &name, bool instanceOrDevice,
186 bool byPointersOrNames)
187 : TestCase(testCtx, name)
188 , m_instanceOrDevice(instanceOrDevice)
189 , m_byPointersOrNames(byPointersOrNames)
190 {
191 }
createInstance(Context & ctx) const192 virtual TestInstance *createInstance(Context &ctx) const override
193 {
194 if (m_instanceOrDevice)
195 return new InstanceExtensionDuplicatesInstance(ctx, m_byPointersOrNames);
196 return new DeviceExtensionDuplicatesInstance(ctx, m_byPointersOrNames);
197 }
198
199 private:
200 const bool m_instanceOrDevice;
201 const bool m_byPointersOrNames;
202 };
203
iterate(void)204 tcu::TestStatus InstanceExtensionDuplicatesInstance::iterate(void)
205 {
206 const vk::PlatformInterface &vkp = m_context.getPlatformInterface();
207 const tcu::CommandLine &cmd = m_context.getTestContext().getCommandLine();
208 const std::vector<VkExtensionProperties> availableExtensions = enumerateInstanceExtensionProperties(vkp, nullptr);
209
210 if (0u == availableExtensions.size())
211 {
212 return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING,
213 "Unable to perform test due to empty instance extension list");
214 }
215
216 std::vector<std::string> availableExtensionNames(availableExtensions.size());
217 std::vector<const char *> enabledExtensions(availableExtensions.size());
218
219 std::transform(availableExtensions.begin(), availableExtensions.end(), availableExtensionNames.begin(),
220 [](const VkExtensionProperties &props) { return std::string(props.extensionName); });
221 std::transform(availableExtensionNames.begin(), availableExtensionNames.end(), enabledExtensions.begin(),
222 [](const std::string &ext) { return ext.c_str(); });
223
224 ut::StringDuplicator duplicator(enabledExtensions);
225 const std::vector<const char *> duplicatedExtensions =
226 m_byPointersOrNames ? duplicator.duplicatePointers() : duplicator.duplicateStrings();
227 const uint32_t duplicatedExtensionCnt = static_cast<uint32_t>(duplicatedExtensions.size());
228
229 const VkApplicationInfo applicationInfo{
230 VK_STRUCTURE_TYPE_APPLICATION_INFO, // VkStructureType sType;
231 nullptr, // const void* pNext;
232 "extension_duplicates_instance", // const char* pApplicationName;
233 VK_API_VERSION_1_0, // uint32_t applicationVersion;
234 "extension_duplicates_instance_engine", // const char* pEngineName;
235 VK_API_VERSION_1_0, // uint32_t engineVersion;
236 m_context.getUsedApiVersion() // uint32_t apiVersion;
237 };
238
239 const VkInstanceCreateInfo instanceCreateInfo{
240 VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // VkStructureType sType;
241 nullptr, // const void* pNext;
242 VkInstanceCreateFlags(0), // VkInstanceCreateFlags flags;
243 &applicationInfo, // const VkApplicationInfo* pApplicationInfo;
244 0u, // uint32_t enabledLayerCount;
245 nullptr, // const char* const* ppEnabledLayerNames;
246 duplicatedExtensionCnt, // uint32_t enabledExtensionCount;
247 duplicatedExtensions.data() // const char* const* ppEnabledExtensionNames;
248 };
249
250 UncheckedInstance uncheckedInstance;
251 const VkResult res =
252 createUncheckedInstance(m_context, &instanceCreateInfo, nullptr, &uncheckedInstance, cmd.isValidationEnabled());
253
254 auto failMessage = [&]() -> std::string
255 {
256 std::ostringstream os;
257 os << "vkCreateInstance returned " << getResultName(res);
258 os.flush();
259 return os.str();
260 };
261
262 auto passMessage = [&]() -> std::string
263 {
264 std::ostringstream os;
265 os << "Created " << duplicatedExtensionCnt << " duplicates of " << duplicator.getInputCount() << " extensions";
266 os.flush();
267 return os.str();
268 };
269
270 return (VK_SUCCESS == res) ? tcu::TestStatus::pass(passMessage()) : tcu::TestStatus::fail(failMessage());
271 }
272
iterate(void)273 tcu::TestStatus DeviceExtensionDuplicatesInstance::iterate(void)
274 {
275 #ifdef CTS_USES_VULKANSC
276 CustomInstance customInstance = createCustomInstanceFromContext(m_context);
277 const vk::InstanceInterface &vki = customInstance.getDriver();
278 const VkPhysicalDevice phd = chooseDevice(vki, customInstance, m_context.getTestContext().getCommandLine());
279 #else
280 const vk::InstanceInterface &vki = m_context.getInstanceInterface();
281 const VkPhysicalDevice phd = m_context.getPhysicalDevice();
282 #endif // CTS_USES_VULKANSC
283 const DeviceInterface &vkd = m_context.getDeviceInterface();
284 const uint32_t idx = m_context.getUniversalQueueFamilyIndex();
285 const tcu::CommandLine &cmd = m_context.getTestContext().getCommandLine();
286 const float qpr = 1.0f;
287 ut::StringDuplicator sd(m_context.getDeviceCreationExtensions());
288
289 const std::vector<const char *> dup = m_byPointersOrNames ? sd.duplicatePointers() : sd.duplicateStrings();
290 const uint32_t cnt = static_cast<uint32_t>(dup.size());
291 if (0u == cnt)
292 {
293 return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING,
294 "Unable to perform test due to empty device extension list");
295 }
296
297 const VkDeviceQueueCreateInfo queueCreateInfo{
298 VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // VkStructureType sType;
299 nullptr, // const void* pNext;
300 VkDeviceQueueCreateFlags(0), // VkDeviceQueueCreateFlags flags;
301 idx, // uint32_t queueFamilyIndex;
302 1u, // uint32_t queueCount;
303 &qpr // const float* pQueuePriorities;
304 };
305
306 void *pNext = DE_NULL;
307 #ifdef CTS_USES_VULKANSC
308 VkDeviceObjectReservationCreateInfo memReservationInfo =
309 m_context.getTestContext().getCommandLine().isSubProcess() ? m_context.getResourceInterface()->getStatMax() :
310 resetDeviceObjectReservationCreateInfo();
311 memReservationInfo.pNext = pNext;
312 pNext = &memReservationInfo;
313
314 VkPhysicalDeviceVulkanSC10Features sc10Features = createDefaultSC10Features();
315 sc10Features.pNext = pNext;
316 pNext = &sc10Features;
317 #endif // CTS_USES_VULKANSC
318
319 const VkDeviceCreateInfo deviceCreateInfo{
320 VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // VkStructureType sType;
321 pNext, // const void* pNext;
322 VkDeviceCreateFlags(0), // VkDeviceCreateFlags flags;
323 1u, // uint32_t queueCreateInfoCount;
324 &queueCreateInfo, // const VkDeviceQueueCreateInfo* pQueueCreateInfos;
325 0u, // uint32_t enabledLayerCount;
326 nullptr, // const char* const* ppEnabledLayerNames;
327 cnt, // uint32_t enabledExtensionCount;
328 dup.data(), // const char* const* ppEnabledExtensionNames;
329 nullptr // const VkPhysicalDeviceFeatures* pEnabledFeatures;
330
331 };
332
333 VkDevice device = VK_NULL_HANDLE;
334 const VkResult res =
335 createUncheckedDevice(cmd.isValidationEnabled(), vki, phd, &deviceCreateInfo, nullptr, &device);
336 if (VK_SUCCESS == res && VK_NULL_HANDLE != device)
337 {
338 vkd.destroyDevice(device, nullptr);
339 }
340
341 auto failMessage = [&]() -> std::string
342 {
343 std::ostringstream os;
344 os << "vkCreateDevice returned " << getResultName(res);
345 os.flush();
346 return os.str();
347 };
348
349 auto passMessage = [&]() -> std::string
350 {
351 std::ostringstream os;
352 os << "Created " << cnt << " duplicates of " << sd.getInputCount() << " extensions";
353 os.flush();
354 return os.str();
355 };
356
357 return (VK_SUCCESS == res) ? tcu::TestStatus::pass(passMessage()) : tcu::TestStatus::fail(failMessage());
358 }
359
360 } // unnamed namespace
361
createExtensionDuplicatesTests(tcu::TestContext & testCtx)362 tcu::TestCaseGroup *createExtensionDuplicatesTests(tcu::TestContext &testCtx)
363 {
364 typedef std::pair<const char *, bool> item_t;
365 item_t const types[]{{"instance", true}, {"device", false}};
366
367 item_t const methods[]{{"by_pointers", true}, {"by_names", false}};
368
369 // Verifies that we can create a device or an instance with duplicate extensions
370 de::MovePtr<tcu::TestCaseGroup> rootGroup(new tcu::TestCaseGroup(testCtx, "extension_duplicates"));
371 for (const item_t &type : types)
372 {
373 de::MovePtr<tcu::TestCaseGroup> typeGroup(new tcu::TestCaseGroup(testCtx, type.first));
374 for (const item_t &meth : methods)
375 {
376 typeGroup->addChild(new ExtensionDuplicatesCase(testCtx, meth.first, type.second, meth.second));
377 }
378 rootGroup->addChild(typeGroup.release());
379 }
380
381 return rootGroup.release();
382 }
383
384 } // namespace api
385 } // namespace vkt
386