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