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