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