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