1 /*-------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2017 Google 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 SPIR-V Assembly Tests for the SPV_KHR_variable_pointers extension
22  *//*--------------------------------------------------------------------*/
23 
24 #include "tcuFloat.hpp"
25 #include "tcuRGBA.hpp"
26 #include "tcuStringTemplate.hpp"
27 #include "tcuTestLog.hpp"
28 #include "tcuVectorUtil.hpp"
29 
30 #include "vkDefs.hpp"
31 #include "vkDeviceUtil.hpp"
32 #include "vkMemUtil.hpp"
33 #include "vkPlatform.hpp"
34 #include "vkPrograms.hpp"
35 #include "vkQueryUtil.hpp"
36 #include "vkRef.hpp"
37 #include "vkRefUtil.hpp"
38 #include "vkStrUtil.hpp"
39 #include "vkTypeUtil.hpp"
40 
41 #include "deRandom.hpp"
42 #include "deStringUtil.hpp"
43 #include "deUniquePtr.hpp"
44 #include "deMath.h"
45 
46 #include "vktSpvAsmComputeShaderCase.hpp"
47 #include "vktSpvAsmComputeShaderTestUtil.hpp"
48 #include "vktSpvAsmGraphicsShaderTestUtil.hpp"
49 #include "vktSpvAsmVariablePointersTests.hpp"
50 #include "vktTestCaseUtil.hpp"
51 #include "vktTestGroupUtil.hpp"
52 
53 #include <limits>
54 #include <map>
55 #include <string>
56 #include <sstream>
57 #include <utility>
58 
59 namespace vkt
60 {
61 namespace SpirVAssembly
62 {
63 
64 using namespace vk;
65 using std::map;
66 using std::string;
67 using std::vector;
68 using tcu::IVec3;
69 using tcu::IVec4;
70 using tcu::RGBA;
71 using tcu::TestLog;
72 using tcu::TestStatus;
73 using tcu::Vec4;
74 using de::UniquePtr;
75 using tcu::StringTemplate;
76 using tcu::Vec4;
77 
78 namespace
79 {
80 
81 template<typename T>
fillRandomScalars(de::Random & rnd,T minValue,T maxValue,void * dst,int numValues,int offset=0)82 void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
83 {
84 	T* const typedPtr = (T*)dst;
85 	for (int ndx = 0; ndx < numValues; ndx++)
86 		typedPtr[offset + ndx] = randomScalar<T>(rnd, minValue, maxValue);
87 }
88 
89 // The following structure (outer_struct) is passed as a vector of 64 32-bit floats into some shaders.
90 //
91 // struct struct inner_struct {
92 //   vec4 x[2];
93 //   vec4 y[2];
94 // };
95 //
96 // struct outer_struct {
97 //   inner_struct r[2][2];
98 // };
99 //
100 // This method finds the correct offset from the base of a vector<float32> given the indexes into the structure.
101 // Returns the index in the inclusive range of 0 and 63. Each unit of the offset represents offset by the size of a 32-bit float.
getBaseOffset(deUint32 indexMatrixRow,deUint32 indexMatrixCol,deUint32 indexInnerStruct,deUint32 indexVec4Array,deUint32 indexVec4)102 deUint32 getBaseOffset (deUint32 indexMatrixRow,
103 						deUint32 indexMatrixCol,
104 						deUint32 indexInnerStruct,
105 						deUint32 indexVec4Array,
106 						deUint32 indexVec4)
107 {
108 	DE_ASSERT(indexMatrixRow < 2);
109 	DE_ASSERT(indexMatrixCol < 2);
110 	DE_ASSERT(indexInnerStruct < 2);
111 	DE_ASSERT(indexVec4Array < 2);
112 	DE_ASSERT(indexVec4 < 4);
113 
114 	deUint32 offset = 0;
115 
116 	// We have a matrix of 2 rows and 2 columns (total of 4 inner_structs). Each inner_struct contains 16 floats.
117 	// So, offset by 1 row means offset by 32 floats, and offset by 1 column means offset by 16 floats.
118 	offset += indexMatrixRow * 32;
119 	offset += indexMatrixCol * 16;
120 
121 	// The inner structure contains 2 members, each having 8 floats.
122 	// So offset by 1 in the inner struct means offset by 8 floats.
123 	offset += indexInnerStruct * 8;
124 
125 	// Each member (x|y) have 2 vectors of 4 floats. So, offset by 1 int the vec4 array means an offset by 4 floats.
126 	offset += indexVec4Array * 4;
127 
128 	// Each vec4 contains 4 floats, so each offset in the vec4 means offset by 1 float.
129 	offset += indexVec4;
130 
131 	return offset;
132 }
133 
134 // The following structure (input_buffer) is passed as a vector of 128 32-bit floats into some shaders.
135 //
136 // struct struct inner_struct {
137 //   vec4 x[2];
138 //   vec4 y[2];
139 // };
140 //
141 // struct outer_struct {
142 //   inner_struct r[2][2];
143 // };
144 //
145 // struct input_buffer {
146 //   outer_struct a;
147 //   outer_struct b;
148 // }
149 //
150 // This method finds the correct offset from the base of a vector<float32> given the indexes into the structure.
151 // Returns the index in the inclusive range of 0 and 127.
getBaseOffsetForSingleInputBuffer(deUint32 indexOuterStruct,deUint32 indexMatrixRow,deUint32 indexMatrixCol,deUint32 indexInnerStruct,deUint32 indexVec4Array,deUint32 indexVec4)152 deUint32 getBaseOffsetForSingleInputBuffer (deUint32 indexOuterStruct,
153 											deUint32 indexMatrixRow,
154 											deUint32 indexMatrixCol,
155 											deUint32 indexInnerStruct,
156 											deUint32 indexVec4Array,
157 											deUint32 indexVec4)
158 {
159 	DE_ASSERT(indexOuterStruct < 2);
160 	DE_ASSERT(indexMatrixRow < 2);
161 	DE_ASSERT(indexMatrixCol < 2);
162 	DE_ASSERT(indexInnerStruct < 2);
163 	DE_ASSERT(indexVec4Array < 2);
164 	DE_ASSERT(indexVec4 < 4);
165 
166 	// Get the offset assuming you have only one outer_struct.
167 	deUint32 offset = getBaseOffset(indexMatrixRow, indexMatrixCol, indexInnerStruct, indexVec4Array, indexVec4);
168 
169 	// If the second outer structure (b) is chosen in the input_buffer, we need to add an offset of 64 since
170 	// each outer_struct contains 64 floats.
171 	if (indexOuterStruct == 1)
172 		offset += 64;
173 
174 	return offset;
175 }
176 
addVariablePointersComputeGroup(tcu::TestCaseGroup * group)177 void addVariablePointersComputeGroup (tcu::TestCaseGroup* group)
178 {
179 	tcu::TestContext&				testCtx					= group->getTestContext();
180 	de::Random						rnd						(deStringHash(group->getName()));
181 	const int						seed					= testCtx.getCommandLine().getBaseSeed();
182 	const int						numMuxes				= 100;
183 	std::string						inputArraySize			= "200";
184 	vector<float>					inputAFloats			(2*numMuxes, 0);
185 	vector<float>					inputBFloats			(2*numMuxes, 0);
186 	vector<float>					inputSFloats			(numMuxes, 0);
187 	vector<float>					AmuxAOutputFloats		(numMuxes, 0);
188 	vector<float>					AmuxBOutputFloats		(numMuxes, 0);
189 	vector<float>					incrAmuxAOutputFloats	(numMuxes, 0);
190 	vector<float>					incrAmuxBOutputFloats	(numMuxes, 0);
191 	VulkanFeatures					requiredFeatures;
192 
193 	// Each output entry is chosen as follows: ( 0 <= i < numMuxes)
194 	// 1) For tests with one input buffer:  output[i] = (s[i] < 0) ? A[2*i] : A[2*i+1];
195 	// 2) For tests with two input buffers: output[i] = (s[i] < 0) ? A[i]   : B[i];
196 
197 	fillRandomScalars(rnd, -100.f, 100.f, &inputAFloats[0], 2*numMuxes);
198 	fillRandomScalars(rnd, -100.f, 100.f, &inputBFloats[0], 2*numMuxes);
199 
200 	// We want to guarantee that the S input has some positive and some negative values.
201 	// We choose random negative numbers for the first half, random positive numbers for the second half, and then shuffle.
202 	fillRandomScalars(rnd, -100.f, -1.f , &inputSFloats[0], numMuxes / 2);
203 	fillRandomScalars(rnd, 1.f   , 100.f, &inputSFloats[numMuxes / 2], numMuxes / 2);
204 	de::Random(seed).shuffle(inputSFloats.begin(), inputSFloats.end());
205 
206 	for (size_t i = 0; i < numMuxes; ++i)
207 	{
208 		AmuxAOutputFloats[i]     = (inputSFloats[i] < 0) ? inputAFloats[2*i]     : inputAFloats[2*i+1];
209 		AmuxBOutputFloats[i]	 = (inputSFloats[i] < 0) ? inputAFloats[i]		 : inputBFloats[i];
210 		incrAmuxAOutputFloats[i] = (inputSFloats[i] < 0) ? 1 + inputAFloats[2*i] : 1 + inputAFloats[2*i+1];
211 		incrAmuxBOutputFloats[i] = (inputSFloats[i] < 0) ? 1 + inputAFloats[i]	 : 1 + inputBFloats[i];
212 	}
213 
214 	const StringTemplate shaderTemplate (
215 		"OpCapability Shader\n"
216 
217 		"${ExtraCapability}\n"
218 
219 		"OpExtension \"SPV_KHR_variable_pointers\"\n"
220 		"OpExtension \"SPV_KHR_storage_buffer_storage_class\"\n"
221 		"OpMemoryModel Logical GLSL450\n"
222 		"OpEntryPoint GLCompute %main \"main\" %id\n"
223 		"OpExecutionMode %main LocalSize 1 1 1\n"
224 
225 		"OpSource GLSL 430\n"
226 		"OpName %main           \"main\"\n"
227 		"OpName %id             \"gl_GlobalInvocationID\"\n"
228 
229 		// Decorations
230 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
231 		"OpDecorate %indata_a DescriptorSet 0\n"
232 		"OpDecorate %indata_a Binding 0\n"
233 		"OpDecorate %indata_b DescriptorSet 0\n"
234 		"OpDecorate %indata_b Binding 1\n"
235 		"OpDecorate %indata_s DescriptorSet 0\n"
236 		"OpDecorate %indata_s Binding 2\n"
237 		"OpDecorate %outdata DescriptorSet 0\n"
238 		"OpDecorate %outdata Binding 3\n"
239 		"OpDecorate %f32arr ArrayStride 4\n"
240 		"OpDecorate %sb_f32ptr ArrayStride 4\n"
241 		"OpDecorate %buf Block\n"
242 		"OpMemberDecorate %buf 0 Offset 0\n"
243 
244 		+ string(getComputeAsmCommonTypes()) +
245 
246 		"%sb_f32ptr				= OpTypePointer StorageBuffer %f32\n"
247 		"%buf					= OpTypeStruct %f32arr\n"
248 		"%bufptr				= OpTypePointer StorageBuffer %buf\n"
249 		"%indata_a				= OpVariable %bufptr StorageBuffer\n"
250 		"%indata_b				= OpVariable %bufptr StorageBuffer\n"
251 		"%indata_s				= OpVariable %bufptr StorageBuffer\n"
252 		"%outdata				= OpVariable %bufptr StorageBuffer\n"
253 		"%id					= OpVariable %uvec3ptr Input\n"
254 		"%zero				    = OpConstant %i32 0\n"
255 		"%one					= OpConstant %i32 1\n"
256 		"%fzero					= OpConstant %f32 0\n"
257 		"%fone					= OpConstant %f32 1\n"
258 
259 		"${ExtraTypes}"
260 
261 		"${ExtraGlobalScopeVars}"
262 
263 		// We're going to put the "selector" function here.
264 		// This function type is needed tests that use OpFunctionCall.
265 		"%selector_func_type	= OpTypeFunction %sb_f32ptr %bool %sb_f32ptr %sb_f32ptr\n"
266 		"%choose_input_func		= OpFunction %sb_f32ptr None %selector_func_type\n"
267 		"%is_neg_param			= OpFunctionParameter %bool\n"
268 		"%first_ptr_param		= OpFunctionParameter %sb_f32ptr\n"
269 		"%second_ptr_param		= OpFunctionParameter %sb_f32ptr\n"
270 		"%selector_func_begin	= OpLabel\n"
271 		"%result_ptr			= OpSelect %sb_f32ptr %is_neg_param %first_ptr_param %second_ptr_param\n"
272 		"OpReturnValue %result_ptr\n"
273 		"OpFunctionEnd\n"
274 
275 		// main function is the entry_point
276 		"%main					= OpFunction %void None %voidf\n"
277 		"%label					= OpLabel\n"
278 
279 		"${ExtraFunctionScopeVars}"
280 
281 		"%idval					= OpLoad %uvec3 %id\n"
282 		"%i						= OpCompositeExtract %u32 %idval 0\n"
283 		"%two_i					= OpIAdd %u32 %i %i\n"
284 		"%two_i_plus_1			= OpIAdd %u32 %two_i %one\n"
285 		"%inloc_a_i				= OpAccessChain %sb_f32ptr %indata_a %zero %i\n"
286 		"%inloc_b_i				= OpAccessChain %sb_f32ptr %indata_b %zero %i\n"
287 		"%inloc_s_i             = OpAccessChain %sb_f32ptr %indata_s %zero %i\n"
288 		"%outloc_i              = OpAccessChain %sb_f32ptr %outdata  %zero %i\n"
289 		"%inloc_a_2i			= OpAccessChain %sb_f32ptr %indata_a %zero %two_i\n"
290 		"%inloc_a_2i_plus_1		= OpAccessChain %sb_f32ptr %indata_a %zero %two_i_plus_1\n"
291 		"%inval_s_i				= OpLoad %f32 %inloc_s_i\n"
292 		"%is_neg				= OpFOrdLessThan %bool %inval_s_i %fzero\n"
293 
294 		"${ExtraSetupComputations}"
295 
296 		"${ResultStrategy}"
297 
298 		"%mux_output			= OpLoad %f32 ${VarPtrName}\n"
299 		"						  OpStore %outloc_i %mux_output\n"
300 		"						  OpReturn\n"
301 		"						  OpFunctionEnd\n");
302 
303 	const bool singleInputBuffer[]  = { true, false };
304 	for (int inputBufferTypeIndex = 0 ; inputBufferTypeIndex < 2; ++inputBufferTypeIndex)
305 	{
306 		const bool isSingleInputBuffer			= singleInputBuffer[inputBufferTypeIndex];
307 		const string extraCap					= isSingleInputBuffer	? "OpCapability VariablePointersStorageBuffer\n" : "OpCapability VariablePointers\n";
308 		const vector<float>& expectedOutput		= isSingleInputBuffer	? AmuxAOutputFloats		 : AmuxBOutputFloats;
309 		const vector<float>& expectedIncrOutput	= isSingleInputBuffer	? incrAmuxAOutputFloats	 : incrAmuxBOutputFloats;
310 		const string bufferType					= isSingleInputBuffer	? "single_buffer"	 : "two_buffers";
311 		const string muxInput1					= isSingleInputBuffer	? " %inloc_a_2i "		 : " %inloc_a_i ";
312 		const string muxInput2					= isSingleInputBuffer	? " %inloc_a_2i_plus_1 " : " %inloc_b_i ";
313 
314 		// Set the proper extension features required for the test
315 		if (isSingleInputBuffer)
316 			requiredFeatures.extVariablePointers	= EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS_STORAGEBUFFER;
317 		else
318 			requiredFeatures.extVariablePointers	= EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS;
319 
320 		{ // Variable Pointer Reads (using OpSelect)
321 			ComputeShaderSpec				spec;
322 			map<string, string>				specs;
323 			string name						= "reads_opselect_" + bufferType;
324 			specs["ExtraCapability"]		= extraCap;
325 			specs["ExtraTypes"]				= "";
326 			specs["ExtraGlobalScopeVars"]	= "";
327 			specs["ExtraFunctionScopeVars"]	= "";
328 			specs["ExtraSetupComputations"]	= "";
329 			specs["VarPtrName"]				= "%mux_output_var_ptr";
330 			specs["ResultStrategy"]			= "%mux_output_var_ptr	= OpSelect %sb_f32ptr %is_neg" + muxInput1 + muxInput2 + "\n";
331 			spec.assembly					= shaderTemplate.specialize(specs);
332 			spec.numWorkGroups				= IVec3(numMuxes, 1, 1);
333 			spec.requestedVulkanFeatures	= requiredFeatures;
334 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
335 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
336 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
337 			spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
338 			spec.extensions.push_back("VK_KHR_variable_pointers");
339 			group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
340 		}
341 		{ // Variable Pointer Reads (using OpFunctionCall)
342 			ComputeShaderSpec				spec;
343 			map<string, string>				specs;
344 			string name						= "reads_opfunctioncall_" + bufferType;
345 			specs["ExtraCapability"]		= extraCap;
346 			specs["ExtraTypes"]				= "";
347 			specs["ExtraGlobalScopeVars"]	= "";
348 			specs["ExtraFunctionScopeVars"]	= "";
349 			specs["ExtraSetupComputations"]	= "";
350 			specs["VarPtrName"]				= "%mux_output_var_ptr";
351 			specs["ResultStrategy"]			= "%mux_output_var_ptr = OpFunctionCall %sb_f32ptr %choose_input_func %is_neg" + muxInput1 + muxInput2 + "\n";
352 			spec.assembly					= shaderTemplate.specialize(specs);
353 			spec.numWorkGroups				= IVec3(numMuxes, 1, 1);
354 			spec.requestedVulkanFeatures	= requiredFeatures;
355 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
356 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
357 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
358 			spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
359 			spec.extensions.push_back("VK_KHR_variable_pointers");
360 			group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
361 		}
362 		{ // Variable Pointer Reads (using OpPhi)
363 			ComputeShaderSpec				spec;
364 			map<string, string>				specs;
365 			string name						= "reads_opphi_" + bufferType;
366 			specs["ExtraCapability"]		= extraCap;
367 			specs["ExtraTypes"]				= "";
368 			specs["ExtraGlobalScopeVars"]	= "";
369 			specs["ExtraFunctionScopeVars"]	= "";
370 			specs["ExtraSetupComputations"]	= "";
371 			specs["VarPtrName"]				= "%mux_output_var_ptr";
372 			specs["ResultStrategy"]			=
373 				"							  OpSelectionMerge %end_label None\n"
374 				"							  OpBranchConditional %is_neg %take_mux_input_1 %take_mux_input_2\n"
375 				"%take_mux_input_1			= OpLabel\n"
376 				"							  OpBranch %end_label\n"
377 				"%take_mux_input_2			= OpLabel\n"
378 				"						      OpBranch %end_label\n"
379 				"%end_label					= OpLabel\n"
380 				"%mux_output_var_ptr		= OpPhi %sb_f32ptr" + muxInput1 + "%take_mux_input_1" + muxInput2 + "%take_mux_input_2\n";
381 			spec.assembly					= shaderTemplate.specialize(specs);
382 			spec.numWorkGroups				= IVec3(numMuxes, 1, 1);
383 			spec.requestedVulkanFeatures	= requiredFeatures;
384 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
385 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
386 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
387 			spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
388 			spec.extensions.push_back("VK_KHR_variable_pointers");
389 			group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
390 		}
391 		{ // Variable Pointer Reads (using OpCopyObject)
392 			ComputeShaderSpec				spec;
393 			map<string, string>				specs;
394 			string name						= "reads_opcopyobject_" + bufferType;
395 			specs["ExtraCapability"]		= extraCap;
396 			specs["ExtraTypes"]				= "";
397 			specs["ExtraGlobalScopeVars"]	= "";
398 			specs["ExtraFunctionScopeVars"]	= "";
399 			specs["ExtraSetupComputations"]	= "";
400 			specs["VarPtrName"]				= "%mux_output_var_ptr";
401 			specs["ResultStrategy"]			=
402 				"%mux_input_1_copy			= OpCopyObject %sb_f32ptr" + muxInput1 + "\n"
403 				"%mux_input_2_copy			= OpCopyObject %sb_f32ptr" + muxInput2 + "\n"
404 				"%mux_output_var_ptr		= OpSelect %sb_f32ptr %is_neg %mux_input_1_copy %mux_input_2_copy\n";
405 			spec.assembly					= shaderTemplate.specialize(specs);
406 			spec.numWorkGroups				= IVec3(numMuxes, 1, 1);
407 			spec.requestedVulkanFeatures	= requiredFeatures;
408 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
409 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
410 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
411 			spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
412 			spec.extensions.push_back("VK_KHR_variable_pointers");
413 			group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
414 		}
415 		{ // Test storing into Private variables.
416 			const char* storageClasses[]		= {"Private", "Function"};
417 			for (int classId = 0; classId < 2; ++classId)
418 			{
419 				ComputeShaderSpec				spec;
420 				map<string, string>				specs;
421 				std::string storageClass		= storageClasses[classId];
422 				std::string name				= "stores_" + string(de::toLower(storageClass)) + "_" + bufferType;
423 				std::string description			= "Test storing variable pointer into " + storageClass + " variable.";
424 				std::string extraVariable		= "%mux_output_copy	= OpVariable %sb_f32ptrptr " + storageClass + "\n";
425 				specs["ExtraTypes"]				= "%sb_f32ptrptr = OpTypePointer " + storageClass + " %sb_f32ptr\n";
426 				specs["ExtraCapability"]		= extraCap;
427 				specs["ExtraGlobalScopeVars"]	= (classId == 0) ? extraVariable : "";
428 				specs["ExtraFunctionScopeVars"]	= (classId == 1) ? extraVariable : "";
429 				specs["ExtraSetupComputations"]	= "";
430 				specs["VarPtrName"]				= "%mux_output_var_ptr";
431 				specs["ResultStrategy"]			=
432 					"%opselect_result			= OpSelect %sb_f32ptr %is_neg" + muxInput1 + muxInput2 + "\n"
433 					"							  OpStore %mux_output_copy %opselect_result\n"
434 					"%mux_output_var_ptr		= OpLoad %sb_f32ptr %mux_output_copy\n";
435 				spec.assembly					= shaderTemplate.specialize(specs);
436 				spec.numWorkGroups				= IVec3(numMuxes, 1, 1);
437 				spec.requestedVulkanFeatures	= requiredFeatures;
438 				spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
439 				spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
440 				spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
441 				spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
442 				spec.extensions.push_back("VK_KHR_variable_pointers");
443 				group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), description.c_str(), spec));
444 			}
445 		}
446 		{ // Variable Pointer Reads (Using OpPtrAccessChain)
447 			ComputeShaderSpec				spec;
448 			map<string, string>				specs;
449 			std::string name				= "reads_opptraccesschain_" + bufferType;
450 			std::string in_1				= isSingleInputBuffer ? " %a_2i_ptr "		 : " %a_i_ptr ";
451 			std::string in_2				= isSingleInputBuffer ? " %a_2i_plus_1_ptr " : " %b_i_ptr ";
452 			specs["ExtraTypes"]				= "";
453 			specs["ExtraCapability"]		= extraCap;
454 			specs["ExtraGlobalScopeVars"]	= "";
455 			specs["ExtraFunctionScopeVars"]	= "";
456 			specs["ExtraSetupComputations"]	= "";
457 			specs["VarPtrName"]				= "%mux_output_var_ptr";
458 			specs["ResultStrategy"]			=
459 					"%a_ptr					= OpAccessChain %sb_f32ptr %indata_a %zero %zero\n"
460 					"%b_ptr					= OpAccessChain %sb_f32ptr %indata_b %zero %zero\n"
461 					"%s_ptr					= OpAccessChain %sb_f32ptr %indata_s %zero %zero\n"
462 					"%out_ptr               = OpAccessChain %sb_f32ptr %outdata  %zero %zero\n"
463 					"%a_i_ptr               = OpPtrAccessChain %sb_f32ptr %a_ptr %i\n"
464 					"%b_i_ptr               = OpPtrAccessChain %sb_f32ptr %b_ptr %i\n"
465 					"%s_i_ptr               = OpPtrAccessChain %sb_f32ptr %s_ptr %i\n"
466 					"%a_2i_ptr              = OpPtrAccessChain %sb_f32ptr %a_ptr %two_i\n"
467 					"%a_2i_plus_1_ptr       = OpPtrAccessChain %sb_f32ptr %a_ptr %two_i_plus_1\n"
468 					"%mux_output_var_ptr    = OpSelect %sb_f32ptr %is_neg " + in_1 + in_2 + "\n";
469 			spec.assembly					= shaderTemplate.specialize(specs);
470 			spec.numWorkGroups				= IVec3(numMuxes, 1, 1);
471 			spec.requestedVulkanFeatures	= requiredFeatures;
472 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
473 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
474 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
475 			spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
476 			spec.extensions.push_back("VK_KHR_variable_pointers");
477 			group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
478 		}
479 		{   // Variable Pointer Writes
480 			ComputeShaderSpec				spec;
481 			map<string, string>				specs;
482 			std::string	name				= "writes_" + bufferType;
483 			specs["ExtraCapability"]		= extraCap;
484 			specs["ExtraTypes"]				= "";
485 			specs["ExtraGlobalScopeVars"]	= "";
486 			specs["ExtraFunctionScopeVars"]	= "";
487 			specs["ExtraSetupComputations"]	= "";
488 			specs["VarPtrName"]				= "%mux_output_var_ptr";
489 			specs["ResultStrategy"]			= "%mux_output_var_ptr = OpSelect %sb_f32ptr %is_neg" + muxInput1 + muxInput2 + "\n" +
490 											  "               %val = OpLoad %f32 %mux_output_var_ptr\n"
491 											  "        %val_plus_1 = OpFAdd %f32 %val %fone\n"
492 											  "						 OpStore %mux_output_var_ptr %val_plus_1\n";
493 			spec.assembly					= shaderTemplate.specialize(specs);
494 			spec.numWorkGroups				= IVec3(numMuxes, 1, 1);
495 			spec.requestedVulkanFeatures	= requiredFeatures;
496 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
497 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
498 			spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
499 			spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedIncrOutput))));
500 			spec.extensions.push_back("VK_KHR_variable_pointers");
501 			group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
502 		}
503 
504 		// If we only have VariablePointersStorageBuffer, then the extension does not apply to Workgroup storage class.
505 		// Therefore the Workgroup tests apply to cases where the VariablePointers capability is used (when 2 input buffers are used).
506 		if (!isSingleInputBuffer)
507 		{
508 			// VariablePointers on Workgroup
509 			ComputeShaderSpec				spec;
510 			map<string, string>				specs;
511 			std::string name				= "workgroup_" + bufferType;
512 			specs["ExtraCapability"]		= extraCap;
513 			specs["ExtraTypes"]				=
514 					"%c_i32_N				= OpConstant %i32 " + inputArraySize + " \n"
515 					"%f32arr_N				= OpTypeArray %f32 %c_i32_N\n"
516 					"%f32arr_wrkgrp_ptr		= OpTypePointer Workgroup %f32arr_N\n"
517 					"%f32_wrkgrp_ptr		= OpTypePointer Workgroup %f32\n";
518 			specs["ExtraGlobalScopeVars"]	=
519 					"%AW					= OpVariable %f32arr_wrkgrp_ptr Workgroup\n"
520 					"%BW					= OpVariable %f32arr_wrkgrp_ptr Workgroup\n";
521 			specs["ExtraFunctionScopeVars"]	= "";
522 			specs["ExtraSetupComputations"]	=
523 					"%loc_AW_i				= OpAccessChain %f32_wrkgrp_ptr %AW %i\n"
524 					"%loc_BW_i				= OpAccessChain %f32_wrkgrp_ptr %BW %i\n"
525 					"%inval_a_i				= OpLoad %f32 %inloc_a_i\n"
526 					"%inval_b_i				= OpLoad %f32 %inloc_b_i\n"
527 					"%inval_a_2i			= OpLoad %f32 %inloc_a_2i\n"
528 					"%inval_a_2i_plus_1		= OpLoad %f32 %inloc_a_2i_plus_1\n";
529 			specs["VarPtrName"]				= "%output_var_ptr";
530 			specs["ResultStrategy"]			=
531 					"						  OpStore %loc_AW_i %inval_a_i\n"
532 					"						  OpStore %loc_BW_i %inval_b_i\n"
533 					"%output_var_ptr		= OpSelect %f32_wrkgrp_ptr %is_neg %loc_AW_i %loc_BW_i\n";
534 			spec.assembly					= shaderTemplate.specialize(specs);
535 			spec.numWorkGroups				= IVec3(numMuxes, 1, 1);
536 			spec.requestedVulkanFeatures	= requiredFeatures;
537 			spec.inputs.push_back (Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
538 			spec.inputs.push_back (Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
539 			spec.inputs.push_back (Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
540 			spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
541 			spec.extensions.push_back("VK_KHR_variable_pointers");
542 			group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
543 		}
544 	}
545 }
546 
addComplexTypesVariablePointersComputeGroup(tcu::TestCaseGroup * group)547 void addComplexTypesVariablePointersComputeGroup (tcu::TestCaseGroup* group)
548 {
549 	tcu::TestContext&				testCtx					= group->getTestContext();
550 	const int						numFloats				= 64;
551 	vector<float>					inputA					(numFloats, 0);
552 	vector<float>					inputB					(numFloats, 0);
553 	vector<float>					inputC					(2*numFloats, 0);
554 	vector<float>					expectedOutput			(1, 0);
555 	VulkanFeatures					requiredFeatures;
556 
557 	// These tests exercise variable pointers into various levels of the following data-structures.
558 	//
559 	// struct struct inner_struct {
560 	//   vec4 x[2]; // array of 2 vectors. Each vector is 4 floats.
561 	//   vec4 y[2]; // array of 2 vectors. Each vector is 4 floats.
562 	// };
563 	//
564 	// struct outer_struct {
565 	//   inner_struct r[2][2];
566 	// };
567 	//
568 	// struct input_buffer {
569 	//   outer_struct a;
570 	//   outer_struct b;
571 	// }
572 	//
573 	// inputA is of type outer_struct.
574 	// inputB is of type outer_struct.
575 	// inputC is of type input_buffer.
576 	//
577 	// inputA and inputB are of the same size. When testing variable pointers pointing to
578 	// two different input buffers, we use inputA and inputB.
579 	//
580 	// inputC is twice the size of inputA. When testing the VariablePointersStorageBuffer capability,
581 	// the variable pointer must be confined to a single buffer. These tests will use inputC.
582 	//
583 	// The inner_struct contains 16 floats.
584 	// The outer_struct contains 64 floats.
585 	// The input_buffer contains 128 floats.
586 	// Populate the first input (inputA) to contain:  {0, 4, ... , 252}
587 	// Populate the second input (inputB) to contain: {3, 7, ... , 255}
588 	// Populate the third input (inputC) to contain:  {0, 4, ... , 252, 3, 7, ... , 255}
589 	// Note that the first half of inputC is the same as inputA and the second half is the same as inputB.
590 	for (size_t i = 0; i < numFloats; ++i)
591 	{
592 		inputA[i] = 4*float(i) / 255;
593 		inputB[i] = ((4*float(i)) + 3) / 255;
594 		inputC[i] = inputA[i];
595 		inputC[i + numFloats] = inputB[i];
596 	}
597 
598 	// In the following tests we use variable pointers to point to different types:
599 	// nested structures, matrices of structures, arrays of structures, arrays of vectors, vectors of scalars, and scalars.
600 	// outer_structure.inner_structure[?][?].x[?][?];
601 	//   ^                    ^        ^  ^  ^ ^  ^
602 	//
603 	// For tests with 2 input buffers:
604 	// 1. inputA						or	inputB							= nested structure
605 	// 2. inputA.r						or	inputB.r						= matrices of structures
606 	// 3. inputA.r[?]					or	inputB.r[?]						= arrays of structures
607 	// 4. inputA.r[?][?]				or	inputB.r[?][?]					= structures
608 	// 5. inputA.r[?][?].(x|y)			or	inputB.r[?][?].(x|y)			= arrays of vectors
609 	// 6. inputA.r[?][?].(x|y)[?]		or	inputB.r[?][?].(x|y)[?]			= vectors of scalars
610 	// 7. inputA.r[?][?].(x|y)[?][?]	or	inputB.r[?][?].(x|y)[?][?]		= scalars
611 	// For tests with 1 input buffer:
612 	// 1. inputC.a						or	inputC.b						= nested structure
613 	// 2. inputC.a.r					or	inputC.b.r						= matrices of structures
614 	// 3. inputC.a.r[?]					or	inputC.b.r[?]					= arrays of structures
615 	// 4. inputC.a.r[?][?]				or	inputC.b.r[?][?]				= structures
616 	// 5. inputC.a.r[?][?].(x|y)		or	inputC.b.r[?][?].(x|y)			= arrays of vectors
617 	// 6. inputC.a.r[?][?].(x|y)[?]		or	inputC.b.r[?][?].(x|y)[?]		= vectors of scalars
618 	// 7. inputC.a.r[?][?].(x|y)[?][?]	or	inputC.b.r[?][?].(x|y)[?][?]	= scalars
619 	const int numLevels = 7;
620 
621 	const string commonDecorations (
622 		// Decorations
623 		"OpDecorate %id BuiltIn GlobalInvocationId		\n"
624 		"OpDecorate %outdata DescriptorSet 0			\n"
625 		"OpDecorate %outdata Binding 3					\n"
626 
627 		// Set the Block decoration
628 		"OpDecorate %output_buffer	Block				\n"
629 
630 		// Set the Offsets
631 		"OpMemberDecorate %output_buffer 0 Offset 0		\n"
632 		"OpMemberDecorate %input_buffer  0 Offset 0		\n"
633 		"OpMemberDecorate %input_buffer  1 Offset 256	\n"
634 		"OpMemberDecorate %outer_struct  0 Offset 0		\n"
635 		"OpMemberDecorate %inner_struct  0 Offset 0		\n"
636 		"OpMemberDecorate %inner_struct  1 Offset 32	\n"
637 
638 		// Set the ArrayStrides
639 		"OpDecorate %arr2_v4float        ArrayStride 16		\n"
640 		"OpDecorate %arr2_inner_struct   ArrayStride 64		\n"
641 		"OpDecorate %mat2x2_inner_struct ArrayStride 128	\n"
642 		"OpDecorate %outer_struct_ptr    ArrayStride 256	\n"
643 		"OpDecorate %v4f32_ptr           ArrayStride 16		\n"
644 	);
645 
646 	const string inputABDecorations (
647 		"OpDecorate %inputA DescriptorSet 0				\n"
648 		"OpDecorate %inputB DescriptorSet 0				\n"
649 		"OpDecorate %inputA Binding 0					\n"
650 		"OpDecorate %inputB Binding 1					\n"
651 
652 		// inputA and inputB have type outer_struct so it needs Block
653 		"OpDecorate %outer_struct	Block				\n"
654 	);
655 
656 	const string inputCDecorations (
657 		"OpDecorate %inputC DescriptorSet 0				\n"
658 		"OpDecorate %inputC Binding 2					\n"
659 
660 		// inputC has type input_buffer so it needs Block
661 		"OpDecorate %input_buffer	Block				\n"
662 	);
663 
664 	const string types (
665 		///////////////
666 		// CONSTANTS //
667 		///////////////
668 		"%c_bool_true			= OpConstantTrue	%bool							\n"
669 		"%c_bool_false			= OpConstantFalse	%bool							\n"
670 		"%c_i32_0				= OpConstant		%i32		0					\n"
671 		"%c_i32_1				= OpConstant		%i32		1					\n"
672 		"%c_i32_2				= OpConstant		%i32		2					\n"
673 		"%c_i32_3				= OpConstant		%i32		3					\n"
674 		"%c_u32_2				= OpConstant		%u32		2					\n"
675 
676 		///////////
677 		// TYPES //
678 		///////////
679 		"%v4f32                 = OpTypeVector %f32 4                               \n"
680 
681 		// struct struct inner_struct {
682 		//   vec4 x[2]; // array of 2 vectors
683 		//   vec4 y[2]; // array of 2 vectors
684 		// };
685 		"%arr2_v4float			= OpTypeArray %v4f32 %c_u32_2						\n"
686 		"%inner_struct			= OpTypeStruct %arr2_v4float %arr2_v4float			\n"
687 
688 		// struct outer_struct {
689 		//   inner_struct r[2][2];
690 		// };
691 		"%arr2_inner_struct		= OpTypeArray %inner_struct %c_u32_2				\n"
692 		"%mat2x2_inner_struct	= OpTypeArray %arr2_inner_struct %c_u32_2			\n"
693 		"%outer_struct			= OpTypeStruct %mat2x2_inner_struct					\n"
694 
695 		// struct input_buffer {
696 		//   outer_struct a;
697 		//   outer_struct b;
698 		// }
699 		"%input_buffer			= OpTypeStruct %outer_struct %outer_struct			\n"
700 
701 		// struct output_struct {
702 		//   float out;
703 		// }
704 		"%output_buffer			= OpTypeStruct %f32									\n"
705 
706 		///////////////////
707 		// POINTER TYPES //
708 		///////////////////
709 		"%output_buffer_ptr		= OpTypePointer StorageBuffer %output_buffer		\n"
710 		"%input_buffer_ptr		= OpTypePointer StorageBuffer %input_buffer			\n"
711 		"%outer_struct_ptr		= OpTypePointer StorageBuffer %outer_struct			\n"
712 		"%mat2x2_ptr			= OpTypePointer StorageBuffer %mat2x2_inner_struct	\n"
713 		"%arr2_ptr				= OpTypePointer StorageBuffer %arr2_inner_struct	\n"
714 		"%inner_struct_ptr		= OpTypePointer StorageBuffer %inner_struct			\n"
715 		"%arr_v4f32_ptr			= OpTypePointer StorageBuffer %arr2_v4float			\n"
716 		"%v4f32_ptr				= OpTypePointer StorageBuffer %v4f32				\n"
717 		"%sb_f32ptr				= OpTypePointer StorageBuffer %f32					\n"
718 
719 		///////////////
720 		// VARIABLES //
721 		///////////////
722 		"%id					= OpVariable %uvec3ptr			Input				\n"
723 		"%outdata				= OpVariable %output_buffer_ptr	StorageBuffer		\n"
724 	);
725 
726 	const string inputABVariables (
727 		"%inputA				= OpVariable %outer_struct_ptr	StorageBuffer		\n"
728 		"%inputB				= OpVariable %outer_struct_ptr	StorageBuffer		\n"
729 	);
730 
731 	const string inputCVariables (
732 		"%inputC				= OpVariable %input_buffer_ptr	StorageBuffer		\n"
733 	);
734 
735 	const string inputCIntermediates (
736 		// Here are the 2 nested structures within InputC.
737 		"%inputC_a				= OpAccessChain %outer_struct_ptr %inputC %c_i32_0\n"
738 		"%inputC_b				= OpAccessChain %outer_struct_ptr %inputC %c_i32_1\n"
739 	);
740 
741 	const StringTemplate shaderTemplate (
742 		"OpCapability Shader\n"
743 
744 		"${extra_capability}\n"
745 
746 		"OpExtension \"SPV_KHR_variable_pointers\"\n"
747 		"OpExtension \"SPV_KHR_storage_buffer_storage_class\"\n"
748 		"OpMemoryModel Logical GLSL450\n"
749 		"OpEntryPoint GLCompute %main \"main\" %id\n"
750 		"OpExecutionMode %main LocalSize 1 1 1\n"
751 
752 		"OpSource GLSL 430\n"
753 		"OpName %main           \"main\"\n"
754 		"OpName %id             \"gl_GlobalInvocationID\"\n"
755 
756 		+ commonDecorations +
757 
758 		"${input_decorations}\n"
759 
760 		+ string(getComputeAsmCommonTypes())
761 
762 		+ types +
763 
764 		"${input_variables}\n"
765 
766 		// These selector functions return variable pointers.
767 		// These functions are used by tests that use OpFunctionCall to obtain the variable pointer
768 		"%selector_func_type	= OpTypeFunction ${selected_type} %bool ${selected_type} ${selected_type}\n"
769 		"%choose_input_func		= OpFunction ${selected_type} None %selector_func_type\n"
770 		"%choose_first_param	= OpFunctionParameter %bool\n"
771 		"%first_param			= OpFunctionParameter ${selected_type}\n"
772 		"%second_param			= OpFunctionParameter ${selected_type}\n"
773 		"%selector_func_begin	= OpLabel\n"
774 		"%result_ptr			= OpSelect ${selected_type} %choose_first_param %first_param %second_param\n"
775 		"OpReturnValue %result_ptr\n"
776 		"OpFunctionEnd\n"
777 
778 		// main function is the entry_point
779 		"%main					= OpFunction %void None %voidf\n"
780 		"%label					= OpLabel\n"
781 
782 		"${input_intermediates}\n"
783 
784 		// Define the 2 pointers from which we're going to choose one.
785 		"${a_loc} \n"
786 		"${b_loc} \n"
787 
788 		// Choose between the 2 pointers / variable pointers.
789 		"${selection_strategy} \n"
790 
791 		// OpAccessChain into the variable pointer until you get to the float.
792 		"%result_loc			= OpAccessChain %sb_f32ptr %var_ptr  ${remaining_indexes} \n"
793 
794 		// Now load from the result_loc
795 		"%result_val			= OpLoad %f32 %result_loc \n"
796 
797 		// Store the chosen value to the output buffer.
798 		"%outdata_loc			= OpAccessChain %sb_f32ptr %outdata %c_i32_0\n"
799 		"						  OpStore %outdata_loc %result_val\n"
800 		"						  OpReturn\n"
801 		"						  OpFunctionEnd\n");
802 
803 	for (int isSingleInputBuffer = 0 ; isSingleInputBuffer < 2; ++isSingleInputBuffer)
804 	{
805 		// Set the proper extension features required for the test
806 		if (isSingleInputBuffer)
807 			requiredFeatures.extVariablePointers	= EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS_STORAGEBUFFER;
808 		else
809 			requiredFeatures.extVariablePointers	= EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS;
810 
811 		for (int selectInputA = 0; selectInputA < 2; ++selectInputA)
812 		{
813 			const string extraCap					= isSingleInputBuffer	? "OpCapability VariablePointersStorageBuffer\n" : "OpCapability VariablePointers\n";
814 			const string inputDecorations			= isSingleInputBuffer	? inputCDecorations								 : inputABDecorations;
815 			const string inputVariables				= isSingleInputBuffer	? inputCVariables								 : inputABVariables;
816 			const string inputIntermediates			= isSingleInputBuffer	? inputCIntermediates							 : "";
817 			const vector<float>& selectedInput		= isSingleInputBuffer	? inputC										 : (selectInputA ? inputA : inputB);
818 			const string bufferType					= isSingleInputBuffer	? "single_buffer_"								 : "two_buffers_";
819 			const string baseA						= isSingleInputBuffer	? "%inputC_a"									 : "%inputA";
820 			const string baseB						= isSingleInputBuffer	? "%inputC_b"									 : "%inputB";
821 			const string selectedInputStr			= selectInputA			? "first_input"									 : "second_input";
822 			const string spirvSelectInputA			= selectInputA			? "%c_bool_true"								 : "%c_bool_false";
823 			const int outerStructIndex				= isSingleInputBuffer	? (selectInputA ? 0 : 1)						 : 0;
824 
825 			// The indexes chosen at each level. At any level, any given offset is exercised.
826 			// outerStructIndex is 0 for inputA and inputB (because outer_struct has only 1 member).
827 			// outerStructIndex is 0 for member <a> of inputC and 1 for member <b>.
828 			const int indexesForLevel[numLevels][6]= {{outerStructIndex, 0, 0, 0, 0, 1},
829 													  {outerStructIndex, 1, 0, 1, 0, 2},
830 													  {outerStructIndex, 0, 1, 0, 1, 3},
831 													  {outerStructIndex, 1, 1, 1, 0, 0},
832 													  {outerStructIndex, 0, 0, 1, 1, 1},
833 													  {outerStructIndex, 1, 0, 0, 0, 2},
834 													  {outerStructIndex, 1, 1, 1, 1, 3}};
835 
836 			const string indexLevelNames[]		= {"_outer_struct_", "_matrices_", "_arrays_", "_inner_structs_", "_vec4arr_", "_vec4_", "_float_"};
837 			const string inputALocations[]		= {	"",
838 												"%a_loc = OpAccessChain %mat2x2_ptr       " + baseA + " %c_i32_0",
839 												"%a_loc = OpAccessChain %arr2_ptr         " + baseA + " %c_i32_0 %c_i32_0",
840 												"%a_loc = OpAccessChain %inner_struct_ptr " + baseA + " %c_i32_0 %c_i32_1 %c_i32_1",
841 												"%a_loc = OpAccessChain %arr_v4f32_ptr    " + baseA + " %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
842 												"%a_loc = OpAccessChain %v4f32_ptr        " + baseA + " %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
843 												"%a_loc = OpAccessChain %sb_f32ptr        " + baseA + " %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3"};
844 
845 			const string inputBLocations[]		= {	"",
846 												"%b_loc = OpAccessChain %mat2x2_ptr       " + baseB + " %c_i32_0",
847 												"%b_loc = OpAccessChain %arr2_ptr         " + baseB + " %c_i32_0 %c_i32_0",
848 												"%b_loc = OpAccessChain %inner_struct_ptr " + baseB + " %c_i32_0 %c_i32_1 %c_i32_1",
849 												"%b_loc = OpAccessChain %arr_v4f32_ptr    " + baseB + " %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
850 												"%b_loc = OpAccessChain %v4f32_ptr        " + baseB + " %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
851 												"%b_loc = OpAccessChain %sb_f32ptr        " + baseB + " %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3"};
852 
853 			const string inputAPtrAccessChain[]	= {	"",
854 												"%a_loc = OpPtrAccessChain %mat2x2_ptr       " + baseA + " %c_i32_0 %c_i32_0",
855 												"%a_loc = OpPtrAccessChain %arr2_ptr         " + baseA + " %c_i32_0 %c_i32_0 %c_i32_0",
856 												"%a_loc = OpPtrAccessChain %inner_struct_ptr " + baseA + " %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1",
857 												"%a_loc = OpPtrAccessChain %arr_v4f32_ptr    " + baseA + " %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
858 												"%a_loc = OpPtrAccessChain %v4f32_ptr        " + baseA + " %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
859 												// Next case emulates:
860 												// %a_loc = OpPtrAccessChain %sb_f32ptr          baseA     %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3
861 												// But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
862 												//    %a_loc_arr is a pointer to an array that we want to index with 1.
863 												// But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
864 												// get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
865 												"%a_loc_arr = OpPtrAccessChain %arr_v4f32_ptr " + baseA + " %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
866 												"%a_loc_first_elem = OpAccessChain %v4f32_ptr %a_loc_arr %c_i32_0 "
867 												"%a_loc = OpPtrAccessChain %sb_f32ptr %a_loc_first_elem %c_i32_1 %c_i32_3"};
868 
869 			const string inputBPtrAccessChain[]	= {	"",
870 												"%b_loc = OpPtrAccessChain %mat2x2_ptr       " + baseB + " %c_i32_0 %c_i32_0",
871 												"%b_loc = OpPtrAccessChain %arr2_ptr         " + baseB + " %c_i32_0 %c_i32_0 %c_i32_0",
872 												"%b_loc = OpPtrAccessChain %inner_struct_ptr " + baseB + " %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1",
873 												"%b_loc = OpPtrAccessChain %arr_v4f32_ptr    " + baseB + " %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
874 												"%b_loc = OpPtrAccessChain %v4f32_ptr        " + baseB + " %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
875 												// Next case emulates:
876 												// %b_loc = OpPtrAccessChain %sb_f32ptr          basseB     %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3
877 												// But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
878 												//    %b_loc_arr is a pointer to an array that we want to index with 1.
879 												// But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
880 												// get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
881 												"%b_loc_arr = OpPtrAccessChain %arr_v4f32_ptr " + baseB + " %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
882 												"%b_loc_first_elem = OpAccessChain %v4f32_ptr %b_loc_arr %c_i32_0 "
883 												"%b_loc = OpPtrAccessChain %sb_f32ptr %b_loc_first_elem %c_i32_1 %c_i32_3"};
884 
885 
886 			const string remainingIndexesAtLevel[]= {"%c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
887 													 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_2",
888 													 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_3",
889 													 "%c_i32_1 %c_i32_0 %c_i32_0",
890 													 "%c_i32_1 %c_i32_1",
891 													 "%c_i32_2",
892 													 ""};
893 
894 			const string pointerTypeAtLevel[] = {"%outer_struct_ptr", "%mat2x2_ptr", "%arr2_ptr", "%inner_struct_ptr", "%arr_v4f32_ptr", "%v4f32_ptr", "%sb_f32ptr"};
895 			const string baseANameAtLevel[]	  = {baseA, "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc"};
896 			const string baseBNameAtLevel[]	  = {baseB, "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc"};
897 
898 			for (int indexLevel = 0; indexLevel < numLevels; ++indexLevel)
899 			{
900 				const int baseOffset	= getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
901 																			indexesForLevel[indexLevel][1],
902 																			indexesForLevel[indexLevel][2],
903 																			indexesForLevel[indexLevel][3],
904 																			indexesForLevel[indexLevel][4],
905 																			indexesForLevel[indexLevel][5]);
906 				// Use OpSelect to choose between 2 pointers
907 				{
908 					ComputeShaderSpec				spec;
909 					map<string, string>				specs;
910 					string opCodeForTests			= "opselect";
911 					string name						= opCodeForTests + indexLevelNames[indexLevel] + bufferType + selectedInputStr;
912 					specs["extra_capability"]		= extraCap;
913 					specs["input_decorations"]		= inputDecorations;
914 					specs["input_variables"]		= inputVariables;
915 					specs["input_intermediates"]	= inputIntermediates;
916 					specs["selected_type"]			= pointerTypeAtLevel[indexLevel];
917 					specs["select_inputA"]			= spirvSelectInputA;
918 					specs["a_loc"]					= inputALocations[indexLevel];
919 					specs["b_loc"]					= inputBLocations[indexLevel];
920 					specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
921 					specs["selection_strategy"]		= "%var_ptr	= OpSelect "
922 														+ pointerTypeAtLevel[indexLevel] + " "
923 														+ spirvSelectInputA + " "
924 														+ baseANameAtLevel[indexLevel] + " "
925 														+ baseBNameAtLevel[indexLevel] + "\n";
926 					expectedOutput[0]				= selectedInput[baseOffset];
927 					spec.assembly					= shaderTemplate.specialize(specs);
928 					spec.numWorkGroups				= IVec3(1, 1, 1);
929 					spec.requestedVulkanFeatures	= requiredFeatures;
930 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
931 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
932 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputC)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
933 					spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
934 					spec.extensions.push_back("VK_KHR_variable_pointers");
935 					group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
936 				}
937 
938 				// Use OpFunctionCall to choose between 2 pointers
939 				{
940 					ComputeShaderSpec				spec;
941 					map<string, string>				specs;
942 					string opCodeForTests			= "opfunctioncall";
943 					string name						= opCodeForTests + indexLevelNames[indexLevel] + bufferType + selectedInputStr;
944 					specs["extra_capability"]		= extraCap;
945 					specs["input_decorations"]		= inputDecorations;
946 					specs["input_variables"]		= inputVariables;
947 					specs["input_intermediates"]	= inputIntermediates;
948 					specs["selected_type"]			= pointerTypeAtLevel[indexLevel];
949 					specs["select_inputA"]			= spirvSelectInputA;
950 					specs["a_loc"]					= inputALocations[indexLevel];
951 					specs["b_loc"]					= inputBLocations[indexLevel];
952 					specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
953 					specs["selection_strategy"]		= "%var_ptr	= OpFunctionCall "
954 														+ pointerTypeAtLevel[indexLevel]
955 														+ " %choose_input_func "
956 														+ spirvSelectInputA + " "
957 														+ baseANameAtLevel[indexLevel] + " "
958 														+ baseBNameAtLevel[indexLevel] + "\n";
959 					expectedOutput[0]				= selectedInput[baseOffset];
960 					spec.assembly					= shaderTemplate.specialize(specs);
961 					spec.numWorkGroups				= IVec3(1, 1, 1);
962 					spec.requestedVulkanFeatures	= requiredFeatures;
963 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
964 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
965 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputC)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
966 					spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
967 					spec.extensions.push_back("VK_KHR_variable_pointers");
968 					group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
969 				}
970 
971 				// Use OpPhi to choose between 2 pointers
972 				{
973 
974 					ComputeShaderSpec				spec;
975 					map<string, string>				specs;
976 					string opCodeForTests			= "opphi";
977 					string name						= opCodeForTests + indexLevelNames[indexLevel] + bufferType + selectedInputStr;
978 					specs["extra_capability"]		= extraCap;
979 					specs["input_decorations"]		= inputDecorations;
980 					specs["input_variables"]		= inputVariables;
981 					specs["input_intermediates"]	= inputIntermediates;
982 					specs["selected_type"]			= pointerTypeAtLevel[indexLevel];
983 					specs["select_inputA"]			= spirvSelectInputA;
984 					specs["a_loc"]					= inputALocations[indexLevel];
985 					specs["b_loc"]					= inputBLocations[indexLevel];
986 					specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
987 					specs["selection_strategy"]		=
988 									"				  OpSelectionMerge %end_label None\n"
989 									"				  OpBranchConditional " + spirvSelectInputA + " %take_input_a %take_input_b\n"
990 									"%take_input_a	= OpLabel\n"
991 									"				  OpBranch %end_label\n"
992 									"%take_input_b	= OpLabel\n"
993 									"			      OpBranch %end_label\n"
994 									"%end_label		= OpLabel\n"
995 									"%var_ptr		= OpPhi "
996 														+ pointerTypeAtLevel[indexLevel] + " "
997 														+ baseANameAtLevel[indexLevel]
998 														+ " %take_input_a "
999 														+ baseBNameAtLevel[indexLevel]
1000 														+ " %take_input_b\n";
1001 					expectedOutput[0]				= selectedInput[baseOffset];
1002 					spec.assembly					= shaderTemplate.specialize(specs);
1003 					spec.numWorkGroups				= IVec3(1, 1, 1);
1004 					spec.requestedVulkanFeatures	= requiredFeatures;
1005 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1006 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1007 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputC)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1008 					spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
1009 					spec.extensions.push_back("VK_KHR_variable_pointers");
1010 					group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
1011 				}
1012 
1013 				// Use OpCopyObject to get variable pointers
1014 				{
1015 					ComputeShaderSpec				spec;
1016 					map<string, string>				specs;
1017 					string opCodeForTests			= "opcopyobject";
1018 					string name						= opCodeForTests + indexLevelNames[indexLevel] + bufferType + selectedInputStr;
1019 					specs["extra_capability"]		= extraCap;
1020 					specs["input_decorations"]		= inputDecorations;
1021 					specs["input_variables"]		= inputVariables;
1022 					specs["input_intermediates"]	= inputIntermediates;
1023 					specs["selected_type"]			= pointerTypeAtLevel[indexLevel];
1024 					specs["select_inputA"]			= spirvSelectInputA;
1025 					specs["a_loc"]					= inputALocations[indexLevel];
1026 					specs["b_loc"]					= inputBLocations[indexLevel];
1027 					specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
1028 					specs["selection_strategy"]		=
1029 										"%in_a_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseANameAtLevel[indexLevel] + "\n"
1030 										"%in_b_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseBNameAtLevel[indexLevel] + "\n"
1031 										"%var_ptr	= OpSelect " + pointerTypeAtLevel[indexLevel] + " " + spirvSelectInputA + " %in_a_copy %in_b_copy\n";
1032 					expectedOutput[0]				= selectedInput[baseOffset];
1033 					spec.assembly					= shaderTemplate.specialize(specs);
1034 					spec.numWorkGroups				= IVec3(1, 1, 1);
1035 					spec.requestedVulkanFeatures	= requiredFeatures;
1036 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1037 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1038 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputC)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1039 					spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
1040 					spec.extensions.push_back("VK_KHR_variable_pointers");
1041 					group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
1042 				}
1043 
1044 				// Use OpPtrAccessChain to get variable pointers
1045 				{
1046 					ComputeShaderSpec				spec;
1047 					map<string, string>				specs;
1048 					string opCodeForTests			= "opptraccesschain";
1049 					string name						= opCodeForTests + indexLevelNames[indexLevel] + bufferType + selectedInputStr;
1050 					specs["extra_capability"]		= extraCap;
1051 					specs["input_decorations"]		= inputDecorations;
1052 					specs["input_variables"]		= inputVariables;
1053 					specs["input_intermediates"]	= inputIntermediates;
1054 					specs["selected_type"]			= pointerTypeAtLevel[indexLevel];
1055 					specs["select_inputA"]			= spirvSelectInputA;
1056 					specs["a_loc"]					= inputAPtrAccessChain[indexLevel];
1057 					specs["b_loc"]					= inputBPtrAccessChain[indexLevel];
1058 					specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
1059 					specs["selection_strategy"]		= "%var_ptr	= OpSelect "
1060 														+ pointerTypeAtLevel[indexLevel] + " "
1061 														+ spirvSelectInputA + " "
1062 														+ baseANameAtLevel[indexLevel] + " "
1063 														+ baseBNameAtLevel[indexLevel] + "\n";
1064 					expectedOutput[0]				= selectedInput[baseOffset];
1065 					spec.assembly					= shaderTemplate.specialize(specs);
1066 					spec.numWorkGroups				= IVec3(1, 1, 1);
1067 					spec.requestedVulkanFeatures	= requiredFeatures;
1068 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1069 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1070 					spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputC)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1071 					spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
1072 					spec.extensions.push_back("VK_KHR_variable_pointers");
1073 					group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
1074 				}
1075 			}
1076 		}
1077 	}
1078 }
1079 
addNullptrVariablePointersComputeGroup(tcu::TestCaseGroup * group)1080 void addNullptrVariablePointersComputeGroup (tcu::TestCaseGroup* group)
1081 {
1082 	tcu::TestContext&				testCtx					= group->getTestContext();
1083 	float							someFloat				= 78;
1084 	vector<float>					input					(1, someFloat);
1085 	vector<float>					expectedOutput			(1, someFloat);
1086 	VulkanFeatures					requiredFeatures;
1087 
1088 	// Requires the variable pointers feature.
1089 	requiredFeatures.extVariablePointers	= EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS;
1090 
1091 	const string decorations (
1092 		// Decorations
1093 		"OpDecorate %id BuiltIn GlobalInvocationId		\n"
1094 		"OpDecorate %input DescriptorSet 0				\n"
1095 		"OpDecorate %outdata DescriptorSet 0			\n"
1096 		"OpDecorate %input Binding 0					\n"
1097 		"OpDecorate %outdata Binding 1					\n"
1098 
1099 		// Set the Block decoration
1100 		"OpDecorate %float_struct	Block				\n"
1101 
1102 		// Set the Offsets
1103 		"OpMemberDecorate %float_struct 0 Offset 0		\n"
1104 	);
1105 
1106 	const string types (
1107 		///////////
1108 		// TYPES //
1109 		///////////
1110 		// struct float_struct {
1111 		//   float x;
1112 		// };
1113 		"%float_struct		= OpTypeStruct %f32											\n"
1114 
1115 		///////////////////
1116 		// POINTER TYPES //
1117 		///////////////////
1118 		"%float_struct_ptr	= OpTypePointer StorageBuffer %float_struct					\n"
1119 		"%sb_f32ptr			= OpTypePointer StorageBuffer %f32							\n"
1120 		"%func_f32ptrptr	= OpTypePointer Function %sb_f32ptr							\n"
1121 
1122 		///////////////
1123 		// CONSTANTS //
1124 		///////////////
1125 		"%c_bool_true		= OpConstantTrue	%bool									\n"
1126 		"%c_i32_0			= OpConstant		%i32		0							\n"
1127 		"%c_null_ptr		= OpConstantNull	%sb_f32ptr								\n"
1128 
1129 		///////////////
1130 		// VARIABLES //
1131 		///////////////
1132 		"%id				= OpVariable %uvec3ptr			Input						\n"
1133 		"%input				= OpVariable %float_struct_ptr	StorageBuffer				\n"
1134 		"%outdata			= OpVariable %float_struct_ptr	StorageBuffer				\n"
1135 	);
1136 
1137 	const StringTemplate shaderTemplate (
1138 		"OpCapability Shader\n"
1139 		"OpCapability VariablePointers\n"
1140 
1141 		"OpExtension \"SPV_KHR_variable_pointers\"\n"
1142 		"OpExtension \"SPV_KHR_storage_buffer_storage_class\"\n"
1143 		"OpMemoryModel Logical GLSL450\n"
1144 		"OpEntryPoint GLCompute %main \"main\" %id\n"
1145 		"OpExecutionMode %main LocalSize 1 1 1\n"
1146 
1147 		"OpSource GLSL 430\n"
1148 		"OpName %main           \"main\"\n"
1149 		"OpName %id             \"gl_GlobalInvocationID\"\n"
1150 
1151 		+ decorations
1152 
1153 		+ string(getComputeAsmCommonTypes())
1154 
1155 		+ types +
1156 
1157 		// main function is the entry_point
1158 		"%main					= OpFunction %void None %voidf\n"
1159 		"%label					= OpLabel\n"
1160 
1161 		// Note that the Variable Pointers extension allows creation
1162 		// of a pointer variable with storage class of Private or Function.
1163 		"%f32_ptr_var		    = OpVariable %func_f32ptrptr Function %c_null_ptr\n"
1164 
1165 		"%input_loc				= OpAccessChain %sb_f32ptr %input %c_i32_0\n"
1166 		"%output_loc			= OpAccessChain %sb_f32ptr %outdata %c_i32_0\n"
1167 
1168 		"${NullptrTestingStrategy}\n"
1169 
1170 		"						  OpReturn\n"
1171 		"						  OpFunctionEnd\n");
1172 
1173 	// f32_ptr_var has been inintialized to NULL.
1174 	// Now set it to point to the float variable that holds the input value
1175 	{
1176 		ComputeShaderSpec				spec;
1177 		map<string, string>				specs;
1178 		string name						= "opvariable_initialized_null";
1179 		specs["NullptrTestingStrategy"]	=
1180 							"                  OpStore %f32_ptr_var %input_loc       \n"
1181 							"%loaded_f32_ptr = OpLoad  %sb_f32ptr   %f32_ptr_var    \n"
1182 							"%loaded_f32     = OpLoad  %f32         %loaded_f32_ptr \n"
1183 							"                  OpStore %output_loc  %loaded_f32     \n";
1184 
1185 		spec.assembly					= shaderTemplate.specialize(specs);
1186 		spec.numWorkGroups				= IVec3(1, 1, 1);
1187 		spec.requestedVulkanFeatures	= requiredFeatures;
1188 		spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(input)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1189 		spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
1190 		spec.extensions.push_back("VK_KHR_variable_pointers");
1191 		group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
1192 	}
1193 	// Use OpSelect to choose between nullptr and valid pointer. Since we can't dereference nullptr,
1194 	// it is forced to always choose the valid pointer.
1195 	{
1196 		ComputeShaderSpec				spec;
1197 		map<string, string>				specs;
1198 		string name						= "opselect_null_or_valid_ptr";
1199 		specs["NullptrTestingStrategy"]	=
1200 							"%selected_ptr = OpSelect %sb_f32ptr %c_bool_true %input_loc %c_null_ptr\n"
1201 							"%loaded_var = OpLoad %f32 %selected_ptr\n"
1202 							"OpStore %output_loc %loaded_var\n";
1203 
1204 		spec.assembly					= shaderTemplate.specialize(specs);
1205 		spec.numWorkGroups				= IVec3(1, 1, 1);
1206 		spec.requestedVulkanFeatures	= requiredFeatures;
1207 		spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(input)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1208 		spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
1209 		spec.extensions.push_back("VK_KHR_variable_pointers");
1210 		group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
1211 	}
1212 }
1213 
addVariablePointersGraphicsGroup(tcu::TestCaseGroup * testGroup)1214 void addVariablePointersGraphicsGroup (tcu::TestCaseGroup* testGroup)
1215 {
1216 	tcu::TestContext&				testCtx					= testGroup->getTestContext();
1217 	de::Random						rnd						(deStringHash(testGroup->getName()));
1218 	map<string, string>				fragments;
1219 	RGBA							defaultColors[4];
1220 	vector<string>					extensions;
1221 	const int						seed					= testCtx.getCommandLine().getBaseSeed();
1222 	const int						numMuxes				= 100;
1223 	const std::string				numMuxesStr				= "100";
1224 	vector<float>					inputAFloats			(2*numMuxes, 0);
1225 	vector<float>					inputBFloats			(2*numMuxes, 0);
1226 	vector<float>					inputSFloats			(numMuxes, 0);
1227 	vector<float>					AmuxAOutputFloats		(numMuxes, 0);
1228 	vector<float>					AmuxBOutputFloats		(numMuxes, 0);
1229 	vector<float>					incrAmuxAOutputFloats	(numMuxes, 0);
1230 	vector<float>					incrAmuxBOutputFloats	(numMuxes, 0);
1231 	VulkanFeatures					requiredFeatures;
1232 
1233 	extensions.push_back("VK_KHR_variable_pointers");
1234 	getDefaultColors(defaultColors);
1235 
1236 	// Each output entry is chosen as follows: ( 0 <= i < numMuxes)
1237 	// 1) For tests with one input buffer:  output[i] = (s[i] < 0) ? A[2*i] : A[2*i+1];
1238 	// 2) For tests with two input buffers: output[i] = (s[i] < 0) ? A[i]   : B[i];
1239 
1240 	fillRandomScalars(rnd, -100.f, 100.f, &inputAFloats[0], 2*numMuxes);
1241 	fillRandomScalars(rnd, -100.f, 100.f, &inputBFloats[0], 2*numMuxes);
1242 
1243 	// We want to guarantee that the S input has some positive and some negative values.
1244 	// We choose random negative numbers for the first half, random positive numbers for the second half, and then shuffle.
1245 	fillRandomScalars(rnd, -100.f, -1.f , &inputSFloats[0], numMuxes / 2);
1246 	fillRandomScalars(rnd, 1.f   , 100.f, &inputSFloats[numMuxes / 2], numMuxes / 2);
1247 	de::Random(seed).shuffle(inputSFloats.begin(), inputSFloats.end());
1248 
1249 	for (size_t i = 0; i < numMuxes; ++i)
1250 	{
1251 		AmuxAOutputFloats[i]	 = (inputSFloats[i] < 0) ? inputAFloats[2*i]	 : inputAFloats[2*i+1];
1252 		AmuxBOutputFloats[i]	 = (inputSFloats[i] < 0) ? inputAFloats[i]		 : inputBFloats[i];
1253 		incrAmuxAOutputFloats[i] = (inputSFloats[i] < 0) ? 1 + inputAFloats[2*i] : 1 + inputAFloats[2*i+1];
1254 		incrAmuxBOutputFloats[i] = (inputSFloats[i] < 0) ? 1 + inputAFloats[i]	 : 1 + inputBFloats[i];
1255 	}
1256 
1257 	fragments["extension"]		= "OpExtension \"SPV_KHR_variable_pointers\"\n"
1258 								  "OpExtension \"SPV_KHR_storage_buffer_storage_class\"\n";
1259 
1260 	const StringTemplate preMain		(
1261 		"%c_i32_limit = OpConstant %i32 " + numMuxesStr + "\n"
1262 		"     %sb_f32 = OpTypePointer StorageBuffer %f32\n"
1263 		"     %ra_f32 = OpTypeRuntimeArray %f32\n"
1264 		"        %buf = OpTypeStruct %ra_f32\n"
1265 		"     %sb_buf = OpTypePointer StorageBuffer %buf\n"
1266 
1267 		" ${ExtraTypes}"
1268 
1269 		" ${ExtraGlobalScopeVars}"
1270 
1271 		"   %indata_a = OpVariable %sb_buf StorageBuffer\n"
1272 		"   %indata_b = OpVariable %sb_buf StorageBuffer\n"
1273 		"   %indata_s = OpVariable %sb_buf StorageBuffer\n"
1274 		"    %outdata = OpVariable %sb_buf StorageBuffer\n"
1275 
1276 		" ${ExtraFunctions} ");
1277 
1278 	const std::string selectorFunction	(
1279 		// We're going to put the "selector" function here.
1280 		// This function type is needed for tests that use OpFunctionCall.
1281 		"%selector_func_type	= OpTypeFunction %sb_f32 %bool %sb_f32 %sb_f32\n"
1282 		"%choose_input_func		= OpFunction %sb_f32 None %selector_func_type\n"
1283 		"%is_neg_param			= OpFunctionParameter %bool\n"
1284 		"%first_ptr_param		= OpFunctionParameter %sb_f32\n"
1285 		"%second_ptr_param		= OpFunctionParameter %sb_f32\n"
1286 		"%selector_func_begin	= OpLabel\n"
1287 		"%result_ptr			= OpSelect %sb_f32 %is_neg_param %first_ptr_param %second_ptr_param\n"
1288 		"OpReturnValue %result_ptr\n"
1289 		"OpFunctionEnd\n");
1290 
1291 	const StringTemplate decoration		(
1292 		"OpMemberDecorate %buf 0 Offset 0\n"
1293 		"OpDecorate %buf Block\n"
1294 		"OpDecorate %ra_f32 ArrayStride 4\n"
1295 		"OpDecorate %sb_f32 ArrayStride 4\n"
1296 		"OpDecorate %indata_a DescriptorSet 0\n"
1297 		"OpDecorate %indata_b DescriptorSet 0\n"
1298 		"OpDecorate %indata_s DescriptorSet 0\n"
1299 		"OpDecorate %outdata  DescriptorSet 0\n"
1300 		"OpDecorate %indata_a Binding 0\n"
1301 		"OpDecorate %indata_b Binding 1\n"
1302 		"OpDecorate %indata_s Binding 2\n"
1303 		"OpDecorate %outdata  Binding 3\n");
1304 
1305 	const StringTemplate testFunction	(
1306 		"%test_code		= OpFunction %v4f32 None %v4f32_v4f32_function\n"
1307 		"%param			= OpFunctionParameter %v4f32\n"
1308 		"%entry			= OpLabel\n"
1309 
1310 		"${ExtraFunctionScopeVars}"
1311 
1312 		"%i				= OpVariable %fp_i32 Function\n"
1313 
1314 		"%should_run    = OpFunctionCall %bool %isUniqueIdZero\n"
1315 		"                 OpSelectionMerge %end_if None\n"
1316 		"                 OpBranchConditional %should_run %run_test %end_if\n"
1317 
1318 		"%run_test      = OpLabel\n"
1319 		"				OpStore %i %c_i32_0\n"
1320 		"				OpBranch %loop\n"
1321 		// loop header
1322 		"%loop			= OpLabel\n"
1323 		"%15			= OpLoad %i32 %i\n"
1324 		"%lt			= OpSLessThan %bool %15 %c_i32_limit\n"
1325 		"				OpLoopMerge %merge %inc None\n"
1326 		"				OpBranchConditional %lt %write %merge\n"
1327 		// loop body
1328 		"%write				= OpLabel\n"
1329 		"%30				= OpLoad %i32 %i\n"
1330 		"%two_i				= OpIAdd %i32 %30 %30\n"
1331 		"%two_i_plus_1		= OpIAdd %i32 %two_i %c_i32_1\n"
1332 		"%loc_s_i			= OpAccessChain %sb_f32 %indata_s %c_i32_0 %30\n"
1333 		"%loc_a_i			= OpAccessChain %sb_f32 %indata_a %c_i32_0 %30\n"
1334 		"%loc_b_i			= OpAccessChain %sb_f32 %indata_b %c_i32_0 %30\n"
1335 		"%loc_a_2i			= OpAccessChain %sb_f32 %indata_a %c_i32_0 %two_i\n"
1336 		"%loc_a_2i_plus_1	= OpAccessChain %sb_f32 %indata_a %c_i32_0 %two_i_plus_1\n"
1337 		"%loc_outdata_i		= OpAccessChain %sb_f32 %outdata  %c_i32_0 %30\n"
1338 		"%val_s_i			= OpLoad %f32 %loc_s_i\n"
1339 		"%is_neg			= OpFOrdLessThan %bool %val_s_i %c_f32_0\n"
1340 
1341 		// select using a strategy.
1342 		"${ResultStrategy}"
1343 
1344 		// load through the variable pointer
1345 		"%mux_output	= OpLoad %f32 ${VarPtrName}\n"
1346 
1347 		// store to the output vector.
1348 		"				OpStore %loc_outdata_i %mux_output\n"
1349 		"				OpBranch %inc\n"
1350 		// ++i
1351 		"  %inc			= OpLabel\n"
1352 		"   %37			= OpLoad %i32 %i\n"
1353 		"   %39			= OpIAdd %i32 %37 %c_i32_1\n"
1354 		"         OpStore %i %39\n"
1355 		"         OpBranch %loop\n"
1356 
1357 		// Return and FunctionEnd
1358 		"%merge			= OpLabel\n"
1359 		"                 OpBranch %end_if\n"
1360 		"%end_if		= OpLabel\n"
1361 		"OpReturnValue %param\n"
1362 		"OpFunctionEnd\n");
1363 
1364 	const bool singleInputBuffer[] = { true, false };
1365 	for (int inputBufferTypeIndex = 0 ; inputBufferTypeIndex < 2; ++inputBufferTypeIndex)
1366 	{
1367 		const bool isSingleInputBuffer			= singleInputBuffer[inputBufferTypeIndex];
1368 		const string cap						= isSingleInputBuffer	? "OpCapability VariablePointersStorageBuffer\n" : "OpCapability VariablePointers\n";
1369 		const vector<float>& expectedOutput		= isSingleInputBuffer	? AmuxAOutputFloats		 : AmuxBOutputFloats;
1370 		const vector<float>& expectedIncrOutput = isSingleInputBuffer	? incrAmuxAOutputFloats	 : incrAmuxBOutputFloats;
1371 		const string bufferType					= isSingleInputBuffer	? "single_buffer"		 : "two_buffers";
1372 		const string muxInput1					= isSingleInputBuffer	? " %loc_a_2i "			 : " %loc_a_i ";
1373 		const string muxInput2					= isSingleInputBuffer	? " %loc_a_2i_plus_1 "	 : " %loc_b_i ";
1374 
1375 		// Set the proper extension features required for the test
1376 		if (isSingleInputBuffer)
1377 			requiredFeatures.extVariablePointers	= EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS_STORAGEBUFFER;
1378 		else
1379 			requiredFeatures.extVariablePointers	= EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS;
1380 
1381 		// All of the following tests write their results into an output SSBO, therefore they require the following features.
1382 		requiredFeatures.coreFeatures.vertexPipelineStoresAndAtomics = DE_TRUE;
1383 		requiredFeatures.coreFeatures.fragmentStoresAndAtomics		 = DE_TRUE;
1384 
1385 		{ // Variable Pointer Reads (using OpSelect)
1386 			GraphicsResources				resources;
1387 			map<string, string>				specs;
1388 			string name						= "reads_opselect_" + bufferType;
1389 			specs["ExtraTypes"]				= "";
1390 			specs["ExtraGlobalScopeVars"]	= "";
1391 			specs["ExtraFunctionScopeVars"]	= "";
1392 			specs["ExtraFunctions"]			= "";
1393 			specs["VarPtrName"]				= "%mux_output_var_ptr";
1394 			specs["ResultStrategy"]			= "%mux_output_var_ptr	= OpSelect %sb_f32 %is_neg" + muxInput1 + muxInput2 + "\n";
1395 
1396 			fragments["capability"]			= cap;
1397 			fragments["decoration"]			= decoration.specialize(specs);
1398 			fragments["pre_main"]			= preMain.specialize(specs);
1399 			fragments["testfun"]			= testFunction.specialize(specs);
1400 
1401 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1402 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1403 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1404 			resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1405 			createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1406 		}
1407 		{ // Variable Pointer Reads (using OpFunctionCall)
1408 			GraphicsResources				resources;
1409 			map<string, string>				specs;
1410 			string name						= "reads_opfunctioncall_" + bufferType;
1411 			specs["ExtraTypes"]				= "";
1412 			specs["ExtraGlobalScopeVars"]	= "";
1413 			specs["ExtraFunctionScopeVars"]	= "";
1414 			specs["ExtraFunctions"]			= selectorFunction;
1415 			specs["VarPtrName"]				= "%mux_output_var_ptr";
1416 			specs["ResultStrategy"]			= "%mux_output_var_ptr = OpFunctionCall %sb_f32 %choose_input_func %is_neg" + muxInput1 + muxInput2 + "\n";
1417 
1418 			fragments["capability"]			= cap;
1419 			fragments["decoration"]			= decoration.specialize(specs);
1420 			fragments["pre_main"]			= preMain.specialize(specs);
1421 			fragments["testfun"]			= testFunction.specialize(specs);
1422 
1423 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1424 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1425 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1426 			resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1427 			createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1428 		}
1429 		{ // Variable Pointer Reads (using OpPhi)
1430 			GraphicsResources				resources;
1431 			map<string, string>				specs;
1432 			string name						= "reads_opphi_" + bufferType;
1433 			specs["ExtraTypes"]				= "";
1434 			specs["ExtraGlobalScopeVars"]	= "";
1435 			specs["ExtraFunctionScopeVars"]	= "";
1436 			specs["ExtraFunctions"]			= "";
1437 			specs["VarPtrName"]				= "%mux_output_var_ptr";
1438 			specs["ResultStrategy"]			=
1439 				"							  OpSelectionMerge %end_label None\n"
1440 				"							  OpBranchConditional %is_neg %take_mux_input_1 %take_mux_input_2\n"
1441 				"%take_mux_input_1			= OpLabel\n"
1442 				"							  OpBranch %end_label\n"
1443 				"%take_mux_input_2			= OpLabel\n"
1444 				"						      OpBranch %end_label\n"
1445 				"%end_label					= OpLabel\n"
1446 				"%mux_output_var_ptr		= OpPhi %sb_f32" + muxInput1 + "%take_mux_input_1" + muxInput2 + "%take_mux_input_2\n";
1447 
1448 			fragments["capability"]			= cap;
1449 			fragments["decoration"]			= decoration.specialize(specs);
1450 			fragments["pre_main"]			= preMain.specialize(specs);
1451 			fragments["testfun"]			= testFunction.specialize(specs);
1452 
1453 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1454 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1455 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1456 			resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1457 			createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1458 		}
1459 		{ // Variable Pointer Reads (using OpCopyObject)
1460 			GraphicsResources				resources;
1461 			map<string, string>				specs;
1462 			string name						= "reads_opcopyobject_" + bufferType;
1463 			specs["ExtraTypes"]				= "";
1464 			specs["ExtraGlobalScopeVars"]	= "";
1465 			specs["ExtraFunctionScopeVars"]	= "";
1466 			specs["ExtraFunctions"]			= "";
1467 			specs["VarPtrName"]				= "%mux_output_var_ptr";
1468 			specs["ResultStrategy"]			=
1469 				"%mux_input_1_copy			= OpCopyObject %sb_f32" + muxInput1 + "\n"
1470 				"%mux_input_2_copy			= OpCopyObject %sb_f32" + muxInput2 + "\n"
1471 				"%mux_output_var_ptr		= OpSelect %sb_f32 %is_neg %mux_input_1_copy %mux_input_2_copy\n";
1472 
1473 			fragments["capability"]			= cap;
1474 			fragments["decoration"]			= decoration.specialize(specs);
1475 			fragments["pre_main"]			= preMain.specialize(specs);
1476 			fragments["testfun"]			= testFunction.specialize(specs);
1477 
1478 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1479 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1480 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1481 			resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1482 			createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1483 		}
1484 		{ // Test storing into Private variables.
1485 			const char* storageClasses[]		= {"Private", "Function"};
1486 			for (int classId = 0; classId < 2; ++classId)
1487 			{
1488 				GraphicsResources				resources;
1489 				map<string, string>				specs;
1490 				std::string storageClass		= storageClasses[classId];
1491 				std::string name				= "stores_" + string(de::toLower(storageClass)) + "_" + bufferType;
1492 				std::string extraVariable		= "%mux_output_copy	= OpVariable %sb_f32ptrptr " + storageClass + "\n";
1493 				specs["ExtraTypes"]				= "%sb_f32ptrptr = OpTypePointer " + storageClass + " %sb_f32\n";
1494 				specs["ExtraGlobalScopeVars"]	= (classId == 0) ? extraVariable : "";
1495 				specs["ExtraFunctionScopeVars"]	= (classId == 1) ? extraVariable : "";
1496 				specs["ExtraFunctions"]			= "";
1497 				specs["VarPtrName"]				= "%mux_output_var_ptr";
1498 				specs["ResultStrategy"]			=
1499 					"%opselect_result			= OpSelect %sb_f32 %is_neg" + muxInput1 + muxInput2 + "\n"
1500 					"							  OpStore %mux_output_copy %opselect_result\n"
1501 					"%mux_output_var_ptr		= OpLoad %sb_f32 %mux_output_copy\n";
1502 
1503 				fragments["capability"]			= cap;
1504 				fragments["decoration"]			= decoration.specialize(specs);
1505 				fragments["pre_main"]			= preMain.specialize(specs);
1506 				fragments["testfun"]			= testFunction.specialize(specs);
1507 
1508 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1509 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1510 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1511 				resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1512 				createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1513 			}
1514 		}
1515 		{ // Variable Pointer Reads (using OpPtrAccessChain)
1516 			GraphicsResources				resources;
1517 			map<string, string>				specs;
1518 			std::string name				= "reads_opptraccesschain_" + bufferType;
1519 			std::string in_1				= isSingleInputBuffer ? " %a_2i_ptr "		 : " %a_i_ptr ";
1520 			std::string in_2				= isSingleInputBuffer ? " %a_2i_plus_1_ptr " : " %b_i_ptr ";
1521 			specs["ExtraTypes"]				= "";
1522 			specs["ExtraGlobalScopeVars"]	= "";
1523 			specs["ExtraFunctionScopeVars"]	= "";
1524 			specs["ExtraFunctions"]			= "";
1525 			specs["VarPtrName"]				= "%mux_output_var_ptr";
1526 			specs["ResultStrategy"]			=
1527 					"%a_ptr					= OpAccessChain %sb_f32 %indata_a %c_i32_0 %c_i32_0\n"
1528 					"%b_ptr					= OpAccessChain %sb_f32 %indata_b %c_i32_0 %c_i32_0\n"
1529 					"%s_ptr					= OpAccessChain %sb_f32 %indata_s %c_i32_0 %c_i32_0\n"
1530 					"%out_ptr               = OpAccessChain %sb_f32 %outdata  %c_i32_0 %c_i32_0\n"
1531 					"%a_i_ptr               = OpPtrAccessChain %sb_f32 %a_ptr %30\n"
1532 					"%b_i_ptr               = OpPtrAccessChain %sb_f32 %b_ptr %30\n"
1533 					"%s_i_ptr               = OpPtrAccessChain %sb_f32 %s_ptr %30\n"
1534 					"%a_2i_ptr              = OpPtrAccessChain %sb_f32 %a_ptr %two_i\n"
1535 					"%a_2i_plus_1_ptr       = OpPtrAccessChain %sb_f32 %a_ptr %two_i_plus_1\n"
1536 					"%mux_output_var_ptr    = OpSelect %sb_f32 %is_neg " + in_1 + in_2 + "\n";
1537 
1538 			fragments["decoration"]			= decoration.specialize(specs);
1539 			fragments["pre_main"]			= preMain.specialize(specs);
1540 			fragments["testfun"]			= testFunction.specialize(specs);
1541 			fragments["capability"]			= cap;
1542 
1543 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1544 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1545 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1546 			resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1547 			createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1548 		}
1549 		{   // Variable Pointer Writes
1550 			GraphicsResources				resources;
1551 			map<string, string>				specs;
1552 			std::string	name				= "writes_" + bufferType;
1553 			specs["ExtraTypes"]				= "";
1554 			specs["ExtraGlobalScopeVars"]	= "";
1555 			specs["ExtraFunctionScopeVars"]	= "";
1556 			specs["ExtraFunctions"]			= "";
1557 			specs["VarPtrName"]				= "%mux_output_var_ptr";
1558 			specs["ResultStrategy"]			=
1559 					   "%mux_output_var_ptr = OpSelect %sb_f32 %is_neg" + muxInput1 + muxInput2 + "\n" +
1560 					   "               %val = OpLoad %f32 %mux_output_var_ptr\n"
1561 					   "        %val_plus_1 = OpFAdd %f32 %val %c_f32_1\n"
1562 					   "					  OpStore %mux_output_var_ptr %val_plus_1\n";
1563 			fragments["capability"]			= cap;
1564 			fragments["decoration"]			= decoration.specialize(specs);
1565 			fragments["pre_main"]			= preMain.specialize(specs);
1566 			fragments["testfun"]			= testFunction.specialize(specs);
1567 
1568 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1569 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1570 			resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1571 			resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedIncrOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1572 			createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1573 		}
1574 	}
1575 }
1576 
1577 // Modifies the 'red channel' of the input color to the given float value.
1578 // Returns the modified color.
getExpectedOutputColor(RGBA (& inputColors)[4],RGBA (& expectedOutputColors)[4],float val)1579 void getExpectedOutputColor(RGBA (&inputColors)[4], RGBA (&expectedOutputColors)[4], float val)
1580 {
1581 	Vec4 inColor0 = inputColors[0].toVec();
1582 	Vec4 inColor1 = inputColors[1].toVec();
1583 	Vec4 inColor2 = inputColors[2].toVec();
1584 	Vec4 inColor3 = inputColors[3].toVec();
1585 	inColor0[0] = val;
1586 	inColor1[0] = val;
1587 	inColor2[0] = val;
1588 	inColor3[0] = val;
1589 	expectedOutputColors[0] = RGBA(inColor0);
1590 	expectedOutputColors[1] = RGBA(inColor1);
1591 	expectedOutputColors[2] = RGBA(inColor2);
1592 	expectedOutputColors[3] = RGBA(inColor3);
1593 }
1594 
addTwoInputBufferReadOnlyVariablePointersGraphicsGroup(tcu::TestCaseGroup * testGroup)1595 void addTwoInputBufferReadOnlyVariablePointersGraphicsGroup (tcu::TestCaseGroup* testGroup)
1596 {
1597 	const int						numFloatsPerInput		= 64;
1598 	vector<float>					inputA					(numFloatsPerInput, 0);
1599 	vector<float>					inputB					(numFloatsPerInput, 0);
1600 	deUint32						baseOffset				= -1;
1601 	VulkanFeatures					requiredFeatures;
1602 	map<string, string>				fragments;
1603 	RGBA							defaultColors[4];
1604 	RGBA							expectedColors[4];
1605 	vector<string>					extensions;
1606 
1607 	getDefaultColors(defaultColors);
1608 
1609 	// Set the proper extension features required for the tests.
1610 	requiredFeatures.extVariablePointers = EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS;
1611 
1612 	// Set the required extension.
1613 	extensions.push_back("VK_KHR_variable_pointers");
1614 
1615 	// These tests exercise variable pointers into various levels of the following data-structure:
1616 	// struct struct inner_struct {
1617 	//   vec4 x[2]; // array of 2 vectors. Each vector is 4 floats.
1618 	//   vec4 y[2]; // array of 2 vectors. Each vector is 4 floats.
1619 	// };
1620 	//
1621 	// struct outer_struct {
1622 	//   inner_struct r[2][2];
1623 	// };
1624 	//
1625 	// The inner_struct contains 16 floats, and the outer_struct contains 64 floats.
1626 	// Therefore the input can be an array of 64 floats.
1627 
1628 	// Populate the first input (inputA) to contain:  {0, 4, ... , 252} / 255.f
1629 	// Populate the second input (inputB) to contain: {3, 7, ... , 255} / 255.f
1630 	for (size_t i = 0; i < numFloatsPerInput; ++i)
1631 	{
1632 		inputA[i] = 4*float(i) / 255;
1633 		inputB[i] = ((4*float(i)) + 3) / 255;
1634 	}
1635 
1636 	// In the following tests we use variable pointers to point to different types:
1637 	// nested structures, matrices of structures, arrays of structures, arrays of vectors, vectors of scalars, and scalars.
1638 	// outer_structure.inner_structure[?][?].x[?][?];
1639 	//   ^                    ^        ^  ^  ^ ^  ^
1640 	//
1641 	// 1. inputA						or	inputB						= nested structure
1642 	// 2. inputA.r						or	inputB.r					= matrices of structures
1643 	// 3. inputA.r[?]					or	inputB.r[?]					= arrays of structures
1644 	// 4. inputA.r[?][?]				or	inputB.r[?][?]				= structures
1645 	// 5. inputA.r[?][?].(x|y)			or	inputB.r[?][?].(x|y)		= arrays of vectors
1646 	// 6. inputA.r[?][?].(x|y)[?]		or	inputB.r[?][?].(x|y)[?]		= vectors of scalars
1647 	// 7. inputA.r[?][?].(x|y)[?][?]	or	inputB.r[?][?].(x|y)[?][?]	= scalars
1648 	const int numLevels	= 7;
1649 
1650 	fragments["capability"]		=	"OpCapability VariablePointers							\n";
1651 	fragments["extension"]		=	"OpExtension \"SPV_KHR_variable_pointers\"				\n"
1652 									"OpExtension \"SPV_KHR_storage_buffer_storage_class\"	\n";
1653 
1654 	const StringTemplate decoration		(
1655 		// Set the Offsets
1656 		"OpMemberDecorate			%outer_struct 0 Offset 0	\n"
1657 		"OpMemberDecorate			%inner_struct 0 Offset 0	\n"
1658 		"OpMemberDecorate			%inner_struct 1 Offset 32	\n"
1659 
1660 		// Set the ArrayStrides
1661 		"OpDecorate %arr2_v4float        ArrayStride 16			\n"
1662 		"OpDecorate %arr2_inner_struct   ArrayStride 64			\n"
1663 		"OpDecorate %mat2x2_inner_struct ArrayStride 128		\n"
1664 		"OpDecorate %mat2x2_ptr			 ArrayStride 128		\n"
1665 		"OpDecorate %sb_buf              ArrayStride 256		\n"
1666 		"OpDecorate %v4f32_ptr           ArrayStride 16			\n"
1667 
1668 		"OpDecorate					%outer_struct Block			\n"
1669 
1670 		"OpDecorate %in_a			DescriptorSet 0				\n"
1671 		"OpDecorate %in_b			DescriptorSet 0				\n"
1672 		"OpDecorate %in_a			Binding 0					\n"
1673 		"OpDecorate %in_b			Binding 1					\n"
1674 	);
1675 
1676 	const StringTemplate preMain		(
1677 		///////////
1678 		// TYPES //
1679 		///////////
1680 
1681 		// struct struct inner_struct {
1682 		//   vec4 x[2]; // array of 2 vectors
1683 		//   vec4 y[2]; // array of 2 vectors
1684 		// };
1685 		"%arr2_v4float			= OpTypeArray %v4f32 %c_u32_2						\n"
1686 		"%inner_struct			= OpTypeStruct %arr2_v4float %arr2_v4float			\n"
1687 
1688 		// struct outer_struct {
1689 		//   inner_struct r[2][2];
1690 		// };
1691 		"%arr2_inner_struct		= OpTypeArray %inner_struct %c_u32_2				\n"
1692 		"%mat2x2_inner_struct	= OpTypeArray %arr2_inner_struct %c_u32_2			\n"
1693 		"%outer_struct			= OpTypeStruct %mat2x2_inner_struct					\n"
1694 
1695 		///////////////////
1696 		// POINTER TYPES //
1697 		///////////////////
1698 		"%sb_buf				= OpTypePointer StorageBuffer %outer_struct			\n"
1699 		"%mat2x2_ptr			= OpTypePointer StorageBuffer %mat2x2_inner_struct	\n"
1700 		"%arr2_ptr				= OpTypePointer StorageBuffer %arr2_inner_struct	\n"
1701 		"%inner_struct_ptr		= OpTypePointer StorageBuffer %inner_struct			\n"
1702 		"%arr_v4f32_ptr			= OpTypePointer StorageBuffer %arr2_v4float			\n"
1703 		"%v4f32_ptr				= OpTypePointer StorageBuffer %v4f32				\n"
1704 		"%sb_f32ptr				= OpTypePointer StorageBuffer %f32					\n"
1705 
1706 		///////////////
1707 		// VARIABLES //
1708 		///////////////
1709 		"%in_a					= OpVariable %sb_buf StorageBuffer					\n"
1710 		"%in_b					= OpVariable %sb_buf StorageBuffer					\n"
1711 
1712 		///////////////
1713 		// CONSTANTS //
1714 		///////////////
1715 		"%c_bool_true			= OpConstantTrue %bool								\n"
1716 		"%c_bool_false			= OpConstantFalse %bool								\n"
1717 
1718 		//////////////////////
1719 		// HELPER FUNCTIONS //
1720 		//////////////////////
1721 		"${helper_functions} \n"
1722 	);
1723 
1724 	const StringTemplate selectorFunctions	(
1725 		// This selector function returns a variable pointer.
1726 		// These functions are used by tests that use OpFunctionCall to obtain the variable pointer.
1727 		"%selector_func_type	= OpTypeFunction ${selected_type} %bool ${selected_type} ${selected_type}\n"
1728 		"%choose_input_func		= OpFunction ${selected_type} None %selector_func_type\n"
1729 		"%choose_first_param	= OpFunctionParameter %bool\n"
1730 		"%first_param			= OpFunctionParameter ${selected_type}\n"
1731 		"%second_param			= OpFunctionParameter ${selected_type}\n"
1732 		"%selector_func_begin	= OpLabel\n"
1733 		"%result_ptr			= OpSelect ${selected_type} %choose_first_param %first_param %second_param\n"
1734 		"						  OpReturnValue %result_ptr\n"
1735 		"						  OpFunctionEnd\n"
1736 	);
1737 
1738 	const StringTemplate testFunction	(
1739 		"%test_code		= OpFunction %v4f32 None %v4f32_v4f32_function\n"
1740 		"%param			= OpFunctionParameter %v4f32\n"
1741 		"%entry			= OpLabel\n"
1742 
1743 		// Define base pointers for OpPtrAccessChain
1744 		"%in_a_matptr	= OpAccessChain %mat2x2_ptr %in_a %c_i32_0\n"
1745 		"%in_b_matptr	= OpAccessChain %mat2x2_ptr %in_b %c_i32_0\n"
1746 
1747 		// Define the 2 pointers from which we're going to choose one.
1748 		"${a_loc} \n"
1749 		"${b_loc} \n"
1750 
1751 		// Choose between the 2 pointers / variable pointers
1752 		"${selection_strategy} \n"
1753 
1754 		// OpAccessChain into the variable pointer until you get to the float.
1755 		"%result_loc	= OpAccessChain %sb_f32ptr %var_ptr  ${remaining_indexes} \n"
1756 
1757 		// Now load from the result_loc
1758 		"%result_val	= OpLoad %f32 %result_loc\n"
1759 
1760 		// Modify the 'RED channel' of the output color to the chosen value
1761 		"%output_color	= OpCompositeInsert %v4f32 %result_val %param 0 \n"
1762 
1763 		// Return and FunctionEnd
1764 		"OpReturnValue %output_color\n"
1765 		"OpFunctionEnd\n");
1766 
1767 	// When select is 0, the variable pointer should point to a value in the first input (inputA).
1768 	// When select is 1, the variable pointer should point to a value in the second input (inputB).
1769 	for (int selectInputA = 0; selectInputA < 2; ++selectInputA)
1770 	{
1771 		const string selectedInputStr		= selectInputA ? "first_input"	: "second_input";
1772 		const string spirvSelectInputA		= selectInputA ? "%c_bool_true"	: "%c_bool_false";
1773 		vector<float>& selectedInput		= selectInputA ? inputA			: inputB;
1774 
1775 		// The indexes chosen at each level. At any level, any given offset is exercised.
1776 		// The first index is always zero as the outer structure has only 1 member.
1777 		const int indexesForLevel[numLevels][6]= {{0, 0, 0, 0, 0, 1},
1778 												  {0, 1, 0, 1, 0, 2},
1779 												  {0, 0, 1, 0, 1, 3},
1780 												  {0, 1, 1, 1, 0, 0},
1781 												  {0, 0, 0, 1, 1, 1},
1782 												  {0, 1, 0, 0, 0, 2},
1783 												  {0, 1, 1, 1, 1, 3}};
1784 
1785 		const string indexLevelNames[]		= {"_outer_struct_", "_matrices_", "_arrays_", "_inner_structs_", "_vec4arr_", "_vec4_", "_float_"};
1786 		const string inputALocations[]		= {	"",
1787 											"%a_loc = OpAccessChain %mat2x2_ptr       %in_a %c_i32_0",
1788 											"%a_loc = OpAccessChain %arr2_ptr         %in_a %c_i32_0 %c_i32_0",
1789 											"%a_loc = OpAccessChain %inner_struct_ptr %in_a %c_i32_0 %c_i32_1 %c_i32_1",
1790 											"%a_loc = OpAccessChain %arr_v4f32_ptr    %in_a %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
1791 											"%a_loc = OpAccessChain %v4f32_ptr        %in_a %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
1792 											"%a_loc = OpAccessChain %sb_f32ptr        %in_a %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3"};
1793 
1794 		const string inputBLocations[]		= {	"",
1795 											"%b_loc = OpAccessChain %mat2x2_ptr       %in_b %c_i32_0",
1796 											"%b_loc = OpAccessChain %arr2_ptr         %in_b %c_i32_0 %c_i32_0",
1797 											"%b_loc = OpAccessChain %inner_struct_ptr %in_b %c_i32_0 %c_i32_1 %c_i32_1",
1798 											"%b_loc = OpAccessChain %arr_v4f32_ptr    %in_b %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
1799 											"%b_loc = OpAccessChain %v4f32_ptr        %in_b %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
1800 											"%b_loc = OpAccessChain %sb_f32ptr        %in_b %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3"};
1801 
1802 		const string inputAPtrAccessChain[]	= {	"",
1803 											"%a_loc = OpPtrAccessChain %mat2x2_ptr       %in_a_matptr %c_i32_0",
1804 											"%a_loc = OpPtrAccessChain %arr2_ptr         %in_a_matptr %c_i32_0 %c_i32_0",
1805 											"%a_loc = OpPtrAccessChain %inner_struct_ptr %in_a_matptr %c_i32_0 %c_i32_1 %c_i32_1",
1806 											"%a_loc = OpPtrAccessChain %arr_v4f32_ptr    %in_a_matptr %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
1807 											"%a_loc = OpPtrAccessChain %v4f32_ptr        %in_a_matptr %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
1808 											// Next case emulates:
1809 											// %a_loc = OpPtrAccessChain %sb_f32ptr      %in_a_matptr %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3
1810 											// But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
1811 											//    %a_loc_arr is a pointer to an array that we want to index with 1.
1812 											// But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
1813 											// get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
1814 											"%a_loc_arr = OpPtrAccessChain %arr_v4f32_ptr %in_a_matptr %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
1815 											"%a_loc_first_elem = OpAccessChain %v4f32_ptr %a_loc_arr %c_i32_0 "
1816 											"%a_loc = OpPtrAccessChain %sb_f32ptr %a_loc_first_elem %c_i32_1 %c_i32_3"};
1817 
1818 		const string inputBPtrAccessChain[]	= {	"",
1819 											"%b_loc = OpPtrAccessChain %mat2x2_ptr       %in_b_matptr %c_i32_0",
1820 											"%b_loc = OpPtrAccessChain %arr2_ptr         %in_b_matptr %c_i32_0 %c_i32_0",
1821 											"%b_loc = OpPtrAccessChain %inner_struct_ptr %in_b_matptr %c_i32_0 %c_i32_1 %c_i32_1",
1822 											"%b_loc = OpPtrAccessChain %arr_v4f32_ptr    %in_b_matptr %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
1823 											"%b_loc = OpPtrAccessChain %v4f32_ptr        %in_b_matptr %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
1824 											// Next case emulates:
1825 											// %b_loc = OpPtrAccessChain %sb_f32ptr      %in_b_matptr %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3
1826 											// But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
1827 											//    %b_loc_arr is a pointer to an array that we want to index with 1.
1828 											// But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
1829 											// get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
1830 											"%b_loc_arr = OpPtrAccessChain %arr_v4f32_ptr %in_b_matptr %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
1831 											"%b_loc_first_elem = OpAccessChain %v4f32_ptr %b_loc_arr %c_i32_0 "
1832 											"%b_loc = OpPtrAccessChain %sb_f32ptr %b_loc_first_elem %c_i32_1 %c_i32_3"};
1833 
1834 
1835 		const string remainingIndexesAtLevel[]= {"%c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
1836 												 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_2",
1837 												 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_3",
1838 												 "%c_i32_1 %c_i32_0 %c_i32_0",
1839 												 "%c_i32_1 %c_i32_1",
1840 												 "%c_i32_2",
1841 												 ""};
1842 
1843 		const string pointerTypeAtLevel[]	= {"%sb_buf", "%mat2x2_ptr", "%arr2_ptr", "%inner_struct_ptr", "%arr_v4f32_ptr", "%v4f32_ptr", "%sb_f32ptr"};
1844 		const string baseANameAtLevel[]		= {"%in_a", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc"};
1845 		const string baseBNameAtLevel[]			= {"%in_b", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc"};
1846 
1847 		for (int indexLevel = 0; indexLevel < numLevels; ++indexLevel)
1848 		{
1849 			// index into the outer structure must be zero since the outer structure has only 1 member.
1850 			DE_ASSERT(indexesForLevel[indexLevel][0] == 0);
1851 
1852 			baseOffset						= getBaseOffset(indexesForLevel[indexLevel][1],
1853 															indexesForLevel[indexLevel][2],
1854 															indexesForLevel[indexLevel][3],
1855 															indexesForLevel[indexLevel][4],
1856 															indexesForLevel[indexLevel][5]);
1857 
1858 			// Use OpSelect to choose between 2 pointers
1859 			{
1860 				GraphicsResources				resources;
1861 				map<string, string>				specs;
1862 				string opCodeForTests			= "opselect";
1863 				string name						= opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
1864 				specs["select_inputA"]			= spirvSelectInputA;
1865 				specs["helper_functions"]		= "";
1866 				specs["a_loc"]					= inputALocations[indexLevel];
1867 				specs["b_loc"]					= inputBLocations[indexLevel];
1868 				specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
1869 				specs["selection_strategy"]		= "%var_ptr	= OpSelect " +
1870 													pointerTypeAtLevel[indexLevel] + " " +
1871 													spirvSelectInputA + " " +
1872 													baseANameAtLevel[indexLevel] + " " +
1873 													baseBNameAtLevel[indexLevel] + "\n";
1874 				fragments["decoration"]			= decoration.specialize(specs);
1875 				fragments["pre_main"]			= preMain.specialize(specs);
1876 				fragments["testfun"]			= testFunction.specialize(specs);
1877 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1878 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1879 				getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
1880 				createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
1881 			}
1882 			// Use OpCopyObject to get variable pointers
1883 			{
1884 				GraphicsResources				resources;
1885 				map<string, string>				specs;
1886 				string opCodeForTests			= "opcopyobject";
1887 				string name						= opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
1888 				specs["select_inputA"]			= spirvSelectInputA;
1889 				specs["helper_functions"]		= "";
1890 				specs["a_loc"]					= inputALocations[indexLevel];
1891 				specs["b_loc"]					= inputBLocations[indexLevel];
1892 				specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
1893 				specs["selection_strategy"]		=
1894 									"%in_a_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseANameAtLevel[indexLevel] + "\n"
1895 									"%in_b_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseBNameAtLevel[indexLevel] + "\n"
1896 									"%var_ptr	= OpSelect " + pointerTypeAtLevel[indexLevel] + " " + spirvSelectInputA + " %in_a_copy %in_b_copy\n";
1897 				fragments["decoration"]			= decoration.specialize(specs);
1898 				fragments["pre_main"]			= preMain.specialize(specs);
1899 				fragments["testfun"]			= testFunction.specialize(specs);
1900 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1901 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1902 				getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
1903 				createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
1904 			}
1905 			// Use OpPhi to choose between 2 pointers
1906 			{
1907 				GraphicsResources				resources;
1908 				map<string, string>				specs;
1909 				string opCodeForTests			= "opphi";
1910 				string name						= opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
1911 				specs["select_inputA"]			= spirvSelectInputA;
1912 				specs["helper_functions"]		= "";
1913 				specs["a_loc"]					= inputALocations[indexLevel];
1914 				specs["b_loc"]					= inputBLocations[indexLevel];
1915 				specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
1916 				specs["selection_strategy"]		=
1917 								"				  OpSelectionMerge %end_label None\n"
1918 								"				  OpBranchConditional " + spirvSelectInputA + " %take_input_a %take_input_b\n"
1919 								"%take_input_a	= OpLabel\n"
1920 								"				  OpBranch %end_label\n"
1921 								"%take_input_b	= OpLabel\n"
1922 								"			      OpBranch %end_label\n"
1923 								"%end_label		= OpLabel\n"
1924 								"%var_ptr		= OpPhi "
1925 													+ pointerTypeAtLevel[indexLevel] + " "
1926 													+ baseANameAtLevel[indexLevel]
1927 													+ " %take_input_a "
1928 													+ baseBNameAtLevel[indexLevel]
1929 													+ " %take_input_b\n";
1930 				fragments["decoration"]			= decoration.specialize(specs);
1931 				fragments["pre_main"]			= preMain.specialize(specs);
1932 				fragments["testfun"]			= testFunction.specialize(specs);
1933 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1934 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1935 				getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
1936 				createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
1937 			}
1938 			// Use OpFunctionCall to choose between 2 pointers
1939 			{
1940 				GraphicsResources				resources;
1941 				map<string, string>				functionSpecs;
1942 				map<string, string>				specs;
1943 				string opCodeForTests			= "opfunctioncall";
1944 				string name						= opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
1945 				functionSpecs["selected_type"]	= pointerTypeAtLevel[indexLevel];
1946 				specs["helper_functions"]		= selectorFunctions.specialize(functionSpecs);
1947 				specs["select_inputA"]			= spirvSelectInputA;
1948 				specs["a_loc"]					= inputALocations[indexLevel];
1949 				specs["b_loc"]					= inputBLocations[indexLevel];
1950 				specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
1951 				specs["selection_strategy"]		= "%var_ptr	= OpFunctionCall "
1952 													+ pointerTypeAtLevel[indexLevel]
1953 													+ " %choose_input_func "
1954 													+ spirvSelectInputA + " "
1955 													+ baseANameAtLevel[indexLevel] + " "
1956 													+ baseBNameAtLevel[indexLevel] + "\n";
1957 				fragments["decoration"]			= decoration.specialize(specs);
1958 				fragments["pre_main"]			= preMain.specialize(specs);
1959 				fragments["testfun"]			= testFunction.specialize(specs);
1960 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1961 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1962 				getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
1963 				createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
1964 			}
1965 			// Use OpPtrAccessChain to get variable pointers
1966 			{
1967 				GraphicsResources				resources;
1968 				map<string, string>				specs;
1969 				string opCodeForTests			= "opptraccesschain";
1970 				string name						= opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
1971 				specs["select_inputA"]			= spirvSelectInputA;
1972 				specs["helper_functions"]		= "";
1973 				specs["a_loc"]					= inputAPtrAccessChain[indexLevel];
1974 				specs["b_loc"]					= inputBPtrAccessChain[indexLevel];
1975 				specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
1976 				specs["selection_strategy"]		= "%var_ptr	= OpSelect "
1977 													+ pointerTypeAtLevel[indexLevel] + " "
1978 													+ spirvSelectInputA + " "
1979 													+ baseANameAtLevel[indexLevel] + " "
1980 													+ baseBNameAtLevel[indexLevel] + "\n";
1981 				fragments["decoration"]			= decoration.specialize(specs);
1982 				fragments["pre_main"]			= preMain.specialize(specs);
1983 				fragments["testfun"]			= testFunction.specialize(specs);
1984 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1985 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1986 				getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
1987 				createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
1988 			}
1989 		}
1990 	}
1991 }
1992 
addSingleInputBufferReadOnlyVariablePointersGraphicsGroup(tcu::TestCaseGroup * testGroup)1993 void addSingleInputBufferReadOnlyVariablePointersGraphicsGroup (tcu::TestCaseGroup* testGroup)
1994 {
1995 	const int						numFloatsPerInnerStruct	= 64;
1996 	vector<float>					inputBuffer				(2 * numFloatsPerInnerStruct, 0);
1997 	deUint32						baseOffset				= -1;
1998 	VulkanFeatures					requiredFeatures;
1999 	map<string, string>				fragments;
2000 	RGBA							defaultColors[4];
2001 	RGBA							expectedColors[4];
2002 	vector<string>					extensions;
2003 
2004 	// Set the proper extension features required for the tests.
2005 	// The following tests use variable pointers confined withing a single buffer.
2006 	requiredFeatures.extVariablePointers = EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS_STORAGEBUFFER;
2007 
2008 	// Set the required extension.
2009 	extensions.push_back("VK_KHR_variable_pointers");
2010 
2011 	getDefaultColors(defaultColors);
2012 
2013 	// These tests exercise variable pointers into various levels of the following data-structure:
2014 	// struct struct inner_struct {
2015 	//   vec4 x[2]; // array of 2 vectors. Each vector is 4 floats.
2016 	//   vec4 y[2]; // array of 2 vectors. Each vector is 4 floats.
2017 	// };
2018 	//
2019 	// struct outer_struct {
2020 	//   inner_struct r[2][2];
2021 	// };
2022 	//
2023 	// struct input_buffer {
2024 	//   outer_struct a;
2025 	//   outer_struct b;
2026 	// }
2027 	//
2028 	// The inner_struct contains 16 floats, and the outer_struct contains 64 floats.
2029 	// Therefore the input_buffer can be an array of 128 floats.
2030 
2031 	// Populate input_buffer's first member (a) to contain:  {0, 4, ... , 252} / 255.f
2032 	// Populate input_buffer's second member (b) to contain: {3, 7, ... , 255} / 255.f
2033 	for (size_t i = 0; i < numFloatsPerInnerStruct; ++i)
2034 	{
2035 		inputBuffer[i] = 4*float(i) / 255;
2036 		inputBuffer[i + numFloatsPerInnerStruct] = ((4*float(i)) + 3) / 255;
2037 	}
2038 
2039 	// In the following tests we use variable pointers to point to different types:
2040 	// nested structures, matrices of structures, arrays of structures, arrays of vectors, vectors of scalars, and scalars.
2041 	// outer_struct.inner_struct[?][?].x[?][?];
2042 	//   ^              ^        ^  ^  ^ ^  ^
2043 	//
2044 	// 1. inputBuffer.a						or	inputBuffer.b						= nested structure
2045 	// 2. inputBuffer.a.r					or	inputBuffer.b.r						= matrices of structures
2046 	// 3. inputBuffer.a.r[?]				or	inputBuffer.b.r[?]					= arrays of structures
2047 	// 4. inputBuffer.a.r[?][?]				or	inputBuffer.b.r[?][?]				= structures
2048 	// 5. inputBuffer.a.r[?][?].(x|y)		or	inputBuffer.b.r[?][?].(x|y)			= arrays of vectors
2049 	// 6. inputBuffer.a.r[?][?].(x|y)[?]	or	inputBuffer.b.r[?][?].(x|y)[?]		= vectors of scalars
2050 	// 7. inputBuffer.a.r[?][?].(x|y)[?][?]	or	inputBuffer.b.r[?][?].(x|y)[?][?]	= scalars
2051 	const int numLevels	=	7;
2052 
2053 	fragments["capability"]		=	"OpCapability VariablePointersStorageBuffer				\n";
2054 	fragments["extension"]		=	"OpExtension \"SPV_KHR_variable_pointers\"				\n"
2055 									"OpExtension \"SPV_KHR_storage_buffer_storage_class\"	\n";
2056 	const StringTemplate decoration		(
2057 		// Set the ArrayStrides
2058 		"OpDecorate %arr2_v4float        ArrayStride 16			\n"
2059 		"OpDecorate %arr2_inner_struct   ArrayStride 64			\n"
2060 		"OpDecorate %mat2x2_inner_struct ArrayStride 128		\n"
2061 		"OpDecorate %outer_struct_ptr    ArrayStride 256		\n"
2062 		"OpDecorate %v4f32_ptr           ArrayStride 16			\n"
2063 
2064 		// Set the Offsets
2065 		"OpMemberDecorate			%input_buffer 0 Offset 0	\n"
2066 		"OpMemberDecorate			%input_buffer 1 Offset 256	\n"
2067 		"OpMemberDecorate			%outer_struct 0 Offset 0	\n"
2068 		"OpMemberDecorate			%inner_struct 0 Offset 0	\n"
2069 		"OpMemberDecorate			%inner_struct 1 Offset 32	\n"
2070 
2071 		"OpDecorate					%input_buffer Block			\n"
2072 
2073 		"OpDecorate %input			DescriptorSet 0				\n"
2074 		"OpDecorate %input			Binding 0					\n"
2075 	);
2076 
2077 	const StringTemplate preMain		(
2078 		///////////
2079 		// TYPES //
2080 		///////////
2081 
2082 		// struct struct inner_struct {
2083 		//   vec4 x[2]; // array of 2 vectors
2084 		//   vec4 y[2]; // array of 2 vectors
2085 		// };
2086 		"%arr2_v4float			= OpTypeArray %v4f32 %c_u32_2						\n"
2087 		"%inner_struct			= OpTypeStruct %arr2_v4float %arr2_v4float			\n"
2088 
2089 		// struct outer_struct {
2090 		//   inner_struct r[2][2];
2091 		// };
2092 		"%arr2_inner_struct		= OpTypeArray %inner_struct %c_u32_2				\n"
2093 		"%mat2x2_inner_struct	= OpTypeArray %arr2_inner_struct %c_u32_2			\n"
2094 		"%outer_struct			= OpTypeStruct %mat2x2_inner_struct					\n"
2095 
2096 		// struct input_buffer {
2097 		//   outer_struct a;
2098 		//   outer_struct b;
2099 		// }
2100 		"%input_buffer			= OpTypeStruct %outer_struct %outer_struct			\n"
2101 
2102 		///////////////////
2103 		// POINTER TYPES //
2104 		///////////////////
2105 		"%input_buffer_ptr		= OpTypePointer StorageBuffer %input_buffer			\n"
2106 		"%outer_struct_ptr		= OpTypePointer StorageBuffer %outer_struct			\n"
2107 		"%mat2x2_ptr			= OpTypePointer StorageBuffer %mat2x2_inner_struct	\n"
2108 		"%arr2_ptr				= OpTypePointer StorageBuffer %arr2_inner_struct	\n"
2109 		"%inner_struct_ptr		= OpTypePointer StorageBuffer %inner_struct			\n"
2110 		"%arr_v4f32_ptr			= OpTypePointer StorageBuffer %arr2_v4float			\n"
2111 		"%v4f32_ptr				= OpTypePointer StorageBuffer %v4f32				\n"
2112 		"%sb_f32ptr				= OpTypePointer StorageBuffer %f32					\n"
2113 
2114 		///////////////
2115 		// VARIABLES //
2116 		///////////////
2117 		"%input					= OpVariable %input_buffer_ptr StorageBuffer		\n"
2118 
2119 		///////////////
2120 		// CONSTANTS //
2121 		///////////////
2122 		"%c_bool_true			= OpConstantTrue %bool								\n"
2123 		"%c_bool_false			= OpConstantFalse %bool								\n"
2124 
2125 		//////////////////////
2126 		// HELPER FUNCTIONS //
2127 		//////////////////////
2128 		"${helper_functions} \n"
2129 	);
2130 
2131 	const StringTemplate selectorFunctions	(
2132 		// These selector functions return variable pointers.
2133 		// These functions are used by tests that use OpFunctionCall to obtain the variable pointer
2134 		"%selector_func_type	= OpTypeFunction ${selected_type} %bool ${selected_type} ${selected_type}\n"
2135 		"%choose_input_func		= OpFunction ${selected_type} None %selector_func_type\n"
2136 		"%choose_first_param	= OpFunctionParameter %bool\n"
2137 		"%first_param			= OpFunctionParameter ${selected_type}\n"
2138 		"%second_param			= OpFunctionParameter ${selected_type}\n"
2139 		"%selector_func_begin	= OpLabel\n"
2140 		"%result_ptr			= OpSelect ${selected_type} %choose_first_param %first_param %second_param\n"
2141 		"OpReturnValue %result_ptr\n"
2142 		"OpFunctionEnd\n"
2143 	);
2144 
2145 	const StringTemplate testFunction	(
2146 		"%test_code		= OpFunction %v4f32 None %v4f32_v4f32_function\n"
2147 		"%param			= OpFunctionParameter %v4f32\n"
2148 		"%entry			= OpLabel\n"
2149 
2150 		// Here are the 2 nested structures:
2151 		"%in_a			= OpAccessChain %outer_struct_ptr %input %c_i32_0\n"
2152 		"%in_b			= OpAccessChain %outer_struct_ptr %input %c_i32_1\n"
2153 
2154 		// Define the 2 pointers from which we're going to choose one.
2155 		"${a_loc} \n"
2156 		"${b_loc} \n"
2157 
2158 		// Choose between the 2 pointers / variable pointers
2159 		"${selection_strategy} \n"
2160 
2161 		// OpAccessChain into the variable pointer until you get to the float.
2162 		"%result_loc	= OpAccessChain %sb_f32ptr %var_ptr  ${remaining_indexes} \n"
2163 
2164 
2165 		// Now load from the result_loc
2166 		"%result_val	= OpLoad %f32 %result_loc\n"
2167 
2168 		// Modify the 'RED channel' of the output color to the chosen value
2169 		"%output_color	= OpCompositeInsert %v4f32 %result_val %param 0 \n"
2170 
2171 		// Return and FunctionEnd
2172 		"OpReturnValue %output_color\n"
2173 		"OpFunctionEnd\n");
2174 
2175 	// When select is 0, the variable pointer should point to a value in the first input_buffer member (a).
2176 	// When select is 1, the variable pointer should point to a value in the second input_buffer member (b).
2177 	// Since the 2 members of the input_buffer (a and b) are of type outer_struct, we can conveniently use
2178 	// the same indexing scheme that we used for the 2-input-buffer tests.
2179 	for (int selectInputA = 0; selectInputA < 2; ++selectInputA)
2180 	{
2181 		const string selectedInputStr	= selectInputA ? "first_input"	: "second_input";
2182 		const string spirvSelectInputA	= selectInputA ? "%c_bool_true"	: "%c_bool_false";
2183 		const int outerStructIndex		= selectInputA ? 0				: 1;
2184 
2185 		// The indexes chosen at each level. At any level, any given offset is exercised.
2186 		// outerStructIndex is 0 for member (a) and 1 for member (b).
2187 		const int indexesForLevel[numLevels][6]= {{outerStructIndex, 0, 0, 0, 0, 1},
2188 												  {outerStructIndex, 1, 0, 1, 0, 2},
2189 												  {outerStructIndex, 0, 1, 0, 1, 3},
2190 												  {outerStructIndex, 1, 1, 1, 0, 0},
2191 												  {outerStructIndex, 0, 0, 1, 1, 1},
2192 												  {outerStructIndex, 1, 0, 0, 0, 2},
2193 												  {outerStructIndex, 1, 1, 1, 1, 3}};
2194 
2195 		const string indexLevelNames[]		= {"_outer_struct_", "_matrices_", "_arrays_", "_inner_structs_", "_vec4arr_", "_vec4_", "_float_"};
2196 		const string inputALocations[]		= {	"",
2197 											"%a_loc = OpAccessChain %mat2x2_ptr       %in_a %c_i32_0",
2198 											"%a_loc = OpAccessChain %arr2_ptr         %in_a %c_i32_0 %c_i32_0",
2199 											"%a_loc = OpAccessChain %inner_struct_ptr %in_a %c_i32_0 %c_i32_1 %c_i32_1",
2200 											"%a_loc = OpAccessChain %arr_v4f32_ptr    %in_a %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2201 											"%a_loc = OpAccessChain %v4f32_ptr        %in_a %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
2202 											"%a_loc = OpAccessChain %sb_f32ptr        %in_a %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3"};
2203 
2204 		const string inputBLocations[]		= {	"",
2205 											"%b_loc = OpAccessChain %mat2x2_ptr       %in_b %c_i32_0",
2206 											"%b_loc = OpAccessChain %arr2_ptr         %in_b %c_i32_0 %c_i32_0",
2207 											"%b_loc = OpAccessChain %inner_struct_ptr %in_b %c_i32_0 %c_i32_1 %c_i32_1",
2208 											"%b_loc = OpAccessChain %arr_v4f32_ptr    %in_b %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2209 											"%b_loc = OpAccessChain %v4f32_ptr        %in_b %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
2210 											"%b_loc = OpAccessChain %sb_f32ptr        %in_b %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3"};
2211 
2212 		const string inputAPtrAccessChain[]	= {	"",
2213 											"%a_loc = OpPtrAccessChain %mat2x2_ptr       %in_a %c_i32_0 %c_i32_0",
2214 											"%a_loc = OpPtrAccessChain %arr2_ptr         %in_a %c_i32_0 %c_i32_0 %c_i32_0",
2215 											"%a_loc = OpPtrAccessChain %inner_struct_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1",
2216 											"%a_loc = OpPtrAccessChain %arr_v4f32_ptr    %in_a %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2217 											"%a_loc = OpPtrAccessChain %v4f32_ptr        %in_a %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
2218 											// Next case emulates:
2219 											// %a_loc = OpPtrAccessChain %sb_f32ptr      %in_a %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3
2220 											// But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
2221 											//    %a_loc_arr is a pointer to an array that we want to index with 1.
2222 											// But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
2223 											// get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
2224 											"%a_loc_arr = OpPtrAccessChain %arr_v4f32_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
2225 											"%a_loc_first_elem = OpAccessChain %v4f32_ptr %a_loc_arr %c_i32_0 "
2226 											"%a_loc = OpPtrAccessChain %sb_f32ptr %a_loc_first_elem %c_i32_1 %c_i32_3"};
2227 
2228 		const string inputBPtrAccessChain[]	= {	"",
2229 											"%b_loc = OpPtrAccessChain %mat2x2_ptr       %in_b %c_i32_0 %c_i32_0",
2230 											"%b_loc = OpPtrAccessChain %arr2_ptr         %in_b %c_i32_0 %c_i32_0 %c_i32_0",
2231 											"%b_loc = OpPtrAccessChain %inner_struct_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1",
2232 											"%b_loc = OpPtrAccessChain %arr_v4f32_ptr    %in_b %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2233 											"%b_loc = OpPtrAccessChain %v4f32_ptr        %in_b %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
2234 											// Next case emulates:
2235 											// %b_loc = OpPtrAccessChain %sb_f32ptr      %in_b %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3
2236 											// But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
2237 											//    %b_loc_arr is a pointer to an array that we want to index with 1.
2238 											// But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
2239 											// get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
2240 											"%b_loc_arr = OpPtrAccessChain %arr_v4f32_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
2241 											"%b_loc_first_elem = OpAccessChain %v4f32_ptr %b_loc_arr %c_i32_0 "
2242 											"%b_loc = OpPtrAccessChain %sb_f32ptr %b_loc_first_elem %c_i32_1 %c_i32_3"};
2243 
2244 
2245 		const string remainingIndexesAtLevel[]= {"%c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2246 												 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_2",
2247 												 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_3",
2248 												 "%c_i32_1 %c_i32_0 %c_i32_0",
2249 												 "%c_i32_1 %c_i32_1",
2250 												 "%c_i32_2",
2251 												 ""};
2252 
2253 		const string pointerTypeAtLevel[] = {"%outer_struct_ptr", "%mat2x2_ptr", "%arr2_ptr", "%inner_struct_ptr", "%arr_v4f32_ptr", "%v4f32_ptr", "%sb_f32ptr"};
2254 		const string baseANameAtLevel[]	  = {"%in_a", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc"};
2255 		const string baseBNameAtLevel[]	  = {"%in_b", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc"};
2256 
2257 		for (int indexLevel = 0; indexLevel < numLevels; ++indexLevel)
2258 		{
2259 			// Use OpSelect to choose between 2 pointers
2260 			{
2261 				GraphicsResources				resources;
2262 				map<string, string>				specs;
2263 				string opCodeForTests			= "opselect";
2264 				string name						= opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2265 				specs["select_inputA"]			= spirvSelectInputA;
2266 				specs["helper_functions"]		= "";
2267 				specs["a_loc"]					= inputALocations[indexLevel];
2268 				specs["b_loc"]					= inputBLocations[indexLevel];
2269 				specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
2270 				specs["selection_strategy"]		= "%var_ptr	= OpSelect " +
2271 													pointerTypeAtLevel[indexLevel] + " " +
2272 													spirvSelectInputA + " " +
2273 													baseANameAtLevel[indexLevel] + " " +
2274 													baseBNameAtLevel[indexLevel] + "\n";
2275 				baseOffset						= getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2276 																					indexesForLevel[indexLevel][1],
2277 																					indexesForLevel[indexLevel][2],
2278 																					indexesForLevel[indexLevel][3],
2279 																					indexesForLevel[indexLevel][4],
2280 																					indexesForLevel[indexLevel][5]);
2281 				fragments["decoration"]			= decoration.specialize(specs);
2282 				fragments["pre_main"]			= preMain.specialize(specs);
2283 				fragments["testfun"]			= testFunction.specialize(specs);
2284 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2285 				getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2286 				createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2287 			}
2288 			// Use OpCopyObject to get variable pointers
2289 			{
2290 				GraphicsResources				resources;
2291 				map<string, string>				specs;
2292 				string opCodeForTests			= "opcopyobject";
2293 				string name						= opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2294 				specs["select_inputA"]			= spirvSelectInputA;
2295 				specs["helper_functions"]		= "";
2296 				specs["a_loc"]					= inputALocations[indexLevel];
2297 				specs["b_loc"]					= inputBLocations[indexLevel];
2298 				specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
2299 				specs["selection_strategy"]		=
2300 									"%in_a_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseANameAtLevel[indexLevel] + "\n"
2301 									"%in_b_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseBNameAtLevel[indexLevel] + "\n"
2302 									"%var_ptr	= OpSelect " + pointerTypeAtLevel[indexLevel] + " " + spirvSelectInputA + " %in_a_copy %in_b_copy\n";
2303 				baseOffset						= getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2304 																					indexesForLevel[indexLevel][1],
2305 																					indexesForLevel[indexLevel][2],
2306 																					indexesForLevel[indexLevel][3],
2307 																					indexesForLevel[indexLevel][4],
2308 																					indexesForLevel[indexLevel][5]);
2309 				fragments["decoration"]			= decoration.specialize(specs);
2310 				fragments["pre_main"]			= preMain.specialize(specs);
2311 				fragments["testfun"]			= testFunction.specialize(specs);
2312 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2313 				getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2314 				createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2315 			}
2316 			// Use OpPhi to choose between 2 pointers
2317 			{
2318 				GraphicsResources				resources;
2319 				map<string, string>				specs;
2320 				string opCodeForTests			= "opphi";
2321 				string name						= opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2322 				specs["select_inputA"]			= spirvSelectInputA;
2323 				specs["helper_functions"]		= "";
2324 				specs["a_loc"]					= inputALocations[indexLevel];
2325 				specs["b_loc"]					= inputBLocations[indexLevel];
2326 				specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
2327 				specs["selection_strategy"]		=
2328 								"				  OpSelectionMerge %end_label None\n"
2329 								"				  OpBranchConditional " + spirvSelectInputA + " %take_input_a %take_input_b\n"
2330 								"%take_input_a	= OpLabel\n"
2331 								"				  OpBranch %end_label\n"
2332 								"%take_input_b	= OpLabel\n"
2333 								"			      OpBranch %end_label\n"
2334 								"%end_label		= OpLabel\n"
2335 								"%var_ptr		= OpPhi "
2336 													+ pointerTypeAtLevel[indexLevel] + " "
2337 													+ baseANameAtLevel[indexLevel]
2338 													+ " %take_input_a "
2339 													+ baseBNameAtLevel[indexLevel]
2340 													+ " %take_input_b\n";
2341 				baseOffset						= getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2342 																					indexesForLevel[indexLevel][1],
2343 																					indexesForLevel[indexLevel][2],
2344 																					indexesForLevel[indexLevel][3],
2345 																					indexesForLevel[indexLevel][4],
2346 																					indexesForLevel[indexLevel][5]);
2347 				fragments["decoration"]			= decoration.specialize(specs);
2348 				fragments["pre_main"]			= preMain.specialize(specs);
2349 				fragments["testfun"]			= testFunction.specialize(specs);
2350 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2351 				getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2352 				createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2353 			}
2354 			// Use OpFunctionCall to choose between 2 pointers
2355 			{
2356 				GraphicsResources				resources;
2357 				map<string, string>				functionSpecs;
2358 				map<string, string>				specs;
2359 				string opCodeForTests			= "opfunctioncall";
2360 				string name						= opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2361 				//string selectedType				= "%mat2x2_ptr";
2362 				functionSpecs["selected_type"]	= pointerTypeAtLevel[indexLevel];
2363 				specs["helper_functions"]		= selectorFunctions.specialize(functionSpecs);
2364 				specs["select_inputA"]			= spirvSelectInputA;
2365 				specs["a_loc"]					= inputALocations[indexLevel];
2366 				specs["b_loc"]					= inputBLocations[indexLevel];
2367 				specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
2368 				specs["selection_strategy"]		= "%var_ptr	= OpFunctionCall "
2369 													+ pointerTypeAtLevel[indexLevel]
2370 													+ " %choose_input_func "
2371 													+ spirvSelectInputA + " "
2372 													+ baseANameAtLevel[indexLevel] + " "
2373 													+ baseBNameAtLevel[indexLevel] + "\n";
2374 				baseOffset						= getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2375 																					indexesForLevel[indexLevel][1],
2376 																					indexesForLevel[indexLevel][2],
2377 																					indexesForLevel[indexLevel][3],
2378 																					indexesForLevel[indexLevel][4],
2379 																					indexesForLevel[indexLevel][5]);
2380 				fragments["decoration"]			= decoration.specialize(specs);
2381 				fragments["pre_main"]			= preMain.specialize(specs);
2382 				fragments["testfun"]			= testFunction.specialize(specs);
2383 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2384 				getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2385 				createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2386 			}
2387 			// Use OpPtrAccessChain to get variable pointers
2388 			{
2389 				GraphicsResources				resources;
2390 				map<string, string>				specs;
2391 				string opCodeForTests			= "opptraccesschain";
2392 				string name						= opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2393 				specs["select_inputA"]			= spirvSelectInputA;
2394 				specs["helper_functions"]		= "";
2395 				specs["a_loc"]					= inputAPtrAccessChain[indexLevel];
2396 				specs["b_loc"]					= inputBPtrAccessChain[indexLevel];
2397 				specs["remaining_indexes"]		= remainingIndexesAtLevel[indexLevel];
2398 				specs["selection_strategy"]		= "%var_ptr	= OpSelect "
2399 													+ pointerTypeAtLevel[indexLevel] + " "
2400 													+ spirvSelectInputA + " "
2401 													+ baseANameAtLevel[indexLevel] + " "
2402 													+ baseBNameAtLevel[indexLevel] + "\n";
2403 				baseOffset						= getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2404 																					indexesForLevel[indexLevel][1],
2405 																					indexesForLevel[indexLevel][2],
2406 																					indexesForLevel[indexLevel][3],
2407 																					indexesForLevel[indexLevel][4],
2408 																					indexesForLevel[indexLevel][5]);
2409 				fragments["decoration"]			= decoration.specialize(specs);
2410 				fragments["pre_main"]			= preMain.specialize(specs);
2411 				fragments["testfun"]			= testFunction.specialize(specs);
2412 				resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2413 				getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2414 				createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2415 			}
2416 		}
2417 	}
2418 }
2419 
addNullptrVariablePointersGraphicsGroup(tcu::TestCaseGroup * testGroup)2420 void addNullptrVariablePointersGraphicsGroup (tcu::TestCaseGroup* testGroup)
2421 {
2422 	float							someFloat				= 78 / 255.f;
2423 	vector<float>					input					(1, someFloat);
2424 	vector<float>					expectedOutput			(1, someFloat);
2425 	VulkanFeatures					requiredFeatures;
2426 	map<string, string>				fragments;
2427 	RGBA							defaultColors[4];
2428 	RGBA							expectedColors[4];
2429 	vector<string>					extensions;
2430 
2431 	getDefaultColors(defaultColors);
2432 	getExpectedOutputColor(defaultColors, expectedColors, someFloat);
2433 
2434 	// Set the required extension.
2435 	extensions.push_back("VK_KHR_variable_pointers");
2436 
2437 	// Requires the variable pointers feature.
2438 	requiredFeatures.extVariablePointers	= EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS;
2439 
2440 	fragments["capability"]		=	"OpCapability VariablePointers							\n";
2441 	fragments["extension"]		=	"OpExtension \"SPV_KHR_variable_pointers\"				\n"
2442 									"OpExtension \"SPV_KHR_storage_buffer_storage_class\"	\n";
2443 	const StringTemplate decoration		(
2444 		// Decorations
2445 		"OpDecorate %input DescriptorSet 0				\n"
2446 		"OpDecorate %input Binding 0					\n"
2447 
2448 		// Set the Block decoration
2449 		"OpDecorate %float_struct	Block				\n"
2450 
2451 		// Set the Offsets
2452 		"OpMemberDecorate %float_struct 0 Offset 0		\n"
2453 	);
2454 
2455 	const StringTemplate preMain		(
2456 		// struct float_struct {
2457 		//   float x;
2458 		// };
2459 		"%float_struct		= OpTypeStruct %f32											\n"
2460 
2461 		// POINTER TYPES
2462 		"%float_struct_ptr	= OpTypePointer StorageBuffer %float_struct					\n"
2463 		"%sb_f32ptr			= OpTypePointer StorageBuffer %f32							\n"
2464 		"%func_f32ptrptr	= OpTypePointer Function %sb_f32ptr							\n"
2465 
2466 		// CONSTANTS
2467 		"%c_bool_true		= OpConstantTrue	%bool									\n"
2468 		"%c_null_ptr		= OpConstantNull	%sb_f32ptr								\n"
2469 
2470 		// VARIABLES
2471 		"%input				= OpVariable %float_struct_ptr	StorageBuffer				\n"
2472 	);
2473 
2474 	const StringTemplate testFunction	(
2475 		"%test_code		= OpFunction %v4f32 None %v4f32_v4f32_function\n"
2476 		"%param			= OpFunctionParameter %v4f32\n"
2477 		"%entry			= OpLabel\n"
2478 
2479 		// Note that the Variable Pointers extension allows creation
2480 		// of a pointer variable with storage class of Private or Function.
2481 		"%f32_ptr_var	= OpVariable %func_f32ptrptr Function %c_null_ptr\n"
2482 
2483 		"%input_loc		= OpAccessChain %sb_f32ptr %input %c_i32_0\n"
2484 
2485 		// Null testing strategy
2486 		"${NullptrTestingStrategy}\n"
2487 		// Modify the 'RED channel' of the output color to the chosen value
2488 		"%output_color	= OpCompositeInsert %v4f32 %result_val %param 0 \n"
2489 		// Return and FunctionEnd
2490 		"OpReturnValue %output_color\n"
2491 		"OpFunctionEnd\n");
2492 
2493 	// f32_ptr_var has been inintialized to NULL.
2494 	// Now set it to the input variable and return it as output
2495 	{
2496 		GraphicsResources				resources;
2497 		map<string, string>				specs;
2498 		specs["NullptrTestingStrategy"]	=
2499 							"                  OpStore %f32_ptr_var %input_loc      \n"
2500 							"%loaded_f32_ptr = OpLoad  %sb_f32ptr   %f32_ptr_var    \n"
2501 							"%result_val     = OpLoad  %f32         %loaded_f32_ptr \n";
2502 		fragments["decoration"]			= decoration.specialize(specs);
2503 		fragments["pre_main"]			= preMain.specialize(specs);
2504 		fragments["testfun"]			= testFunction.specialize(specs);
2505 		resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(input)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2506 		createTestsForAllStages("opvariable_initialized_null", defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2507 	}
2508 	// Use OpSelect to choose between nullptr and a valid pointer. Since we can't dereference nullptr,
2509 	// it is forced to always choose the valid pointer.
2510 	{
2511 		GraphicsResources				resources;
2512 		map<string, string>				specs;
2513 		specs["NullptrTestingStrategy"]	= "%selected_ptr  = OpSelect %sb_f32ptr %c_bool_true %input_loc %c_null_ptr\n"
2514 										  "%result_val    = OpLoad %f32 %selected_ptr\n";
2515 		fragments["decoration"]			= decoration.specialize(specs);
2516 		fragments["pre_main"]			= preMain.specialize(specs);
2517 		fragments["testfun"]			= testFunction.specialize(specs);
2518 		resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(input)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2519 		createTestsForAllStages("opselect_null_or_valid_ptr", defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2520 	}
2521 }
2522 
2523 } // anonymous
2524 
createVariablePointersComputeGroup(tcu::TestContext & testCtx)2525 tcu::TestCaseGroup* createVariablePointersComputeGroup (tcu::TestContext& testCtx)
2526 {
2527 	de::MovePtr<tcu::TestCaseGroup> group	(new tcu::TestCaseGroup(testCtx, "variable_pointers", "Compute tests for SPV_KHR_variable_pointers extension"));
2528 	addTestGroup(group.get(), "compute", "Test the variable pointer extension using a compute shader", addVariablePointersComputeGroup);
2529 	addTestGroup(group.get(),
2530 				 "complex_types_compute",
2531 				 "Testing Variable Pointers pointing to various types in different input buffers",
2532 				 addComplexTypesVariablePointersComputeGroup);
2533 	addTestGroup(group.get(),
2534 				 "nullptr_compute",
2535 				 "Test the usage of nullptr using the variable pointers extension in a compute shader",
2536 				 addNullptrVariablePointersComputeGroup);
2537 
2538 	return group.release();
2539 }
2540 
createVariablePointersGraphicsGroup(tcu::TestContext & testCtx)2541 tcu::TestCaseGroup* createVariablePointersGraphicsGroup (tcu::TestContext& testCtx)
2542 {
2543 	de::MovePtr<tcu::TestCaseGroup> group	(new tcu::TestCaseGroup(testCtx, "variable_pointers", "Graphics tests for SPV_KHR_variable_pointers extension"));
2544 
2545 	addTestGroup(group.get(), "graphics", "Testing Variable Pointers in graphics pipeline", addVariablePointersGraphicsGroup);
2546 	addTestGroup(group.get(),
2547 				 "multi_buffer_read_only_graphics",
2548 				 "Testing Variable Pointers pointing to different input buffers in graphics pipeline (no SSBO writes)",
2549 				 addTwoInputBufferReadOnlyVariablePointersGraphicsGroup);
2550 	addTestGroup(group.get(),
2551 				 "single_buffer_read_only_graphics",
2552 				 "Testing Variable Pointers confined to a single input buffer in graphics pipeline (no SSBO writes)",
2553 				 addSingleInputBufferReadOnlyVariablePointersGraphicsGroup);
2554 	addTestGroup(group.get(),
2555 				 "nullptr_graphics",
2556 				 "Test the usage of nullptr using the variable pointers extension in graphics pipeline",
2557 				 addNullptrVariablePointersGraphicsGroup);
2558 
2559 	return group.release();
2560 }
2561 
2562 } // SpirVAssembly
2563 } // vkt
2564