1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2021 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 * \file
21 * \brief Vulkan SC VkCommandPoolMemoryReservationCreateInfo tests
22 *//*--------------------------------------------------------------------*/
23
24 #include "vktCommandPoolMemoryReservationTests.hpp"
25
26 #include <set>
27 #include <vector>
28 #include <string>
29
30 #include "vkRefUtil.hpp"
31 #include "vktTestCaseUtil.hpp"
32 #include "vkSafetyCriticalUtil.hpp"
33 #include "tcuTestLog.hpp"
34
35 namespace vkt
36 {
37 namespace sc
38 {
39
40 using namespace vk;
41
42 namespace
43 {
44
45 typedef de::SharedPtr<vk::Unique<vk::VkEvent>> VkEventSp;
46
47 enum CommandPoolReservedSize
48 {
49 CPS_UNUSED = 0,
50 CPS_SMALL,
51 CPS_BIG
52 };
53
54 struct TestParams
55 {
56 CommandPoolReservedSize commandPoolReservedSize;
57 uint32_t commandBufferCount;
58 uint32_t iterations;
59 bool multipleRecording;
60 };
61
beginCommandBuffer(const DeviceInterface & vk,const VkCommandBuffer commandBuffer,VkCommandBufferUsageFlags flags)62 void beginCommandBuffer(const DeviceInterface &vk, const VkCommandBuffer commandBuffer, VkCommandBufferUsageFlags flags)
63 {
64 const VkCommandBufferBeginInfo commandBufBeginParams = {
65 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType;
66 nullptr, // const void* pNext;
67 flags, // VkCommandBufferUsageFlags flags;
68 nullptr,
69 };
70 VK_CHECK(vk.beginCommandBuffer(commandBuffer, &commandBufBeginParams));
71 }
72
endCommandBuffer(const DeviceInterface & vk,const VkCommandBuffer commandBuffer)73 void endCommandBuffer(const DeviceInterface &vk, const VkCommandBuffer commandBuffer)
74 {
75 VK_CHECK(vk.endCommandBuffer(commandBuffer));
76 }
77
78 // verify that VkCommandPoolMemoryReservationCreateInfo::commandPoolReservedSize == VkCommandPoolMemoryConsumption::commandPoolReservedSize
verifyCommandPoolReservedSize(Context & context,TestParams testParams)79 tcu::TestStatus verifyCommandPoolReservedSize(Context &context, TestParams testParams)
80 {
81 const VkDevice device = context.getDevice();
82 const DeviceInterface &vk = context.getDeviceInterface();
83 const uint32_t queueFamilyIndex = context.getUniversalQueueFamilyIndex();
84
85 if (testParams.commandBufferCount > context.getDeviceVulkanSC10Properties().maxCommandPoolCommandBuffers)
86 TCU_THROW(NotSupportedError, "commandBufferCount is greater than maxCommandPoolCommandBuffers");
87
88 VkDeviceSize commandPoolReservedSize = 0u;
89 switch (testParams.commandPoolReservedSize)
90 {
91 case CPS_SMALL:
92 commandPoolReservedSize =
93 de::max(VkDeviceSize(64u * context.getTestContext().getCommandLine().getCommandDefaultSize()),
94 VkDeviceSize(context.getTestContext().getCommandLine().getCommandPoolMinSize()));
95 break;
96 case CPS_BIG:
97 commandPoolReservedSize =
98 de::max(VkDeviceSize(8192u * context.getTestContext().getCommandLine().getCommandDefaultSize()),
99 VkDeviceSize(context.getTestContext().getCommandLine().getCommandPoolMinSize()));
100 break;
101 default:
102 TCU_THROW(InternalError, "Unsupported commandPoolReservedSize value");
103 }
104 commandPoolReservedSize = de::max(
105 commandPoolReservedSize, VkDeviceSize(testParams.commandBufferCount *
106 context.getTestContext().getCommandLine().getCommandBufferMinSize()));
107
108 // Create command pool with declared size
109 // By connecting our own VkCommandPoolMemoryReservationCreateInfo we avoid getting unknown data from DeviceDriverSC::createCommandPoolHandlerNorm()
110 VkCommandPoolMemoryReservationCreateInfo cpMemReservationCI = {
111 VK_STRUCTURE_TYPE_COMMAND_POOL_MEMORY_RESERVATION_CREATE_INFO, // VkStructureType sType
112 nullptr, // const void* pNext
113 commandPoolReservedSize, // VkDeviceSize commandPoolReservedSize
114 testParams.commandBufferCount // uint32_t commandPoolMaxCommandBuffers
115 };
116
117 const VkCommandPoolCreateInfo cmdPoolParams = {
118 VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // VkStructureType sType;
119 (const void *)&cpMemReservationCI, // const void* pNext;
120 0u, // VkCommandPoolCreateFlags flags;
121 queueFamilyIndex, // uint32_t queueFamilyIndex;
122 };
123 const Unique<VkCommandPool> cmdPool(createCommandPool(vk, device, &cmdPoolParams));
124
125 // check if size collected by vkGetCommandPoolMemoryConsumption matches size from VkCommandPoolMemoryReservationCreateInfo
126
127 VkCommandPoolMemoryConsumption memConsumption = {
128 VK_STRUCTURE_TYPE_COMMAND_POOL_MEMORY_CONSUMPTION, // VkStructureType sType
129 nullptr, // void* pNext
130 0, // VkDeviceSize commandPoolAllocated
131 0, // VkDeviceSize commandPoolReservedSize
132 0, // VkDeviceSize commandBufferAllocated
133 };
134
135 vk.getCommandPoolMemoryConsumption(device, *cmdPool, nullptr, &memConsumption);
136
137 if (commandPoolReservedSize != memConsumption.commandPoolReservedSize)
138 return tcu::TestStatus::fail("Failed");
139 return tcu::TestStatus::pass("Pass");
140 }
141
142 // verify that VkCommandPoolMemoryReservationCreateInfo::commandPoolAllocated == sum of VkCommandPoolMemoryConsumption::commandBufferAllocated
verifyCommandPoolAllocEqualsCommandBufferAlloc(Context & context,TestParams testParams)143 tcu::TestStatus verifyCommandPoolAllocEqualsCommandBufferAlloc(Context &context, TestParams testParams)
144 {
145 const VkDevice device = context.getDevice();
146 const DeviceInterface &vk = context.getDeviceInterface();
147 const uint32_t queueFamilyIndex = context.getUniversalQueueFamilyIndex();
148
149 // fill command buffers
150 uint32_t eventCount = 0u;
151 switch (testParams.commandPoolReservedSize)
152 {
153 case CPS_SMALL:
154 eventCount = 1u;
155 break;
156 case CPS_BIG:
157 eventCount = 32u;
158 break;
159 default:
160 TCU_THROW(InternalError, "Unsupported commandPoolReservedSize value");
161 }
162 VkDeviceSize commandPoolReservedSize =
163 de::max(VkDeviceSize(eventCount * context.getTestContext().getCommandLine().getCommandDefaultSize()),
164 VkDeviceSize(context.getTestContext().getCommandLine().getCommandPoolMinSize()));
165 commandPoolReservedSize = de::max(
166 commandPoolReservedSize, VkDeviceSize(testParams.commandBufferCount *
167 context.getTestContext().getCommandLine().getCommandBufferMinSize()));
168
169 // Create command pool with declared size
170 // By connecting our own VkCommandPoolMemoryReservationCreateInfo we avoid getting unknown data from DeviceDriverSC::createCommandPoolHandlerNorm()
171 VkCommandPoolMemoryReservationCreateInfo cpMemReservationCI = {
172 VK_STRUCTURE_TYPE_COMMAND_POOL_MEMORY_RESERVATION_CREATE_INFO, // VkStructureType sType
173 nullptr, // const void* pNext
174 commandPoolReservedSize, // VkDeviceSize commandPoolReservedSize
175 testParams.commandBufferCount // uint32_t commandPoolMaxCommandBuffers
176 };
177
178 const VkCommandPoolCreateInfo cmdPoolParams = {
179 VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // VkStructureType sType;
180 (const void *)&cpMemReservationCI, // const void* pNext;
181 0u, // VkCommandPoolCreateFlags flags;
182 queueFamilyIndex, // uint32_t queueFamilyIndex;
183 };
184 const Unique<VkCommandPool> cmdPool(createCommandPool(vk, device, &cmdPoolParams));
185
186 // Allocate command buffers
187 std::vector<Move<VkCommandBuffer>> commandBuffers(testParams.commandBufferCount);
188 const VkCommandBufferAllocateInfo cmdBufferAllocateInfo = {
189 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // VkStructureType sType;
190 nullptr, // const void* pNext;
191 *cmdPool, // VkCommandPool commandPool;
192 VK_COMMAND_BUFFER_LEVEL_PRIMARY, // VkCommandBufferLevel level;
193 testParams.commandBufferCount // uint32_t commandBufferCount;
194 };
195 allocateCommandBuffers(vk, device, &cmdBufferAllocateInfo, commandBuffers.data());
196
197 std::vector<VkEventSp> events;
198 for (uint32_t ndx = 0; ndx < eventCount; ++ndx)
199 events.push_back(VkEventSp(new vk::Unique<VkEvent>(createEvent(vk, device))));
200
201 bool isOK = true;
202 for (uint32_t iter = 0; iter < 2 * testParams.iterations; ++iter)
203 {
204 // Build command buffers on even iteration
205 if (0 == iter % 2)
206 {
207 if (testParams.multipleRecording)
208 {
209 for (uint32_t i = 0; i < testParams.commandBufferCount; ++i)
210 beginCommandBuffer(vk, commandBuffers[i].get(), 0u);
211
212 for (uint32_t i = 0; i < testParams.commandBufferCount; ++i)
213 for (uint32_t j = 0; j < eventCount; ++j)
214 vk.cmdSetEvent(commandBuffers[i].get(), events[j]->get(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
215
216 for (uint32_t i = 0; i < testParams.commandBufferCount; ++i)
217 endCommandBuffer(vk, commandBuffers[i].get());
218 }
219 else
220 {
221 for (uint32_t i = 0; i < testParams.commandBufferCount; ++i)
222 {
223 beginCommandBuffer(vk, commandBuffers[i].get(), 0u);
224
225 for (uint32_t j = 0; j < eventCount; ++j)
226 vk.cmdSetEvent(commandBuffers[i].get(), events[j]->get(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
227
228 endCommandBuffer(vk, commandBuffers[i].get());
229 }
230 }
231 }
232 else // Reset command buffers on odd iteration
233 {
234 // leave loop when implementation is not able to perform vkResetCommandPool()
235 if (context.getDeviceVulkanSC10Properties().commandPoolResetCommandBuffer == VK_FALSE)
236 break;
237 vk.resetCommandPool(device, *cmdPool, VkCommandPoolResetFlags(0u));
238 }
239
240 // check if size collected by sum of command buffer allocs is equal to command pool alloc
241 VkDeviceSize cbAllocSum = 0u;
242 VkDeviceSize commandPoolAlloc = 0u;
243 for (uint32_t i = 0; i < testParams.commandBufferCount; ++i)
244 {
245 VkCommandPoolMemoryConsumption memConsumption = {
246 VK_STRUCTURE_TYPE_COMMAND_POOL_MEMORY_CONSUMPTION, // VkStructureType sType
247 nullptr, // void* pNext
248 0, // VkDeviceSize commandPoolAllocated
249 0, // VkDeviceSize commandPoolReservedSize
250 0, // VkDeviceSize commandBufferAllocated
251 };
252 vk.getCommandPoolMemoryConsumption(device, *cmdPool, commandBuffers[i].get(), &memConsumption);
253 cbAllocSum += memConsumption.commandBufferAllocated;
254 commandPoolAlloc = memConsumption.commandPoolAllocated;
255 }
256 if (cbAllocSum != commandPoolAlloc)
257 isOK = false;
258 // if we just performed a vkResetCommandPool() then allocated commandPool memory should be equal to 0
259 if ((1 == iter % 2) && commandPoolAlloc != 0u)
260 isOK = false;
261 }
262 return isOK ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Failed");
263 }
264
checkSupport(Context & context,TestParams testParams)265 void checkSupport(Context &context, TestParams testParams)
266 {
267 if (testParams.iterations > 1 && context.getDeviceVulkanSC10Properties().commandPoolResetCommandBuffer == VK_FALSE)
268 TCU_THROW(NotSupportedError, "commandPoolResetCommandBuffer is not supported");
269 if (testParams.multipleRecording &&
270 context.getDeviceVulkanSC10Properties().commandPoolMultipleCommandBuffersRecording == VK_FALSE)
271 TCU_THROW(NotSupportedError, "commandPoolMultipleCommandBuffersRecording is not supported");
272 if (testParams.commandBufferCount > context.getDeviceVulkanSC10Properties().maxCommandPoolCommandBuffers)
273 TCU_THROW(NotSupportedError, "commandBufferCount is greater than maxCommandPoolCommandBuffers");
274 }
275
276 } // namespace
277
createCommandPoolMemoryReservationTests(tcu::TestContext & testCtx)278 tcu::TestCaseGroup *createCommandPoolMemoryReservationTests(tcu::TestContext &testCtx)
279 {
280 // Tests verifying memory reservation for command pools in Vulkan SC
281 de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "command_pool_memory_reservation"));
282
283 // add vkGetCommandPoolMemoryConsumption tests
284 const struct
285 {
286 uint32_t commandPoolMaxCommandBuffers;
287 const char *name;
288 } maxCommandBuffers[] = {
289 {1, "cb_single"}, {4, "cb_few"}, {21, "cb_many"}, {256, "cb_min_limit"}, {1024, "cb_above_min_limit"},
290 };
291
292 const struct
293 {
294 CommandPoolReservedSize commandPoolReservedSize;
295 const char *name;
296 } reservedSizes[] = {
297 {CPS_SMALL, "size_small"},
298 {CPS_BIG, "size_big"},
299 };
300
301 const struct
302 {
303 bool multipleRecording;
304 const char *name;
305 } recording[] = {
306 {false, "single_recording"},
307 {true, "multiple_recording"},
308 };
309
310 const struct
311 {
312 uint32_t count;
313 const char *name;
314 } iterations[] = {
315 {1u, "1"},
316 {2u, "2"},
317 {4u, "8"},
318 {8u, "16"},
319 };
320
321 {
322 // Testing vkGetCommandPoolMemoryConsumption
323 de::MovePtr<tcu::TestCaseGroup> memConGroup(new tcu::TestCaseGroup(testCtx, "memory_consumption"));
324
325 for (int cbIdx = 0; cbIdx < DE_LENGTH_OF_ARRAY(maxCommandBuffers); ++cbIdx)
326 {
327 de::MovePtr<tcu::TestCaseGroup> cbGroup(new tcu::TestCaseGroup(testCtx, maxCommandBuffers[cbIdx].name));
328
329 for (int sizeIdx = 0; sizeIdx < DE_LENGTH_OF_ARRAY(reservedSizes); ++sizeIdx)
330 {
331 de::MovePtr<tcu::TestCaseGroup> sizeGroup(new tcu::TestCaseGroup(testCtx, reservedSizes[sizeIdx].name));
332
333 for (int simIdx = 0; simIdx < DE_LENGTH_OF_ARRAY(recording); ++simIdx)
334 {
335 de::MovePtr<tcu::TestCaseGroup> simGroup(new tcu::TestCaseGroup(testCtx, recording[simIdx].name));
336
337 if (!recording[simIdx].multipleRecording)
338 {
339 TestParams testParams = {reservedSizes[sizeIdx].commandPoolReservedSize,
340 maxCommandBuffers[cbIdx].commandPoolMaxCommandBuffers, 1u, false};
341 addFunctionCase(simGroup.get(), "reserved_size", verifyCommandPoolReservedSize, testParams);
342 }
343
344 for (int iterIdx = 0; iterIdx < DE_LENGTH_OF_ARRAY(iterations); ++iterIdx)
345 {
346 TestParams testParams = {reservedSizes[sizeIdx].commandPoolReservedSize,
347 maxCommandBuffers[cbIdx].commandPoolMaxCommandBuffers,
348 iterations[iterIdx].count, recording[simIdx].multipleRecording};
349 std::ostringstream testName;
350 testName << "allocated_size_" << iterations[iterIdx].name;
351 addFunctionCase(simGroup.get(), testName.str(), checkSupport,
352 verifyCommandPoolAllocEqualsCommandBufferAlloc, testParams);
353 }
354
355 sizeGroup->addChild(simGroup.release());
356 }
357 cbGroup->addChild(sizeGroup.release());
358 }
359 memConGroup->addChild(cbGroup.release());
360 }
361 group->addChild(memConGroup.release());
362 }
363
364 return group.release();
365 }
366
367 } // namespace sc
368
369 } // namespace vkt
370