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