1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2020 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 Test copying struct which contains an empty struct.
22 Test pointer comparisons of empty struct members.
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktSpvAsmComputeShaderTestUtil.hpp"
26 #include "vktSpvAsmComputeShaderCase.hpp"
27 #include "vktSpvAsmEmptyStructTests.hpp"
28 #include "tcuStringTemplate.hpp"
29 #include "vktTestGroupUtil.hpp"
30 #include "vktSpvAsmUtils.hpp"
31
32 namespace vkt
33 {
34 namespace SpirVAssembly
35 {
36 namespace
37 {
38
verifyResult(const std::vector<Resource> &,const std::vector<AllocationSp> & outputAllocs,const std::vector<Resource> & expectedOutputs,tcu::TestLog &)39 bool verifyResult(const std::vector<Resource> &, const std::vector<AllocationSp> &outputAllocs,
40 const std::vector<Resource> &expectedOutputs, tcu::TestLog &)
41 {
42 for (uint32_t outputNdx = 0; outputNdx < static_cast<uint32_t>(outputAllocs.size()); ++outputNdx)
43 {
44 std::vector<uint8_t> expectedBytes;
45 expectedOutputs[outputNdx].getBytes(expectedBytes);
46
47 const uint32_t itemCount = static_cast<uint32_t>(expectedBytes.size()) / 4u;
48 const uint32_t *returned = static_cast<const uint32_t *>(outputAllocs[outputNdx]->getHostPtr());
49 const uint32_t *expected = reinterpret_cast<const uint32_t *>(&expectedBytes.front());
50
51 for (uint32_t i = 0; i < itemCount; ++i)
52 {
53 // skip items with 0 as this is used to mark empty structure
54 if (expected[i] == 0)
55 continue;
56 if (expected[i] != returned[i])
57 return false;
58 }
59 }
60 return true;
61 }
62
addCopyingComputeGroup(tcu::TestCaseGroup * group)63 void addCopyingComputeGroup(tcu::TestCaseGroup *group)
64 {
65 const tcu::StringTemplate shaderTemplate(
66 "OpCapability Shader\n"
67
68 "OpExtension \"SPV_KHR_storage_buffer_storage_class\"\n"
69
70 "OpMemoryModel Logical GLSL450\n"
71 "OpEntryPoint GLCompute %main \"main\" %var_id\n"
72 "OpExecutionMode %main LocalSize 1 1 1\n"
73
74 "OpDecorate %var_id BuiltIn GlobalInvocationId\n"
75 "OpDecorate %var_input Binding 0\n"
76 "OpDecorate %var_input DescriptorSet 0\n"
77
78 "OpDecorate %var_outdata Binding 1\n"
79 "OpDecorate %var_outdata DescriptorSet 0\n"
80
81 "OpMemberDecorate %type_container_struct 0 Offset 0\n"
82 "OpMemberDecorate %type_container_struct 1 Offset ${OFFSET_1}\n"
83 "OpMemberDecorate %type_container_struct 2 Offset ${OFFSET_2}\n"
84 "OpMemberDecorate %type_container_struct 3 Offset ${OFFSET_3}\n"
85 "OpDecorate %type_container_struct Block\n"
86
87 + std::string(getComputeAsmCommonTypes()) +
88
89 //struct EmptyStruct {};
90 //struct ContainerStruct {
91 // int i;
92 // A a1;
93 // A a2;
94 // int j;
95 //};
96 //layout(set=, binding = ) buffer block B b;
97
98 // types
99 "%type_empty_struct = OpTypeStruct\n"
100 "%type_container_struct = OpTypeStruct %i32 %type_empty_struct %type_empty_struct %i32\n"
101
102 "%type_container_struct_ubo_ptr = OpTypePointer Uniform %type_container_struct\n"
103 "%type_container_struct_ssbo_ptr = OpTypePointer StorageBuffer %type_container_struct\n"
104
105 // variables
106 "%var_id = OpVariable %uvec3ptr Input\n"
107 "${VARIABLES}\n"
108
109 // void main function
110 "%main = OpFunction %void None %voidf\n"
111 "%label = OpLabel\n"
112
113 "${COPYING_METHOD}"
114
115 "OpReturn\n"
116 "OpFunctionEnd\n");
117
118 struct BufferType
119 {
120 std::string name;
121 VkDescriptorType descriptorType;
122 std::vector<uint32_t> offsets;
123 std::vector<int> input;
124 std::vector<int> expectedOutput;
125 std::string spirvVariables;
126 std::string spirvCopyObject;
127
128 BufferType(const std::string &name_, VkDescriptorType descriptorType_, const std::vector<uint32_t> &offsets_,
129 const std::vector<int> &input_, const std::vector<int> &expectedOutput_,
130 const std::string &spirvVariables_, const std::string &spirvCopyObject_)
131 : name(name_)
132 , descriptorType(descriptorType_)
133 , offsets(offsets_)
134 , input(input_)
135 , expectedOutput(expectedOutput_)
136 , spirvVariables(spirvVariables_)
137 , spirvCopyObject(spirvCopyObject_)
138 {
139 }
140 };
141 const std::vector<BufferType> bufferTypes{
142 {"ubo",
143 VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
144
145 // structure decorated as Block for variable in Uniform storage class
146 // must follow relaxed uniform buffer layout rules and be aligned to 16
147 {0, 16, 32, 48},
148 {2, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0},
149 {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0},
150
151 "%var_input = OpVariable %type_container_struct_ubo_ptr Uniform\n"
152 "%var_outdata = OpVariable %type_container_struct_ssbo_ptr StorageBuffer\n",
153
154 "%input_copy = OpCopyObject %type_container_struct_ubo_ptr %var_input\n"},
155 {"ssbo",
156 VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
157
158 {0, 4, 8, 12},
159 {2, 3, 5, 7},
160 {2, 0, 0, 7},
161
162 "%var_input = OpVariable %type_container_struct_ssbo_ptr StorageBuffer\n"
163 "%var_outdata = OpVariable %type_container_struct_ssbo_ptr StorageBuffer\n",
164
165 "%input_copy = OpCopyObject %type_container_struct_ssbo_ptr %var_input\n"}};
166
167 struct CopyingMethod
168 {
169 std::string name;
170 std::string spirvCopyCode;
171
172 CopyingMethod(const std::string &name_, const std::string &spirvCopyCode_)
173 : name(name_)
174 , spirvCopyCode(spirvCopyCode_)
175 {
176 }
177 };
178 const std::vector<CopyingMethod> copyingMethods{{"copy_object",
179
180 "%result = OpLoad %type_container_struct %input_copy\n"
181 "OpStore %var_outdata %result\n"},
182 {"copy_memory",
183
184 "OpCopyMemory %var_outdata %var_input\n"}};
185
186 for (const auto &bufferType : bufferTypes)
187 {
188 for (const auto ©ingMethod : copyingMethods)
189 {
190 std::string name = copyingMethod.name + "_" + bufferType.name;
191
192 std::map<std::string, std::string> specializationMap{
193 {"OFFSET_1", de::toString(bufferType.offsets[1])},
194 {"OFFSET_2", de::toString(bufferType.offsets[2])},
195 {"OFFSET_3", de::toString(bufferType.offsets[3])},
196 {"VARIABLES", bufferType.spirvVariables},
197
198 // NOTE: to simlify code spirvCopyObject is added also when OpCopyMemory is used
199 {"COPYING_METHOD", bufferType.spirvCopyObject + copyingMethod.spirvCopyCode},
200 };
201
202 ComputeShaderSpec spec;
203 spec.assembly = shaderTemplate.specialize(specializationMap);
204 spec.numWorkGroups = tcu::IVec3(1, 1, 1);
205 spec.verifyIO = verifyResult;
206 spec.inputs.push_back(Resource(BufferSp(new Int32Buffer(bufferType.input)), bufferType.descriptorType));
207 spec.outputs.push_back(Resource(BufferSp(new Int32Buffer(bufferType.expectedOutput))));
208 group->addChild(new SpvAsmComputeShaderCase(group->getTestContext(), name.c_str(), spec));
209 }
210 }
211 }
212
addPointerComparisionComputeGroup(tcu::TestCaseGroup * group)213 void addPointerComparisionComputeGroup(tcu::TestCaseGroup *group)
214 {
215 // NOTE: pointer comparison is possible only for StorageBuffer storage class
216
217 std::string computeSource = "OpCapability Shader\n"
218 "OpCapability VariablePointersStorageBuffer\n"
219
220 "OpMemoryModel Logical GLSL450\n"
221 "OpEntryPoint GLCompute %main \"main\" %var_id %var_input %var_outdata\n"
222 "OpExecutionMode %main LocalSize 1 1 1\n"
223
224 "OpDecorate %var_id BuiltIn GlobalInvocationId\n"
225 "OpDecorate %var_input Binding 0\n"
226 "OpDecorate %var_input DescriptorSet 0\n"
227
228 "OpDecorate %var_outdata Binding 1\n"
229 "OpDecorate %var_outdata DescriptorSet 0\n"
230
231 "OpMemberDecorate %type_container_struct 0 Offset 0\n"
232 "OpMemberDecorate %type_container_struct 1 Offset 4\n"
233 "OpMemberDecorate %type_container_struct 2 Offset 8\n"
234 "OpMemberDecorate %type_container_struct 3 Offset 12\n"
235 "OpDecorate %type_container_struct Block\n"
236
237 "OpMemberDecorate %type_i32_struct 0 Offset 0\n"
238 "OpDecorate %type_i32_struct Block\n"
239
240 + std::string(getComputeAsmCommonTypes("StorageBuffer")) +
241
242 // struct EmptyStruct {};
243 // struct ContainerStruct {
244 // int i;
245 // A a1;
246 // A a2;
247 // int j;
248 //};
249 // layout(set=, binding = ) buffer block B b;
250
251 // types
252 "%type_empty_struct = "
253 "OpTypeStruct\n"
254 "%type_container_struct = OpTypeStruct %i32 "
255 "%type_empty_struct %type_empty_struct %i32\n"
256 "%type_i32_struct = OpTypeStruct %i32\n"
257
258 // constants
259 "%c_i32_0 = OpConstant "
260 "%i32 0\n"
261 "%c_i32_1 = OpConstant "
262 "%i32 1\n"
263 "%c_i32_2 = OpConstant "
264 "%i32 2\n"
265
266 "%type_container_struct_in_ptr = OpTypePointer StorageBuffer "
267 "%type_container_struct\n"
268 "%type_i32_struct_out_ptr = OpTypePointer StorageBuffer "
269 "%type_i32_struct\n"
270
271 "%type_func_struct_ptr_ptr = OpTypePointer "
272 "StorageBuffer %type_empty_struct\n"
273
274 // variables
275 "%var_id = OpVariable "
276 "%uvec3ptr Input\n"
277 "%var_input = "
278 "OpVariable %type_container_struct_in_ptr StorageBuffer\n"
279 "%var_outdata = OpVariable "
280 "%type_i32_struct_out_ptr StorageBuffer\n"
281
282 // void main function
283 "%main = "
284 "OpFunction %void None %voidf\n"
285 "%label = "
286 "OpLabel\n"
287
288 // compare pointers to empty structures
289 "%ptr_to_first = "
290 "OpAccessChain %type_func_struct_ptr_ptr %var_input %c_i32_1\n"
291 "%ptr_to_second = "
292 "OpAccessChain %type_func_struct_ptr_ptr %var_input %c_i32_2\n"
293 "%pointers_not_equal = OpPtrNotEqual %bool "
294 "%ptr_to_first %ptr_to_second\n"
295 "%result = OpSelect "
296 "%i32 %pointers_not_equal %c_i32_1 %c_i32_0\n"
297 "%outloc = "
298 "OpAccessChain %i32ptr %var_outdata %c_i32_0\n"
299 "OpStore %outloc %result\n"
300
301 "OpReturn\n"
302 "OpFunctionEnd\n";
303
304 tcu::TestContext &testCtx = group->getTestContext();
305 std::vector<int> input = {2, 3, 5, 7};
306 std::vector<int> expectedOutput = {1};
307
308 ComputeShaderSpec spec;
309 spec.assembly = computeSource;
310 spec.numWorkGroups = tcu::IVec3(1, 1, 1);
311 spec.spirvVersion = SPIRV_VERSION_1_4;
312 spec.requestedVulkanFeatures.extVariablePointers.variablePointersStorageBuffer = true;
313 spec.inputs.push_back(Resource(BufferSp(new Int32Buffer(input))));
314 spec.outputs.push_back(Resource(BufferSp(new Int32Buffer(expectedOutput))));
315 spec.extensions.push_back("VK_KHR_spirv_1_4");
316 group->addChild(new SpvAsmComputeShaderCase(testCtx, "ssbo", spec));
317 }
318
addFunctionArgumentReturnValueGroup(tcu::TestCaseGroup * group)319 void addFunctionArgumentReturnValueGroup(tcu::TestCaseGroup *group)
320 {
321 const tcu::StringTemplate shaderTemplate(
322 " OpCapability Shader\n"
323 " %1 = OpExtInstImport \"GLSL.std.450\"\n"
324 " OpMemoryModel Logical GLSL450\n"
325 " OpEntryPoint GLCompute %4 \"main\" %29 %42 %51 ${GLOBAL_VARIABLE} %79\n"
326 " OpExecutionMode %4 LocalSize 2 1 1\n"
327 " OpSource GLSL 460\n"
328 " OpDecorate %29 BuiltIn LocalInvocationId\n"
329 " OpMemberDecorate %40 0 Offset 0\n"
330 " OpMemberDecorate %40 1 Offset 4\n"
331 " OpMemberDecorate %40 2 Offset 8\n"
332 " OpDecorate %40 Block\n"
333 " OpDecorate %42 DescriptorSet 0\n"
334 " OpDecorate %42 Binding 1\n"
335 " OpMemberDecorate %49 0 Offset 0\n"
336 " OpDecorate %49 Block\n"
337 " OpDecorate %51 DescriptorSet 0\n"
338 " OpDecorate %51 Binding 0\n"
339 " OpMemberDecorate %77 0 Offset 0\n"
340 " OpMemberDecorate %77 1 Offset 4\n"
341 " OpMemberDecorate %77 2 Offset 8\n"
342 " OpDecorate %77 Block\n"
343 " OpDecorate %79 DescriptorSet 0\n"
344 " OpDecorate %79 Binding 2\n"
345 " OpDecorate %96 BuiltIn WorkgroupSize\n"
346 " %2 = OpTypeVoid\n"
347 " %3 = OpTypeFunction %2\n"
348 " %7 = OpTypeStruct\n"
349 " %8 = OpTypePointer Function %7\n"
350 " %9 = OpTypeBool\n"
351 "%10 = OpTypePointer Function %9\n"
352 "%11 = OpTypeFunction %7 %8 %8 %10\n"
353 "%26 = OpTypeInt 32 0\n"
354 "%27 = OpTypeVector %26 3\n"
355 "%28 = OpTypePointer Input %27\n"
356 "%29 = OpVariable %28 Input\n"
357 "%30 = OpConstant %26 0\n"
358 "%31 = OpTypePointer Input %26\n"
359 "%34 = OpConstant %26 2\n"
360 "%39 = OpTypeStruct\n"
361 "%40 = OpTypeStruct %26 %39 %26\n"
362 "%41 = OpTypePointer StorageBuffer %40\n"
363 "%42 = OpVariable %41 StorageBuffer\n"
364 "%43 = OpTypeInt 32 1\n"
365 "%44 = OpConstant %43 0\n"
366 "%45 = OpConstant %26 1\n"
367 "%46 = OpTypePointer StorageBuffer %26\n"
368 "%48 = OpConstant %43 1\n"
369 "%49 = OpTypeStruct %39\n"
370 "%50 = OpTypePointer StorageBuffer %49\n"
371 "%51 = OpVariable %50 StorageBuffer\n"
372
373 "${VARIABLE_DEFINITION}\n"
374
375 "%59 = OpTypePointer StorageBuffer %39\n"
376 "%69 = OpConstant %43 2\n"
377 "%77 = OpTypeStruct %26 %39 %26\n"
378 "%78 = OpTypePointer StorageBuffer %77\n"
379 "%79 = OpVariable %78 StorageBuffer\n"
380 "%96 = OpConstantComposite %27 %34 %45 %45\n"
381 " %4 = OpFunction %2 None %3\n"
382 " %5 = OpLabel\n"
383
384 "${VARIABLE_FUNCTION_DEFINITION}\n"
385
386 "%58 = OpVariable %8 Function\n"
387 "%63 = OpVariable %8 Function\n"
388 "%65 = OpVariable %10 Function\n"
389 "%85 = OpVariable %8 Function\n"
390 "%89 = OpVariable %8 Function\n"
391 "%91 = OpVariable %10 Function\n"
392 "%32 = OpAccessChain %31 %29 %30\n"
393 "%33 = OpLoad %26 %32\n"
394 "%35 = OpUMod %26 %33 %34\n"
395 "%36 = OpIEqual %9 %35 %30\n"
396 " OpSelectionMerge %38 None\n"
397 " OpBranchConditional %36 %37 %38\n"
398 "%37 = OpLabel\n"
399 "%47 = OpAccessChain %46 %42 %44\n"
400 " OpStore %47 %45\n"
401 "%54 = OpAccessChain %31 %29 %30\n"
402 "%55 = OpLoad %26 %54\n"
403 "%56 = OpUMod %26 %55 %34\n"
404 "%57 = OpIEqual %9 %56 %30\n"
405 "%60 = OpAccessChain %59 %51 %44\n"
406 "%61 = OpLoad %39 %60\n"
407 "%62 = OpCopyLogical %7 %61\n"
408 " OpStore %58 %62\n"
409 "%64 = OpLoad %7 %53\n"
410 " OpStore %63 %64\n"
411 " OpStore %65 %57\n"
412 "%66 = OpFunctionCall %7 %15 %58 %63 %65\n"
413 "%67 = OpAccessChain %59 %42 %48\n"
414 "%68 = OpCopyLogical %39 %66\n"
415 " OpStore %67 %68\n"
416 "%70 = OpAccessChain %46 %42 %69\n"
417 " OpStore %70 %45\n"
418 " OpBranch %38\n"
419 "%38 = OpLabel\n"
420 "%71 = OpAccessChain %31 %29 %30\n"
421 "%72 = OpLoad %26 %71\n"
422 "%73 = OpUMod %26 %72 %34\n"
423 "%74 = OpIEqual %9 %73 %45\n"
424 " OpSelectionMerge %76 None\n"
425 " OpBranchConditional %74 %75 %76\n"
426 "%75 = OpLabel\n"
427 "%80 = OpAccessChain %46 %79 %44\n"
428 " OpStore %80 %45\n"
429 "%81 = OpAccessChain %31 %29 %30\n"
430 "%82 = OpLoad %26 %81\n"
431 "%83 = OpUMod %26 %82 %34\n"
432 "%84 = OpIEqual %9 %83 %45\n"
433 "%86 = OpAccessChain %59 %51 %44\n"
434 "%87 = OpLoad %39 %86\n"
435 "%88 = OpCopyLogical %7 %87\n"
436 " OpStore %85 %88\n"
437 "%90 = OpLoad %7 %53\n"
438 " OpStore %89 %90\n"
439 " OpStore %91 %84\n"
440 "%92 = OpFunctionCall %7 %15 %85 %89 %91\n"
441 "%93 = OpAccessChain %59 %79 %48\n"
442 "%94 = OpCopyLogical %39 %92\n"
443 " OpStore %93 %94\n"
444 "%95 = OpAccessChain %46 %79 %69\n"
445 " OpStore %95 %45\n"
446 " OpBranch %76\n"
447 "%76 = OpLabel\n"
448 " OpReturn\n"
449 " OpFunctionEnd\n"
450 "%15 = OpFunction %7 None %11\n"
451 "%12 = OpFunctionParameter %8\n"
452 "%13 = OpFunctionParameter %8\n"
453 "%14 = OpFunctionParameter %10\n"
454 "%16 = OpLabel\n"
455 "%17 = OpLoad %9 %14\n"
456 " OpSelectionMerge %19 None\n"
457 " OpBranchConditional %17 %18 %22\n"
458 "%18 = OpLabel\n"
459 "%20 = OpLoad %7 %12\n"
460 " OpReturnValue %20\n"
461 "%22 = OpLabel\n"
462 "%23 = OpLoad %7 %13\n"
463 " OpReturnValue %23\n"
464 "%19 = OpLabel\n"
465 " OpUnreachable\n"
466 " OpFunctionEnd\n");
467
468 struct VariableDefinition
469 {
470 std::string name;
471 std::string globalVariable;
472 std::string spirvVariableDefinitionCode;
473 std::string spirvVariableFunctionDefinitionCode;
474 };
475
476 std::vector<VariableDefinition> variableDefinitions{
477 {"global_variable_private",
478
479 "%53",
480
481 "%52 = OpTypePointer Private %7\n"
482 "%53 = OpVariable %52 Private\n",
483
484 ""},
485
486 {"global_variable_shared",
487
488 "%53",
489
490 "%52 = OpTypePointer Workgroup %7\n"
491 "%53 = OpVariable %52 Workgroup\n",
492
493 ""},
494
495 {"local_variable",
496
497 "",
498
499 "",
500
501 "%53 = OpVariable %8 Function\n"},
502 };
503
504 tcu::TestContext &testCtx = group->getTestContext();
505 std::vector<int> input = {2};
506
507 std::vector<uint32_t> expectedOutput = {1, 0xffffffff, 1};
508
509 for (const auto &variableDefinition : variableDefinitions)
510 {
511 std::map<std::string, std::string> specializationMap{
512 {"GLOBAL_VARIABLE", variableDefinition.globalVariable},
513 {"VARIABLE_DEFINITION", variableDefinition.spirvVariableDefinitionCode},
514 {"VARIABLE_FUNCTION_DEFINITION", variableDefinition.spirvVariableFunctionDefinitionCode},
515 };
516
517 ComputeShaderSpec spec;
518 spec.assembly = shaderTemplate.specialize(specializationMap);
519 spec.numWorkGroups = tcu::IVec3(2, 1, 1);
520 spec.spirvVersion = SPIRV_VERSION_1_4;
521 spec.requestedVulkanFeatures.extVariablePointers.variablePointersStorageBuffer = true;
522 spec.inputs.push_back(Resource(BufferSp(new Int32Buffer(input))));
523 spec.outputs.push_back(Resource(BufferSp(new Uint32Buffer(expectedOutput))));
524 spec.outputs.push_back(Resource(BufferSp(new Uint32Buffer(expectedOutput))));
525 spec.extensions.push_back("VK_KHR_spirv_1_4");
526 group->addChild(new SpvAsmComputeShaderCase(testCtx, variableDefinition.name.c_str(), spec));
527 }
528 }
529
530 } // namespace
531
createEmptyStructComputeGroup(tcu::TestContext & testCtx)532 tcu::TestCaseGroup *createEmptyStructComputeGroup(tcu::TestContext &testCtx)
533 {
534 // Tests empty structs in UBOs and SSBOs
535 de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "empty_struct"));
536
537 // Test copying struct which contains an empty struct
538 addTestGroup(group.get(), "copying", addCopyingComputeGroup);
539 // Test pointer comparisons of empty struct members
540 addTestGroup(group.get(), "pointer_comparison", addPointerComparisionComputeGroup);
541 // Test empty structs as function arguments or return type
542 addTestGroup(group.get(), "function", addFunctionArgumentReturnValueGroup);
543
544 return group.release();
545 }
546
547 } // namespace SpirVAssembly
548 } // namespace vkt
549