1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2016 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 Synchronization primitive tests with single queue
22 *//*--------------------------------------------------------------------*/
23
24 #include "vktSynchronizationOperationSingleQueueTests.hpp"
25 #include "vkDefs.hpp"
26 #include "vktTestCase.hpp"
27 #include "vktTestCaseUtil.hpp"
28 #include "vktTestGroupUtil.hpp"
29 #include "vkRef.hpp"
30 #include "vkRefUtil.hpp"
31 #include "vkMemUtil.hpp"
32 #include "vkBarrierUtil.hpp"
33 #include "vkQueryUtil.hpp"
34 #include "vkCmdUtil.hpp"
35 #include "vkTypeUtil.hpp"
36 #include "vkCmdUtil.hpp"
37 #include "deUniquePtr.hpp"
38 #include "tcuTestLog.hpp"
39 #include "vktSynchronizationUtil.hpp"
40 #include "vktSynchronizationOperation.hpp"
41 #include "vktSynchronizationOperationTestData.hpp"
42 #include "vktSynchronizationOperationResources.hpp"
43
44 namespace vkt
45 {
46 namespace synchronization
47 {
48 namespace
49 {
50 using namespace vk;
51
52 class BaseTestInstance : public TestInstance
53 {
54 public:
BaseTestInstance(Context & context,const ResourceDescription & resourceDesc,const OperationSupport & writeOp,const OperationSupport & readOp,PipelineCacheData & pipelineCacheData)55 BaseTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
56 : TestInstance (context)
57 , m_opContext (context, pipelineCacheData)
58 , m_resource (new Resource(m_opContext, resourceDesc, writeOp.getResourceUsageFlags() | readOp.getResourceUsageFlags()))
59 , m_writeOp (writeOp.build(m_opContext, *m_resource))
60 , m_readOp (readOp.build(m_opContext, *m_resource))
61 {
62 }
63
64 protected:
65 OperationContext m_opContext;
66 const de::UniquePtr<Resource> m_resource;
67 const de::UniquePtr<Operation> m_writeOp;
68 const de::UniquePtr<Operation> m_readOp;
69 };
70
71 class EventTestInstance : public BaseTestInstance
72 {
73 public:
EventTestInstance(Context & context,const ResourceDescription & resourceDesc,const OperationSupport & writeOp,const OperationSupport & readOp,PipelineCacheData & pipelineCacheData)74 EventTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
75 : BaseTestInstance (context, resourceDesc, writeOp, readOp, pipelineCacheData)
76 {
77 }
78
iterate(void)79 tcu::TestStatus iterate (void)
80 {
81 const DeviceInterface& vk = m_context.getDeviceInterface();
82 const VkDevice device = m_context.getDevice();
83 const VkQueue queue = m_context.getUniversalQueue();
84 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
85 const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
86 const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer(vk, device, *cmdPool));
87 const Unique<VkEvent> event (createEvent(vk, device));
88 const SyncInfo writeSync = m_writeOp->getSyncInfo();
89 const SyncInfo readSync = m_readOp->getSyncInfo();
90
91 beginCommandBuffer(vk, *cmdBuffer);
92
93 m_writeOp->recordCommands(*cmdBuffer);
94 vk.cmdSetEvent(*cmdBuffer, *event, writeSync.stageMask);
95
96 if (m_resource->getType() == RESOURCE_TYPE_BUFFER || isIndirectBuffer(m_resource->getType()))
97 {
98 const VkBufferMemoryBarrier barrier = makeBufferMemoryBarrier(writeSync.accessMask, readSync.accessMask,
99 m_resource->getBuffer().handle, m_resource->getBuffer().offset, m_resource->getBuffer().size);
100 vk.cmdWaitEvents(*cmdBuffer, 1u, &event.get(), writeSync.stageMask, readSync.stageMask, 0u, DE_NULL, 1u, &barrier, 0u, DE_NULL);
101 }
102 else if (m_resource->getType() == RESOURCE_TYPE_IMAGE)
103 {
104 const VkImageMemoryBarrier barrier = makeImageMemoryBarrier(writeSync.accessMask, readSync.accessMask,
105 writeSync.imageLayout, readSync.imageLayout, m_resource->getImage().handle, m_resource->getImage().subresourceRange);
106 vk.cmdWaitEvents(*cmdBuffer, 1u, &event.get(), writeSync.stageMask, readSync.stageMask, 0u, DE_NULL, 0u, DE_NULL, 1u, &barrier);
107 }
108
109 m_readOp->recordCommands(*cmdBuffer);
110
111 endCommandBuffer(vk, *cmdBuffer);
112 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
113
114 {
115 const Data expected = m_writeOp->getData();
116 const Data actual = m_readOp->getData();
117
118 if (0 != deMemCmp(expected.data, actual.data, expected.size))
119 return tcu::TestStatus::fail("Memory contents don't match");
120 }
121
122 return tcu::TestStatus::pass("OK");
123 }
124 };
125
126 class BarrierTestInstance : public BaseTestInstance
127 {
128 public:
BarrierTestInstance(Context & context,const ResourceDescription & resourceDesc,const OperationSupport & writeOp,const OperationSupport & readOp,PipelineCacheData & pipelineCacheData)129 BarrierTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
130 : BaseTestInstance (context, resourceDesc, writeOp, readOp, pipelineCacheData)
131 {
132 }
133
iterate(void)134 tcu::TestStatus iterate (void)
135 {
136 const DeviceInterface& vk = m_context.getDeviceInterface();
137 const VkDevice device = m_context.getDevice();
138 const VkQueue queue = m_context.getUniversalQueue();
139 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
140 const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
141 const Move<VkCommandBuffer> cmdBuffer (makeCommandBuffer(vk, device, *cmdPool));
142 const SyncInfo writeSync = m_writeOp->getSyncInfo();
143 const SyncInfo readSync = m_readOp->getSyncInfo();
144
145 beginCommandBuffer(vk, *cmdBuffer);
146
147 m_writeOp->recordCommands(*cmdBuffer);
148
149 if (m_resource->getType() == RESOURCE_TYPE_BUFFER || isIndirectBuffer(m_resource->getType()))
150 {
151 const VkBufferMemoryBarrier barrier = makeBufferMemoryBarrier(writeSync.accessMask, readSync.accessMask,
152 m_resource->getBuffer().handle, m_resource->getBuffer().offset, m_resource->getBuffer().size);
153 vk.cmdPipelineBarrier(*cmdBuffer, writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 1u, &barrier, 0u, (const VkImageMemoryBarrier*)DE_NULL);
154 }
155 else if (m_resource->getType() == RESOURCE_TYPE_IMAGE)
156 {
157 const VkImageMemoryBarrier barrier = makeImageMemoryBarrier(writeSync.accessMask, readSync.accessMask,
158 writeSync.imageLayout, readSync.imageLayout, m_resource->getImage().handle, m_resource->getImage().subresourceRange);
159 vk.cmdPipelineBarrier(*cmdBuffer, writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 0u, (const VkBufferMemoryBarrier*)DE_NULL, 1u, &barrier);
160 }
161
162 m_readOp->recordCommands(*cmdBuffer);
163
164 endCommandBuffer(vk, *cmdBuffer);
165
166 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
167
168 {
169 const Data expected = m_writeOp->getData();
170 const Data actual = m_readOp->getData();
171
172 if (0 != deMemCmp(expected.data, actual.data, expected.size))
173 return tcu::TestStatus::fail("Memory contents don't match");
174 }
175
176 return tcu::TestStatus::pass("OK");
177 }
178 };
179
180 class SemaphoreTestInstance : public BaseTestInstance
181 {
182 public:
SemaphoreTestInstance(Context & context,const ResourceDescription & resourceDesc,const OperationSupport & writeOp,const OperationSupport & readOp,PipelineCacheData & pipelineCacheData)183 SemaphoreTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
184 : BaseTestInstance (context, resourceDesc, writeOp, readOp, pipelineCacheData)
185 {
186 }
187
iterate(void)188 tcu::TestStatus iterate (void)
189 {
190 enum {WRITE=0, READ, COUNT};
191 const DeviceInterface& vk = m_context.getDeviceInterface();
192 const VkDevice device = m_context.getDevice();
193 const VkQueue queue = m_context.getUniversalQueue();
194 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
195 const Unique<VkSemaphore> semaphore (createSemaphore (vk, device));
196 const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
197 const Move<VkCommandBuffer> ptrCmdBuffer[COUNT] = {makeCommandBuffer(vk, device, *cmdPool), makeCommandBuffer(vk, device, *cmdPool)};
198 VkCommandBuffer cmdBuffers[COUNT] = {*ptrCmdBuffer[WRITE], *ptrCmdBuffer[READ]};
199 const VkPipelineStageFlags stageBits[] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT };
200 const VkSubmitInfo submitInfo[2] =
201 {
202 {
203 VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
204 DE_NULL, // const void* pNext;
205 0u, // deUint32 waitSemaphoreCount;
206 DE_NULL, // const VkSemaphore* pWaitSemaphores;
207 (const VkPipelineStageFlags*)DE_NULL,
208 1u, // deUint32 commandBufferCount;
209 &cmdBuffers[WRITE], // const VkCommandBuffer* pCommandBuffers;
210 1u, // deUint32 signalSemaphoreCount;
211 &semaphore.get(), // const VkSemaphore* pSignalSemaphores;
212 },
213 {
214 VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
215 DE_NULL, // const void* pNext;
216 1u, // deUint32 waitSemaphoreCount;
217 &semaphore.get(), // const VkSemaphore* pWaitSemaphores;
218 stageBits, // const VkPipelineStageFlags* pWaitDstStageMask;
219 1u, // deUint32 commandBufferCount;
220 &cmdBuffers[READ], // const VkCommandBuffer* pCommandBuffers;
221 0u, // deUint32 signalSemaphoreCount;
222 DE_NULL, // const VkSemaphore* pSignalSemaphores;
223 }
224 };
225 const SyncInfo writeSync = m_writeOp->getSyncInfo();
226 const SyncInfo readSync = m_readOp->getSyncInfo();
227
228 beginCommandBuffer(vk, cmdBuffers[WRITE]);
229
230 m_writeOp->recordCommands(cmdBuffers[WRITE]);
231
232 if (m_resource->getType() == RESOURCE_TYPE_IMAGE)
233 {
234 const VkImageMemoryBarrier barrier = makeImageMemoryBarrier(writeSync.accessMask, readSync.accessMask,
235 writeSync.imageLayout, readSync.imageLayout, m_resource->getImage().handle, m_resource->getImage().subresourceRange);
236 vk.cmdPipelineBarrier(cmdBuffers[WRITE], writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 0u, (const VkBufferMemoryBarrier*)DE_NULL, 1u, &barrier);
237 }
238 else
239 {
240 const VkBufferMemoryBarrier barrier = makeBufferMemoryBarrier(writeSync.accessMask, readSync.accessMask,
241 m_resource->getBuffer().handle, 0, VK_WHOLE_SIZE);
242 vk.cmdPipelineBarrier(cmdBuffers[WRITE], writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 1u, &barrier, 0u, (const VkImageMemoryBarrier*)DE_NULL);
243 }
244
245 endCommandBuffer(vk, cmdBuffers[WRITE]);
246
247 beginCommandBuffer(vk, cmdBuffers[READ]);
248
249 m_readOp->recordCommands(cmdBuffers[READ]);
250
251 endCommandBuffer(vk, cmdBuffers[READ]);
252
253 VK_CHECK(vk.queueSubmit(queue, 2u, submitInfo, DE_NULL));
254 VK_CHECK(vk.queueWaitIdle(queue));
255
256 {
257 const Data expected = m_writeOp->getData();
258 const Data actual = m_readOp->getData();
259
260 if (0 != deMemCmp(expected.data, actual.data, expected.size))
261 return tcu::TestStatus::fail("Memory contents don't match");
262 }
263
264 return tcu::TestStatus::pass("OK");
265 }
266 };
267
268 class FenceTestInstance : public BaseTestInstance
269 {
270 public:
FenceTestInstance(Context & context,const ResourceDescription & resourceDesc,const OperationSupport & writeOp,const OperationSupport & readOp,PipelineCacheData & pipelineCacheData)271 FenceTestInstance (Context& context, const ResourceDescription& resourceDesc, const OperationSupport& writeOp, const OperationSupport& readOp, PipelineCacheData& pipelineCacheData)
272 : BaseTestInstance (context, resourceDesc, writeOp, readOp, pipelineCacheData)
273 {
274 }
275
iterate(void)276 tcu::TestStatus iterate (void)
277 {
278 enum {WRITE=0, READ, COUNT};
279 const DeviceInterface& vk = m_context.getDeviceInterface();
280 const VkDevice device = m_context.getDevice();
281 const VkQueue queue = m_context.getUniversalQueue();
282 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
283 const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
284 const Move<VkCommandBuffer> ptrCmdBuffer[COUNT] = {makeCommandBuffer(vk, device, *cmdPool), makeCommandBuffer(vk, device, *cmdPool)};
285 VkCommandBuffer cmdBuffers[COUNT] = {*ptrCmdBuffer[WRITE], *ptrCmdBuffer[READ]};
286 const SyncInfo writeSync = m_writeOp->getSyncInfo();
287 const SyncInfo readSync = m_readOp->getSyncInfo();
288
289 beginCommandBuffer(vk, cmdBuffers[WRITE]);
290
291 m_writeOp->recordCommands(cmdBuffers[WRITE]);
292
293 if (m_resource->getType() == RESOURCE_TYPE_IMAGE)
294 {
295 const VkImageMemoryBarrier barrier = makeImageMemoryBarrier(writeSync.accessMask, readSync.accessMask,
296 writeSync.imageLayout, readSync.imageLayout, m_resource->getImage().handle, m_resource->getImage().subresourceRange);
297 vk.cmdPipelineBarrier(cmdBuffers[WRITE], writeSync.stageMask, readSync.stageMask, (VkDependencyFlags)0, 0u, (const VkMemoryBarrier*)DE_NULL, 0u, (const VkBufferMemoryBarrier*)DE_NULL, 1u, &barrier);
298 }
299
300 endCommandBuffer(vk, cmdBuffers[WRITE]);
301
302 submitCommandsAndWait(vk, device, queue, cmdBuffers[WRITE]);
303
304 beginCommandBuffer(vk, cmdBuffers[READ]);
305
306 m_readOp->recordCommands(cmdBuffers[READ]);
307
308 endCommandBuffer(vk, cmdBuffers[READ]);
309
310 submitCommandsAndWait(vk, device, queue, cmdBuffers[READ]);
311
312 {
313 const Data expected = m_writeOp->getData();
314 const Data actual = m_readOp->getData();
315
316 if (0 != deMemCmp(expected.data, actual.data, expected.size))
317 return tcu::TestStatus::fail("Memory contents don't match");
318 }
319
320 return tcu::TestStatus::pass("OK");
321 }
322 };
323
324 class SyncTestCase : public TestCase
325 {
326 public:
SyncTestCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const SyncPrimitive syncPrimitive,const ResourceDescription resourceDesc,const OperationName writeOp,const OperationName readOp,PipelineCacheData & pipelineCacheData)327 SyncTestCase (tcu::TestContext& testCtx,
328 const std::string& name,
329 const std::string& description,
330 const SyncPrimitive syncPrimitive,
331 const ResourceDescription resourceDesc,
332 const OperationName writeOp,
333 const OperationName readOp,
334 PipelineCacheData& pipelineCacheData)
335 : TestCase (testCtx, name, description)
336 , m_resourceDesc (resourceDesc)
337 , m_writeOp (makeOperationSupport(writeOp, resourceDesc))
338 , m_readOp (makeOperationSupport(readOp, resourceDesc))
339 , m_syncPrimitive (syncPrimitive)
340 , m_pipelineCacheData (pipelineCacheData)
341 {
342 }
343
initPrograms(SourceCollections & programCollection) const344 void initPrograms (SourceCollections& programCollection) const
345 {
346 m_writeOp->initPrograms(programCollection);
347 m_readOp->initPrograms(programCollection);
348 }
349
createInstance(Context & context) const350 TestInstance* createInstance (Context& context) const
351 {
352 switch (m_syncPrimitive)
353 {
354 case SYNC_PRIMITIVE_FENCE:
355 return new FenceTestInstance(context, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData);
356 case SYNC_PRIMITIVE_SEMAPHORE:
357 return new SemaphoreTestInstance(context, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData);
358 case SYNC_PRIMITIVE_BARRIER:
359 return new BarrierTestInstance(context, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData);
360 case SYNC_PRIMITIVE_EVENT:
361 return new EventTestInstance(context, m_resourceDesc, *m_writeOp, *m_readOp, m_pipelineCacheData);
362 }
363
364 DE_ASSERT(0);
365 return DE_NULL;
366 }
367
368 private:
369 const ResourceDescription m_resourceDesc;
370 const de::UniquePtr<OperationSupport> m_writeOp;
371 const de::UniquePtr<OperationSupport> m_readOp;
372 const SyncPrimitive m_syncPrimitive;
373 PipelineCacheData& m_pipelineCacheData;
374 };
375
createTests(tcu::TestCaseGroup * group,PipelineCacheData * pipelineCacheData)376 void createTests (tcu::TestCaseGroup* group, PipelineCacheData* pipelineCacheData)
377 {
378 tcu::TestContext& testCtx = group->getTestContext();
379
380 static const struct
381 {
382 const char* name;
383 SyncPrimitive syncPrimitive;
384 int numOptions;
385 } groups[] =
386 {
387 { "fence", SYNC_PRIMITIVE_FENCE, 0, },
388 { "semaphore", SYNC_PRIMITIVE_SEMAPHORE, 0, },
389 { "barrier", SYNC_PRIMITIVE_BARRIER, 1, },
390 { "event", SYNC_PRIMITIVE_EVENT, 1, },
391 };
392
393 for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(groups); ++groupNdx)
394 {
395 de::MovePtr<tcu::TestCaseGroup> synchGroup (new tcu::TestCaseGroup(testCtx, groups[groupNdx].name, ""));
396
397 for (int writeOpNdx = 0; writeOpNdx < DE_LENGTH_OF_ARRAY(s_writeOps); ++writeOpNdx)
398 for (int readOpNdx = 0; readOpNdx < DE_LENGTH_OF_ARRAY(s_readOps); ++readOpNdx)
399 {
400 const OperationName writeOp = s_writeOps[writeOpNdx];
401 const OperationName readOp = s_readOps[readOpNdx];
402 const std::string opGroupName = getOperationName(writeOp) + "_" + getOperationName(readOp);
403 bool empty = true;
404
405 de::MovePtr<tcu::TestCaseGroup> opGroup (new tcu::TestCaseGroup(testCtx, opGroupName.c_str(), ""));
406
407 for (int resourceNdx = 0; resourceNdx < DE_LENGTH_OF_ARRAY(s_resources); ++resourceNdx)
408 {
409 const ResourceDescription& resource = s_resources[resourceNdx];
410 std::string name = getResourceName(resource);
411
412 if (isResourceSupported(writeOp, resource) && isResourceSupported(readOp, resource))
413 {
414 opGroup->addChild(new SyncTestCase(testCtx, name, "", groups[groupNdx].syncPrimitive, resource, writeOp, readOp, *pipelineCacheData));
415 empty = false;
416 }
417 }
418 if (!empty)
419 synchGroup->addChild(opGroup.release());
420 }
421
422 group->addChild(synchGroup.release());
423 }
424 }
425
426 } // anonymous
427
createSynchronizedOperationSingleQueueTests(tcu::TestContext & testCtx,PipelineCacheData & pipelineCacheData)428 tcu::TestCaseGroup* createSynchronizedOperationSingleQueueTests (tcu::TestContext& testCtx, PipelineCacheData& pipelineCacheData)
429 {
430 return createTestGroup(testCtx, "single_queue", "Synchronization of a memory-modifying operation", createTests, &pipelineCacheData);
431 }
432
433 } // synchronization
434 } // vkt
435