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