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