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