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