1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2017 The Khronos Group Inc.
6 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 *//*!
21 * \file
22 * \brief Protected memory storage buffer tests
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktProtectedMemStorageBufferTests.hpp"
26
27 #include "deRandom.hpp"
28 #include "deStringUtil.hpp"
29 #include "tcuTestLog.hpp"
30 #include "tcuVector.hpp"
31 #include "tcuStringTemplate.hpp"
32
33 #include "vkPrograms.hpp"
34 #include "vktTestCase.hpp"
35 #include "vktTestGroupUtil.hpp"
36 #include "vkTypeUtil.hpp"
37 #include "vkBuilderUtil.hpp"
38 #include "vkCmdUtil.hpp"
39
40 #include "vktProtectedMemBufferValidator.hpp"
41 #include "vktProtectedMemUtils.hpp"
42 #include "vktProtectedMemContext.hpp"
43
44 namespace vkt
45 {
46 namespace ProtectedMem
47 {
48
49 namespace
50 {
51
52 enum {
53 RENDER_HEIGHT = 128,
54 RENDER_WIDTH = 128,
55 };
56
57 enum {
58 RANDOM_TEST_COUNT = 10,
59 };
60
61 enum SSBOTestType {
62 SSBO_READ,
63 SSBO_WRITE,
64 SSBO_ATOMIC
65 };
66
67 enum SSBOAtomicType {
68 ATOMIC_ADD,
69 ATOMIC_MIN,
70 ATOMIC_MAX,
71 ATOMIC_AND,
72 ATOMIC_OR,
73 ATOMIC_XOR,
74 ATOMIC_EXCHANGE,
75 ATOMIC_COMPSWAP
76 };
77
78
getSSBOTestDescription(SSBOTestType type)79 const char* getSSBOTestDescription (SSBOTestType type)
80 {
81 switch (type) {
82 case SSBO_READ: return "Test for read storage buffer on protected memory.";
83 case SSBO_WRITE: return "Test for write storage buffer on protected memory.";
84 case SSBO_ATOMIC: return "Test for atomic storage buffer on protected memory.";
85 default: DE_FATAL("Invalid SSBO test type"); return "";
86 }
87 }
88
getSSBOTypeString(SSBOTestType type)89 const char* getSSBOTypeString (SSBOTestType type)
90 {
91 switch (type) {
92 case SSBO_READ: return "read";
93 case SSBO_WRITE: return "write";
94 case SSBO_ATOMIC: return "atomic";
95 default: DE_FATAL("Invalid SSBO test type"); return "";
96 }
97 }
98
getSSBOAtomicTypeString(SSBOAtomicType type)99 const char* getSSBOAtomicTypeString (SSBOAtomicType type)
100 {
101 switch (type)
102 {
103 case ATOMIC_ADD: return "add";
104 case ATOMIC_MIN: return "min";
105 case ATOMIC_MAX: return "max";
106 case ATOMIC_AND: return "and";
107 case ATOMIC_OR: return "or";
108 case ATOMIC_XOR: return "xor";
109 case ATOMIC_EXCHANGE: return "exchange";
110 case ATOMIC_COMPSWAP: return "compswap";
111 default: DE_FATAL("Invalid SSBO atomic operation type"); return "";
112 }
113 }
114
addBufferCopyCmd(const vk::DeviceInterface & vk,vk::VkCommandBuffer cmdBuffer,deUint32 queueFamilyIndex,vk::VkBuffer srcBuffer,vk::VkBuffer dstBuffer,deUint32 copySize,bool dstFragment)115 void static addBufferCopyCmd (const vk::DeviceInterface& vk,
116 vk::VkCommandBuffer cmdBuffer,
117 deUint32 queueFamilyIndex,
118 vk::VkBuffer srcBuffer,
119 vk::VkBuffer dstBuffer,
120 deUint32 copySize,
121 bool dstFragment)
122 {
123 const vk::VkBufferMemoryBarrier dstWriteStartBarrier =
124 {
125 vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType
126 DE_NULL, // const void* pNext
127 vk::VK_ACCESS_HOST_WRITE_BIT, // VkAccessFlags srcAccessMask
128 vk::VK_ACCESS_SHADER_WRITE_BIT, // VkAccessFlags dstAccessMask
129 queueFamilyIndex, // uint32_t srcQueueFamilyIndex
130 queueFamilyIndex, // uint32_t dstQueueFamilyIndex
131 srcBuffer, // VkBuffer buffer
132 0u, // VkDeviceSize offset
133 VK_WHOLE_SIZE, // VkDeviceSize size
134 };
135
136 vk.cmdPipelineBarrier(cmdBuffer,
137 vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // srcStageMask
138 vk::VK_PIPELINE_STAGE_TRANSFER_BIT, // dstStageMask
139 (vk::VkDependencyFlags)0,
140 0, (const vk::VkMemoryBarrier*)DE_NULL,
141 1, &dstWriteStartBarrier,
142 0, (const vk::VkImageMemoryBarrier*)DE_NULL);
143
144 const vk::VkBufferCopy copyRegion =
145 {
146 0, // VkDeviceSize srcOffset
147 0, // VkDeviceSize dstOffset
148 copySize // VkDeviceSize size
149 };
150 vk.cmdCopyBuffer(cmdBuffer, srcBuffer, dstBuffer, 1, ©Region);
151
152 const vk::VkBufferMemoryBarrier dstWriteEndBarrier =
153 {
154 vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType
155 DE_NULL, // const void* pNext
156 vk::VK_ACCESS_SHADER_WRITE_BIT, // VkAccessFlags srcAccessMask
157 vk::VK_ACCESS_SHADER_READ_BIT, // VkAccessFlags dstAccessMask
158 queueFamilyIndex, // uint32_t srcQueueFamilyIndex
159 queueFamilyIndex, // uint32_t dstQueueFamilyIndex
160 dstBuffer, // VkBuffer buffer
161 0u, // VkDeviceSize offset
162 VK_WHOLE_SIZE, // VkDeviceSize size
163 };
164
165 vk.cmdPipelineBarrier(cmdBuffer,
166 vk::VK_PIPELINE_STAGE_TRANSFER_BIT, // srcStageMask
167 dstFragment ? vk::VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT :
168 vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, // dstStageMask
169 (vk::VkDependencyFlags)0,
170 0, (const vk::VkMemoryBarrier*)DE_NULL,
171 1, &dstWriteEndBarrier,
172 0, (const vk::VkImageMemoryBarrier*)DE_NULL);
173
174 }
175
176 template<typename T>
177 class StorageBufferTestInstance : public ProtectedTestInstance
178 {
179 public:
180 StorageBufferTestInstance (Context& ctx,
181 const SSBOTestType testType,
182 const glu::ShaderType shaderType,
183 const tcu::UVec4 testInput,
184 const BufferValidator<T>& validator);
185 virtual tcu::TestStatus iterate (void);
186
187 private:
188 tcu::TestStatus executeFragmentTest (void);
189 tcu::TestStatus executeComputeTest (void);
190
191 const SSBOTestType m_testType;
192 const glu::ShaderType m_shaderType;
193 const tcu::UVec4 m_testInput;
194 const BufferValidator<T>& m_validator;
195 const vk::VkFormat m_imageFormat;
196 };
197
198 template<typename T>
199 class StorageBufferTestCase : public TestCase
200 {
201 public:
StorageBufferTestCase(tcu::TestContext & testctx,const SSBOTestType testType,const glu::ShaderType shaderType,const char * name,const tcu::UVec4 testInput,ValidationDataStorage<T> validationData,vk::VkFormat format,const std::string & extraShader="")202 StorageBufferTestCase (tcu::TestContext& testctx,
203 const SSBOTestType testType,
204 const glu::ShaderType shaderType,
205 const char* name,
206 const tcu::UVec4 testInput,
207 ValidationDataStorage<T> validationData,
208 vk::VkFormat format,
209 const std::string& extraShader = "")
210 : TestCase (testctx, name, getSSBOTestDescription(testType))
211 , m_testType (testType)
212 , m_shaderType (shaderType)
213 , m_testInput (testInput)
214 , m_validator (validationData, format)
215 , m_extraShader (extraShader)
216 {
217 }
createInstance(Context & ctx) const218 virtual TestInstance* createInstance (Context& ctx) const
219 {
220 return new StorageBufferTestInstance<T>(ctx, m_testType, m_shaderType, m_testInput, m_validator);
221 }
222 virtual void initPrograms (vk::SourceCollections& programCollection) const;
checkSupport(Context & context) const223 virtual void checkSupport (Context& context) const
224 {
225 checkProtectedQueueSupport(context);
226 }
227
~StorageBufferTestCase(void)228 virtual ~StorageBufferTestCase (void) {}
229
230 private:
231 const SSBOTestType m_testType;
232 const glu::ShaderType m_shaderType;
233 const tcu::UVec4 m_testInput;
234 const BufferValidator<T> m_validator;
235 const std::string m_extraShader;
236 };
237
238 template<typename T>
StorageBufferTestInstance(Context & ctx,const SSBOTestType testType,const glu::ShaderType shaderType,const tcu::UVec4 testInput,const BufferValidator<T> & validator)239 StorageBufferTestInstance<T>::StorageBufferTestInstance (Context& ctx,
240 const SSBOTestType testType,
241 const glu::ShaderType shaderType,
242 const tcu::UVec4 testInput,
243 const BufferValidator<T>& validator)
244 : ProtectedTestInstance (ctx)
245 , m_testType (testType)
246 , m_shaderType (shaderType)
247 , m_testInput (testInput)
248 , m_validator (validator)
249 , m_imageFormat (vk::VK_FORMAT_R8G8B8A8_UNORM)
250 {
251 }
252
253 template<typename T>
initPrograms(vk::SourceCollections & programCollection) const254 void StorageBufferTestCase<T>::initPrograms (vk::SourceCollections& programCollection) const
255 {
256 const char* vertexShader =
257 "#version 450\n"
258 "layout(location=0) out vec4 vIndex;\n"
259 "void main() {\n"
260 " vec2 pos[4] = vec2[4]( vec2(-0.7, 0.7), vec2(0.7, 0.7), vec2(0.0, -0.7), vec2(-0.7, -0.7) );\n"
261 " vIndex = vec4(gl_VertexIndex);\n"
262 " gl_PointSize = 1.0;\n"
263 " gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);\n"
264 "}";
265
266 // set = 0, location = 0 -> buffer ProtectedTestBuffer (uvec4)
267 // set = 0, location = 2 -> buffer ProtectedTestBufferSource (uvec4)
268 const char* readShaderTemplateStr =
269 "#version 450\n"
270 "${INPUT_DECLARATION}\n"
271 "\n"
272 "layout(set=0, binding=0, std140) buffer ProtectedTestBuffer\n"
273 "{\n"
274 " highp uvec4 protectedTestResultBuffer;\n"
275 "};\n"
276 "\n"
277 "layout(set=0, binding=2, std140) buffer ProtectedTestBufferSource\n"
278 "{\n"
279 " highp uvec4 protectedTestBufferSource;\n"
280 "};\n"
281 "\n"
282 "void main (void)\n"
283 "{\n"
284 " protectedTestResultBuffer = protectedTestBufferSource;\n"
285 " ${FRAGMENT_OUTPUT}\n"
286 "}\n";
287
288 // set = 0, location = 0 -> buffer ProtectedTestBuffer (uvec4)
289 // set = 0, location = 1 -> uniform Data (uvec4)
290 const char* writeShaderTemplateStr =
291 "#version 450\n"
292 "${INPUT_DECLARATION}\n"
293 "\n"
294 "layout(set=0, binding=0, std140) buffer ProtectedTestBuffer\n"
295 "{\n"
296 " highp uvec4 protectedTestResultBuffer;\n"
297 "};\n"
298 "\n"
299 "layout(set=0, binding=1, std140) uniform Data\n"
300 "{\n"
301 " highp uvec4 testInput;\n"
302 "};\n"
303 "\n"
304 "void main (void)\n"
305 "{\n"
306 " protectedTestResultBuffer = testInput;\n"
307 " ${FRAGMENT_OUTPUT}\n"
308 "}\n";
309
310 // set = 0, location = 0 -> buffer ProtectedTestBuffer (uint [4])
311 const char* atomicTestShaderTemplateStr =
312 "#version 450\n"
313 "${INPUT_DECLARATION}\n"
314 "\n"
315 "layout(set=0, binding=0, std430) buffer ProtectedTestBuffer\n"
316 "{\n"
317 " highp uint protectedTestResultBuffer[4];\n"
318 "};\n"
319 "\n"
320 "void main (void)\n"
321 "{\n"
322 " uint i = uint(${INVOCATION_ID});\n"
323 " ${ATOMIC_FUNCTION_CALL}\n"
324 " ${FRAGMENT_OUTPUT}\n"
325 "}\n";
326
327 const char* shaderTemplateStr;
328 std::map<std::string, std::string> shaderParam;
329 switch (m_testType) {
330 case SSBO_READ: shaderTemplateStr = readShaderTemplateStr; break;
331 case SSBO_WRITE: shaderTemplateStr = writeShaderTemplateStr; break;
332 case SSBO_ATOMIC: {
333 shaderTemplateStr = atomicTestShaderTemplateStr;
334 shaderParam["ATOMIC_FUNCTION_CALL"] = m_extraShader;
335 break;
336 }
337 default: DE_FATAL("Incorrect SSBO test type"); return;
338 }
339
340 if (m_shaderType == glu::SHADERTYPE_FRAGMENT)
341 {
342 shaderParam["INPUT_DECLARATION"] = "layout(location=0) out mediump vec4 o_color;\n"
343 "layout(location=0) in vec4 vIndex;\n";
344 shaderParam["FRAGMENT_OUTPUT"] = "o_color = vec4( 0.0, 0.4, 1.0, 1.0 );\n";
345 shaderParam["INVOCATION_ID"] = "vIndex.x";
346
347 programCollection.glslSources.add("vert") << glu::VertexSource(vertexShader);
348 programCollection.glslSources.add("TestShader") << glu::FragmentSource(tcu::StringTemplate(shaderTemplateStr).specialize(shaderParam));
349 }
350 else if (m_shaderType == glu::SHADERTYPE_COMPUTE)
351 {
352 shaderParam["INPUT_DECLARATION"] = "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n";
353 shaderParam["FRAGMENT_OUTPUT"] = "";
354 shaderParam["INVOCATION_ID"] = "gl_GlobalInvocationID.x";
355 programCollection.glslSources.add("TestShader") << glu::ComputeSource(tcu::StringTemplate(shaderTemplateStr).specialize(shaderParam));
356 }
357 else
358 DE_FATAL("Incorrect shader type");
359
360 m_validator.initPrograms(programCollection);
361 }
362
363 template<typename T>
executeFragmentTest(void)364 tcu::TestStatus StorageBufferTestInstance<T>::executeFragmentTest(void)
365 {
366 ProtectedContext& ctx (m_protectedContext);
367 const vk::DeviceInterface& vk = ctx.getDeviceInterface();
368 const vk::VkDevice device = ctx.getDevice();
369 const vk::VkQueue queue = ctx.getQueue();
370 const deUint32 queueFamilyIndex = ctx.getQueueFamilyIndex();
371
372 const deUint32 testUniformSize = sizeof(m_testInput);
373 de::UniquePtr<vk::BufferWithMemory> testUniform (makeBuffer(ctx,
374 PROTECTION_DISABLED,
375 queueFamilyIndex,
376 testUniformSize,
377 vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
378 | vk::VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
379 vk::MemoryRequirement::HostVisible));
380
381 // Set the test input uniform data
382 {
383 deMemcpy(testUniform->getAllocation().getHostPtr(), &m_testInput, testUniformSize);
384 vk::flushAlloc(vk, device, testUniform->getAllocation());
385 }
386 const deUint32 testBufferSize = sizeof(ValidationDataStorage<T>);
387 de::MovePtr<vk::BufferWithMemory> testBuffer (makeBuffer(ctx,
388 PROTECTION_ENABLED,
389 queueFamilyIndex,
390 testBufferSize,
391 vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
392 | vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT,
393 vk::MemoryRequirement::Protected));
394 de::MovePtr<vk::BufferWithMemory> testBufferSource (makeBuffer(ctx,
395 PROTECTION_ENABLED,
396 queueFamilyIndex,
397 testBufferSize,
398 vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
399 | vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT,
400 vk::MemoryRequirement::Protected));
401
402 vk::Move<vk::VkShaderModule> vertexShader (vk::createShaderModule(vk, device, ctx.getBinaryCollection().get("vert"), 0));
403 vk::Unique<vk::VkShaderModule> testShader (vk::createShaderModule(vk, device, ctx.getBinaryCollection().get("TestShader"), 0));
404
405 // Create descriptors
406 vk::Unique<vk::VkDescriptorSetLayout> descriptorSetLayout(vk::DescriptorSetLayoutBuilder()
407 .addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_ALL)
408 .addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_ALL)
409 .addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_ALL)
410 .build(vk, device));
411 vk::Unique<vk::VkDescriptorPool> descriptorPool(vk::DescriptorPoolBuilder()
412 .addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u)
413 .addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u)
414 .addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u)
415 .build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
416 vk::Unique<vk::VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
417
418 // Update descriptor set information
419 {
420 vk::VkDescriptorBufferInfo descTestBuffer = makeDescriptorBufferInfo(**testBuffer, 0, testBufferSize);
421 vk::VkDescriptorBufferInfo descTestUniform = makeDescriptorBufferInfo(**testUniform, 0, testUniformSize);
422 vk::VkDescriptorBufferInfo descTestBufferSource = makeDescriptorBufferInfo(**testBufferSource, 0, testBufferSize);
423
424 vk::DescriptorSetUpdateBuilder()
425 .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descTestBuffer)
426 .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &descTestUniform)
427 .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(2u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descTestBufferSource)
428 .update(vk, device);
429 }
430
431 // Create output image
432 de::MovePtr<vk::ImageWithMemory> colorImage (createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex,
433 RENDER_WIDTH, RENDER_HEIGHT,
434 m_imageFormat,
435 vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|vk::VK_IMAGE_USAGE_SAMPLED_BIT));
436 vk::Unique<vk::VkImageView> colorImageView (createImageView(ctx, **colorImage, m_imageFormat));
437 vk::Unique<vk::VkRenderPass> renderPass (createRenderPass(ctx, m_imageFormat));
438 vk::Unique<vk::VkFramebuffer> framebuffer (createFramebuffer(ctx, RENDER_WIDTH, RENDER_HEIGHT, *renderPass, *colorImageView));
439
440 // Build pipeline
441 vk::Unique<vk::VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
442 vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
443 vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
444
445 // Create pipeline
446 vk::Unique<vk::VkPipeline> graphicsPipeline (makeGraphicsPipeline(vk, device, *pipelineLayout, *renderPass,
447 *vertexShader, *testShader,
448 std::vector<vk::VkVertexInputBindingDescription>(),
449 std::vector<vk::VkVertexInputAttributeDescription>(),
450 tcu::UVec2(RENDER_WIDTH, RENDER_HEIGHT),
451 vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST));
452
453 beginCommandBuffer(vk, *cmdBuffer);
454
455 if (m_testType == SSBO_READ || m_testType == SSBO_ATOMIC)
456 {
457 vk::VkBuffer targetBuffer = (m_testType == SSBO_ATOMIC) ? **testBuffer : **testBufferSource;
458 addBufferCopyCmd(vk, *cmdBuffer, queueFamilyIndex, **testUniform, targetBuffer, testUniformSize, true);
459 }
460
461 // Start image barrier
462 {
463 const vk::VkImageMemoryBarrier startImgBarrier =
464 {
465 vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
466 DE_NULL, // pNext
467 0, // srcAccessMask
468 vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // dstAccessMask
469 vk::VK_IMAGE_LAYOUT_UNDEFINED, // oldLayout
470 vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout
471 queueFamilyIndex, // srcQueueFamilyIndex
472 queueFamilyIndex, // dstQueueFamilyIndex
473 **colorImage, // image
474 {
475 vk::VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
476 0u, // baseMipLevel
477 1u, // mipLevels
478 0u, // baseArraySlice
479 1u, // subresourceRange
480 }
481 };
482
483 vk.cmdPipelineBarrier(*cmdBuffer,
484 vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // srcStageMask
485 vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // dstStageMask
486 (vk::VkDependencyFlags)0,
487 0, (const vk::VkMemoryBarrier*)DE_NULL,
488 0, (const vk::VkBufferMemoryBarrier*)DE_NULL,
489 1, &startImgBarrier);
490 }
491
492 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, vk::makeRect2D(0, 0, RENDER_WIDTH, RENDER_HEIGHT), tcu::Vec4(0.125f, 0.25f, 0.5f, 1.0f));
493 vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
494 vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u, DE_NULL);
495
496 vk.cmdDraw(*cmdBuffer, 4u, 1u, 0u, 0u);
497 endRenderPass(vk, *cmdBuffer);
498
499 {
500 const vk::VkImageMemoryBarrier endImgBarrier =
501 {
502 vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
503 DE_NULL, // pNext
504 vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // srcAccessMask
505 vk::VK_ACCESS_SHADER_READ_BIT, // dstAccessMask
506 vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // oldLayout
507 vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // newLayout
508 queueFamilyIndex, // srcQueueFamilyIndex
509 queueFamilyIndex, // dstQueueFamilyIndex
510 **colorImage, // image
511 {
512 vk::VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
513 0u, // baseMipLevel
514 1u, // mipLevels
515 0u, // baseArraySlice
516 1u, // subresourceRange
517 }
518 };
519 vk.cmdPipelineBarrier(*cmdBuffer,
520 vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // srcStageMask
521 vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, // dstStageMask
522 (vk::VkDependencyFlags)0,
523 0, (const vk::VkMemoryBarrier*)DE_NULL,
524 0, (const vk::VkBufferMemoryBarrier*)DE_NULL,
525 1, &endImgBarrier);
526 }
527
528 endCommandBuffer(vk, *cmdBuffer);
529
530 // Execute Draw
531 {
532 const vk::Unique<vk::VkFence> fence (vk::createFence(vk, device));
533 VK_CHECK(vk.resetFences(device, 1, &fence.get()));
534 VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
535 }
536
537 // Log inputs
538 ctx.getTestContext().getLog()
539 << tcu::TestLog::Message << "Input values: \n"
540 << "1: " << m_testInput << "\n"
541 << tcu::TestLog::EndMessage;
542
543 // Validate buffer
544 if (m_validator.validateBuffer(ctx, **testBuffer))
545 return tcu::TestStatus::pass("Everything went OK");
546 else
547 return tcu::TestStatus::fail("Something went really wrong");
548 }
549
550 template<typename T>
executeComputeTest(void)551 tcu::TestStatus StorageBufferTestInstance<T>::executeComputeTest(void)
552 {
553 ProtectedContext& ctx (m_protectedContext);
554 const vk::DeviceInterface& vk = ctx.getDeviceInterface();
555 const vk::VkDevice device = ctx.getDevice();
556 const vk::VkQueue queue = ctx.getQueue();
557 const deUint32 queueFamilyIndex = ctx.getQueueFamilyIndex();
558
559 const deUint32 testUniformSize = sizeof(m_testInput);
560 de::UniquePtr<vk::BufferWithMemory> testUniform (makeBuffer(ctx,
561 PROTECTION_DISABLED,
562 queueFamilyIndex,
563 testUniformSize,
564 vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
565 | vk::VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
566 vk::MemoryRequirement::HostVisible));
567
568 // Set the test input uniform data
569 {
570 deMemcpy(testUniform->getAllocation().getHostPtr(), &m_testInput, testUniformSize);
571 vk::flushAlloc(vk, device, testUniform->getAllocation());
572 }
573
574 const deUint32 testBufferSize = sizeof(ValidationDataStorage<T>);
575 de::MovePtr<vk::BufferWithMemory> testBuffer (makeBuffer(ctx,
576 PROTECTION_ENABLED,
577 queueFamilyIndex,
578 testBufferSize,
579 vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
580 | vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT,
581 vk::MemoryRequirement::Protected));
582 de::MovePtr<vk::BufferWithMemory> testBufferSource (makeBuffer(ctx,
583 PROTECTION_ENABLED,
584 queueFamilyIndex,
585 testBufferSize,
586 vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
587 | vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT,
588 vk::MemoryRequirement::Protected));
589
590 vk::Unique<vk::VkShaderModule> testShader (vk::createShaderModule(vk, device, ctx.getBinaryCollection().get("TestShader"), 0));
591
592 // Create descriptors
593 vk::Unique<vk::VkDescriptorSetLayout> descriptorSetLayout(vk::DescriptorSetLayoutBuilder()
594 .addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT)
595 .addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT)
596 .addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT)
597 .build(vk, device));
598 vk::Unique<vk::VkDescriptorPool> descriptorPool(vk::DescriptorPoolBuilder()
599 .addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u)
600 .addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u)
601 .addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u)
602 .build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
603 vk::Unique<vk::VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
604
605 // Update descriptor set information
606 {
607 vk::VkDescriptorBufferInfo descTestBuffer = makeDescriptorBufferInfo(**testBuffer, 0, testBufferSize);
608 vk::VkDescriptorBufferInfo descTestUniform = makeDescriptorBufferInfo(**testUniform, 0, testUniformSize);
609 vk::VkDescriptorBufferInfo descTestBufferSource = makeDescriptorBufferInfo(**testBufferSource, 0, testBufferSize);
610
611 vk::DescriptorSetUpdateBuilder()
612 .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descTestBuffer)
613 .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &descTestUniform)
614 .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(2u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descTestBufferSource)
615 .update(vk, device);
616 }
617
618 // Build and execute test
619 {
620 const vk::Unique<vk::VkFence> fence (vk::createFence(vk, device));
621 vk::Unique<vk::VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
622 vk::Unique<vk::VkPipeline> SSBOPipeline (makeComputePipeline(vk, device, *pipelineLayout, *testShader, DE_NULL));
623 vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
624 vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
625 deUint32 dispatchCount = (m_testType == SSBO_ATOMIC) ? 4u : 1u;
626
627 beginCommandBuffer(vk, *cmdBuffer);
628
629 if (m_testType == SSBO_READ || m_testType == SSBO_ATOMIC)
630 {
631 vk::VkBuffer targetBuffer = (m_testType == SSBO_ATOMIC) ? **testBuffer : **testBufferSource;
632 addBufferCopyCmd(vk, *cmdBuffer, queueFamilyIndex, **testUniform, targetBuffer, testUniformSize, false);
633 }
634
635 vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *SSBOPipeline);
636 vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u, DE_NULL);
637
638 vk.cmdDispatch(*cmdBuffer, dispatchCount, 1u, 1u);
639
640 endCommandBuffer(vk, *cmdBuffer);
641 VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
642 }
643
644 ctx.getTestContext().getLog()
645 << tcu::TestLog::Message << "Input values: \n"
646 << "1: " << m_testInput << "\n"
647 << tcu::TestLog::EndMessage;
648
649 // Validate buffer
650 if (m_validator.validateBuffer(ctx, **testBuffer))
651 return tcu::TestStatus::pass("Everything went OK");
652 else
653 return tcu::TestStatus::fail("Something went really wrong");
654 }
655
656 template<typename T>
iterate(void)657 tcu::TestStatus StorageBufferTestInstance<T>::iterate(void)
658 {
659 switch (m_shaderType)
660 {
661 case glu::SHADERTYPE_FRAGMENT: return executeFragmentTest();
662 case glu::SHADERTYPE_COMPUTE: return executeComputeTest();
663 default:
664 DE_FATAL("Incorrect shader type"); return tcu::TestStatus::fail("");
665 }
666 }
667
createSpecifiedStorageBufferTests(tcu::TestContext & testCtx,const std::string groupName,SSBOTestType testType,const glu::ShaderType shaderType,const ValidationDataStorage<tcu::UVec4> testData[],size_t testCount)668 tcu::TestCaseGroup* createSpecifiedStorageBufferTests (tcu::TestContext& testCtx,
669 const std::string groupName,
670 SSBOTestType testType,
671 const glu::ShaderType shaderType,
672 const ValidationDataStorage<tcu::UVec4> testData[],
673 size_t testCount)
674 {
675 const std::string testTypeStr = getSSBOTypeString(testType);
676 const std::string description = "Storage buffer " + testTypeStr + " tests";
677 de::MovePtr<tcu::TestCaseGroup> testGroup (new tcu::TestCaseGroup(testCtx, groupName.c_str(), description.c_str()));
678
679 for (size_t ndx = 0; ndx < testCount; ++ndx)
680 {
681 const std::string name = testTypeStr + "_" + de::toString(ndx + 1);
682 testGroup->addChild(new StorageBufferTestCase<tcu::UVec4>(testCtx, testType, shaderType, name.c_str(), testData[ndx].values, testData[ndx], vk::VK_FORMAT_R32G32B32A32_UINT));
683 }
684
685 return testGroup.release();
686 }
687
createRandomizedBufferTests(tcu::TestContext & testCtx,SSBOTestType testType,const glu::ShaderType shaderType,size_t testCount)688 tcu::TestCaseGroup* createRandomizedBufferTests (tcu::TestContext& testCtx, SSBOTestType testType, const glu::ShaderType shaderType, size_t testCount)
689 {
690 de::Random rnd (testCtx.getCommandLine().getBaseSeed());
691 std::vector<ValidationDataStorage<tcu::UVec4> > testData;
692 testData.resize(testCount);
693
694 for (size_t ndx = 0; ndx < testCount; ++ndx)
695 for (deUint32 compIdx = 0; compIdx < 4; ++compIdx)
696 testData[ndx].values[compIdx] = rnd.getUint32();
697
698 return createSpecifiedStorageBufferTests(testCtx, "random", testType, shaderType, testData.data(), testData.size());
699 }
700
createRWStorageBufferTests(tcu::TestContext & testCtx,const std::string groupName,const std::string groupDescription,SSBOTestType testType,const ValidationDataStorage<tcu::UVec4> testData[],size_t testCount)701 tcu::TestCaseGroup* createRWStorageBufferTests (tcu::TestContext& testCtx,
702 const std::string groupName,
703 const std::string groupDescription,
704 SSBOTestType testType,
705 const ValidationDataStorage<tcu::UVec4> testData[],
706 size_t testCount)
707 {
708 de::MovePtr<tcu::TestCaseGroup> ssboRWTestGroup (new tcu::TestCaseGroup(testCtx, groupName.c_str(), groupDescription.c_str()));
709
710 glu::ShaderType shaderTypes[] = {
711 glu::SHADERTYPE_FRAGMENT,
712 glu::SHADERTYPE_COMPUTE
713 };
714
715 for (int shaderNdx = 0; shaderNdx < DE_LENGTH_OF_ARRAY(shaderTypes); ++shaderNdx)
716 {
717 const glu::ShaderType shaderType = shaderTypes[shaderNdx];
718 const std::string shaderName = glu::getShaderTypeName(shaderType);
719 const std::string shaderGroupDesc = "Storage buffer tests for shader type: " + shaderName;
720 de::MovePtr<tcu::TestCaseGroup> testShaderGroup (new tcu::TestCaseGroup(testCtx, shaderName.c_str(), shaderGroupDesc.c_str()));
721
722 testShaderGroup->addChild(createSpecifiedStorageBufferTests(testCtx, "static", testType, shaderType, testData, testCount));
723 testShaderGroup->addChild(createRandomizedBufferTests(testCtx, testType, shaderType, RANDOM_TEST_COUNT));
724 ssboRWTestGroup->addChild(testShaderGroup.release());
725 }
726
727 return ssboRWTestGroup.release();
728 }
729
calculateAtomicOpData(SSBOAtomicType type,const tcu::UVec4 & inputValue,const deUint32 atomicArg,std::string & atomicCall,tcu::UVec4 & refValue,const deUint32 swapNdx=0)730 void calculateAtomicOpData (SSBOAtomicType type,
731 const tcu::UVec4& inputValue,
732 const deUint32 atomicArg,
733 std::string& atomicCall,
734 tcu::UVec4& refValue,
735 const deUint32 swapNdx = 0)
736 {
737 switch (type)
738 {
739 case ATOMIC_ADD:
740 {
741 refValue = inputValue + tcu::UVec4(atomicArg);
742 atomicCall = "atomicAdd(protectedTestResultBuffer[i], " + de::toString(atomicArg) + "u);";
743 break;
744 }
745 case ATOMIC_MIN:
746 {
747 refValue = tcu::UVec4(std::min(inputValue.x(), atomicArg), std::min(inputValue.y(), atomicArg), std::min(inputValue.z(), atomicArg), std::min(inputValue.w(), atomicArg));
748 atomicCall = "atomicMin(protectedTestResultBuffer[i], " + de::toString(atomicArg) + "u);";
749 break;
750 }
751 case ATOMIC_MAX:
752 {
753 refValue = tcu::UVec4(std::max(inputValue.x(), atomicArg), std::max(inputValue.y(), atomicArg), std::max(inputValue.z(), atomicArg), std::max(inputValue.w(), atomicArg));
754 atomicCall = "atomicMax(protectedTestResultBuffer[i], " + de::toString(atomicArg) + "u);";
755 break;
756 }
757 case ATOMIC_AND:
758 {
759 refValue = tcu::UVec4(inputValue.x() & atomicArg, inputValue.y() & atomicArg, inputValue.z() & atomicArg, inputValue.w() & atomicArg);
760 atomicCall = "atomicAnd(protectedTestResultBuffer[i], " + de::toString(atomicArg) + "u);";
761 break;
762 }
763 case ATOMIC_OR:
764 {
765 refValue = tcu::UVec4(inputValue.x() | atomicArg, inputValue.y() | atomicArg, inputValue.z() | atomicArg, inputValue.w() | atomicArg);
766 atomicCall = "atomicOr(protectedTestResultBuffer[i], " + de::toString(atomicArg) + "u);";
767 break;
768 }
769 case ATOMIC_XOR:
770 {
771 refValue = tcu::UVec4(inputValue.x() ^ atomicArg, inputValue.y() ^ atomicArg, inputValue.z() ^ atomicArg, inputValue.w() ^ atomicArg);
772 atomicCall = "atomicXor(protectedTestResultBuffer[i], " + de::toString(atomicArg) + "u);";
773 break;
774 }
775 case ATOMIC_EXCHANGE:
776 {
777 refValue = tcu::UVec4(atomicArg);
778 atomicCall = "atomicExchange(protectedTestResultBuffer[i], " + de::toString(atomicArg) + "u);";
779 break;
780 }
781 case ATOMIC_COMPSWAP:
782 {
783 int selectedNdx = swapNdx % 4;
784 deUint32 selectedChange = inputValue[selectedNdx];
785
786 refValue = inputValue;
787 refValue[selectedNdx] = atomicArg;
788 atomicCall = "atomicCompSwap(protectedTestResultBuffer[i], " + de::toString(selectedChange) + "u, " + de::toString(atomicArg) + "u);";
789 break;
790 }
791 default: DE_FATAL("Incorrect atomic function type"); break;
792 }
793
794 }
795
796 } // anonymous
797
createReadStorageBufferTests(tcu::TestContext & testCtx)798 tcu::TestCaseGroup* createReadStorageBufferTests (tcu::TestContext& testCtx)
799 {
800 const ValidationDataStorage<tcu::UVec4> testData[] = {
801 { tcu::UVec4(0u, 0u, 0u, 0u) }, { tcu::UVec4(1u, 0u, 0u, 0u) },
802 { tcu::UVec4(0u, 1u, 0u, 0u) }, { tcu::UVec4(0u, 0u, 1u, 0u) },
803 { tcu::UVec4(0u, 0u, 0u, 1u) }, { tcu::UVec4(1u, 1u, 1u, 1u) }
804 };
805
806 return createRWStorageBufferTests(testCtx, "ssbo_read", "Storage Buffer Read Tests", SSBO_READ, testData, DE_LENGTH_OF_ARRAY(testData));
807 }
808
createWriteStorageBufferTests(tcu::TestContext & testCtx)809 tcu::TestCaseGroup* createWriteStorageBufferTests (tcu::TestContext& testCtx)
810 {
811 const ValidationDataStorage<tcu::UVec4> testData[] = {
812 { tcu::UVec4(0u, 0u, 0u, 0u) }, { tcu::UVec4(1u, 0u, 0u, 0u) },
813 { tcu::UVec4(0u, 1u, 0u, 0u) }, { tcu::UVec4(0u, 0u, 1u, 0u) },
814 { tcu::UVec4(0u, 0u, 0u, 1u) }, { tcu::UVec4(1u, 1u, 1u, 1u) }
815 };
816
817 return createRWStorageBufferTests(testCtx, "ssbo_write", "Storage Buffer Write Tests", SSBO_WRITE, testData, DE_LENGTH_OF_ARRAY(testData));
818 }
819
createAtomicStorageBufferTests(tcu::TestContext & testctx)820 tcu::TestCaseGroup* createAtomicStorageBufferTests (tcu::TestContext& testctx)
821 {
822 struct {
823 const tcu::UVec4 input;
824 const deUint32 atomicArg;
825 const deUint32 swapNdx;
826 } testData[] = {
827 { tcu::UVec4(0u, 1u, 2u, 3u), 10u, 0u },
828 { tcu::UVec4(10u, 20u, 30u, 40u), 3u, 2u },
829 { tcu::UVec4(800u, 400u, 230u, 999u), 50u, 3u },
830 { tcu::UVec4(100800u, 233400u, 22230u, 77999u), 800u, 1u },
831 };
832
833 SSBOAtomicType testTypes[] = {
834 ATOMIC_ADD,
835 ATOMIC_MIN,
836 ATOMIC_MAX,
837 ATOMIC_AND,
838 ATOMIC_OR,
839 ATOMIC_XOR,
840 ATOMIC_EXCHANGE,
841 ATOMIC_COMPSWAP
842 };
843
844 glu::ShaderType shaderTypes[] = {
845 glu::SHADERTYPE_FRAGMENT,
846 glu::SHADERTYPE_COMPUTE
847 };
848
849 de::Random rnd (testctx.getCommandLine().getBaseSeed());
850 de::MovePtr<tcu::TestCaseGroup> ssboAtomicTests (new tcu::TestCaseGroup(testctx, "ssbo_atomic", "Storage Buffer Atomic Tests"));
851
852 for (int shaderNdx = 0; shaderNdx < DE_LENGTH_OF_ARRAY(shaderTypes); ++shaderNdx)
853 {
854 const glu::ShaderType shaderType = shaderTypes[shaderNdx];
855 const std::string shaderName = glu::getShaderTypeName(shaderType);
856 const std::string shaderDesc = "Storage Buffer Atomic Tests for shader type: " + shaderName;
857 de::MovePtr<tcu::TestCaseGroup> atomicShaderGroup (new tcu::TestCaseGroup(testctx, shaderName.c_str(), shaderDesc.c_str()));
858
859 for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(testTypes); ++typeNdx)
860 {
861 SSBOAtomicType atomicType = testTypes[typeNdx];
862 const std::string atomicTypeStr = getSSBOAtomicTypeString(atomicType);
863 const std::string atomicDesc = "Storage Buffer Atomic Tests: " + atomicTypeStr;
864
865 de::MovePtr<tcu::TestCaseGroup> staticTests (new tcu::TestCaseGroup(testctx, "static", (atomicDesc + " with static input").c_str()));
866 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(testData); ++ndx)
867 {
868 const std::string name = "atomic_" + atomicTypeStr + "_" + de::toString(ndx + 1);
869 const tcu::UVec4& inputValue = testData[ndx].input;
870 const deUint32& atomicArg = testData[ndx].atomicArg;
871 std::string atomicCall;
872 tcu::UVec4 refValue;
873
874 calculateAtomicOpData(atomicType, inputValue, atomicArg, atomicCall, refValue, testData[ndx].swapNdx);
875
876 ValidationDataStorage<tcu::UVec4> validationData = { refValue };
877 staticTests->addChild(new StorageBufferTestCase<tcu::UVec4>(testctx, SSBO_ATOMIC, shaderType, name.c_str(), inputValue, validationData, vk::VK_FORMAT_R32G32B32A32_UINT, atomicCall));
878 }
879
880 de::MovePtr<tcu::TestCaseGroup> randomTests (new tcu::TestCaseGroup(testctx, "random", (atomicDesc + " with random input").c_str()));
881 for (int ndx = 0; ndx < RANDOM_TEST_COUNT; ndx++)
882 {
883 const std::string name = "atomic_" + atomicTypeStr + "_" + de::toString(ndx + 1);
884 deUint32 atomicArg = rnd.getUint16();
885 tcu::UVec4 inputValue;
886 tcu::UVec4 refValue;
887 std::string atomicCall;
888
889 for (int i = 0; i < 4; i++)
890 inputValue[i] = rnd.getUint16();
891
892 calculateAtomicOpData(atomicType, inputValue, atomicArg, atomicCall, refValue, ndx);
893
894 ValidationDataStorage<tcu::UVec4> validationData = { refValue };
895 randomTests->addChild(new StorageBufferTestCase<tcu::UVec4>(testctx, SSBO_ATOMIC, shaderType, name.c_str(), inputValue, validationData, vk::VK_FORMAT_R32G32B32A32_UINT, atomicCall));
896
897 }
898
899 de::MovePtr<tcu::TestCaseGroup> atomicTests (new tcu::TestCaseGroup(testctx, atomicTypeStr.c_str(), atomicDesc.c_str()));
900 atomicTests->addChild(staticTests.release());
901 atomicTests->addChild(randomTests.release());
902 atomicShaderGroup->addChild(atomicTests.release());
903 }
904 ssboAtomicTests->addChild(atomicShaderGroup.release());
905 }
906
907 return ssboAtomicTests.release();
908 }
909
910 } // ProtectedMem
911 } // vkt
912