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 tcu::TestContext &testCtx = group->getTestContext();
1397
1398 static const char dataDir[] = "spirv_assembly/instruction/compute/variable_pointer/dynamic_offset";
1399
1400 struct Case
1401 {
1402 string name;
1403 string desc;
1404 };
1405
1406 static const Case cases[] =
1407 {
1408 { "select_descriptor_array", "Test accessing a descriptor array using a variable pointer from OpSelect" },
1409 };
1410
1411 for (const auto& testCase : cases)
1412 {
1413 const string fileName = testCase.name + ".amber";
1414
1415 group->addChild(cts_amber::createAmberTestCase(testCtx, testCase.name.c_str(), testCase.desc.c_str(), dataDir, fileName, {"VK_KHR_variable_pointers", "VK_KHR_storage_buffer_storage_class", "VariablePointerFeatures.variablePointers", "VariablePointerFeatures.variablePointersStorageBuffer"}));
1416 }
1417 }
1418
addVariablePointersGraphicsGroup(tcu::TestCaseGroup * testGroup)1419 void addVariablePointersGraphicsGroup (tcu::TestCaseGroup* testGroup)
1420 {
1421 tcu::TestContext& testCtx = testGroup->getTestContext();
1422 de::Random rnd (deStringHash(testGroup->getName()));
1423 map<string, string> fragments;
1424 RGBA defaultColors[4];
1425 vector<string> extensions;
1426 const int seed = testCtx.getCommandLine().getBaseSeed();
1427 const int numMuxes = 100;
1428 const std::string numMuxesStr = "100";
1429 vector<float> inputAFloats (2*numMuxes, 0);
1430 vector<float> inputBFloats (2*numMuxes, 0);
1431 vector<float> inputSFloats (numMuxes, 0);
1432 vector<float> AmuxAOutputFloats (numMuxes, 0);
1433 vector<float> AmuxBOutputFloats (numMuxes, 0);
1434 vector<float> incrAmuxAOutputFloats (numMuxes, 0);
1435 vector<float> incrAmuxBOutputFloats (numMuxes, 0);
1436 VulkanFeatures requiredFeatures;
1437
1438 extensions.push_back("VK_KHR_variable_pointers");
1439 getDefaultColors(defaultColors);
1440
1441 // Each output entry is chosen as follows: ( 0 <= i < numMuxes)
1442 // 1) For tests with one input buffer: output[i] = (s[i] < 0) ? A[2*i] : A[2*i+1];
1443 // 2) For tests with two input buffers: output[i] = (s[i] < 0) ? A[i] : B[i];
1444
1445 fillRandomScalars(rnd, -100.f, 100.f, &inputAFloats[0], 2*numMuxes);
1446 fillRandomScalars(rnd, -100.f, 100.f, &inputBFloats[0], 2*numMuxes);
1447
1448 // We want to guarantee that the S input has some positive and some negative values.
1449 // We choose random negative numbers for the first half, random positive numbers for the second half, and then shuffle.
1450 fillRandomScalars(rnd, -100.f, -1.f , &inputSFloats[0], numMuxes / 2);
1451 fillRandomScalars(rnd, 1.f , 100.f, &inputSFloats[numMuxes / 2], numMuxes / 2);
1452 de::Random(seed).shuffle(inputSFloats.begin(), inputSFloats.end());
1453
1454 for (size_t i = 0; i < numMuxes; ++i)
1455 {
1456 AmuxAOutputFloats[i] = (inputSFloats[i] < 0) ? inputAFloats[2*i] : inputAFloats[2*i+1];
1457 AmuxBOutputFloats[i] = (inputSFloats[i] < 0) ? inputAFloats[i] : inputBFloats[i];
1458 incrAmuxAOutputFloats[i] = (inputSFloats[i] < 0) ? 1 + inputAFloats[2*i] : 1 + inputAFloats[2*i+1];
1459 incrAmuxBOutputFloats[i] = (inputSFloats[i] < 0) ? 1 + inputAFloats[i] : 1 + inputBFloats[i];
1460 }
1461
1462 fragments["extension"] = "OpExtension \"SPV_KHR_variable_pointers\"\n"
1463 "OpExtension \"SPV_KHR_storage_buffer_storage_class\"\n";
1464
1465 const StringTemplate preMain (
1466 "%c_i32_limit = OpConstant %i32 " + numMuxesStr + "\n"
1467 " %sb_f32 = OpTypePointer StorageBuffer %f32\n"
1468 " %ra_f32 = OpTypeRuntimeArray %f32\n"
1469 " %buf = OpTypeStruct %ra_f32\n"
1470 " %sb_buf = OpTypePointer StorageBuffer %buf\n"
1471
1472 " ${ExtraTypes}"
1473
1474 " ${ExtraGlobalScopeVars}"
1475
1476 " %indata_a = OpVariable %sb_buf StorageBuffer\n"
1477 " %indata_b = OpVariable %sb_buf StorageBuffer\n"
1478 " %indata_s = OpVariable %sb_buf StorageBuffer\n"
1479 " %outdata = OpVariable %sb_buf StorageBuffer\n"
1480
1481 " ${ExtraFunctions} ");
1482
1483 const std::string selectorFunction (
1484 // We're going to put the "selector" function here.
1485 // This function type is needed for tests that use OpFunctionCall.
1486 "%selector_func_type = OpTypeFunction %sb_f32 %bool %sb_f32 %sb_f32\n"
1487 "%choose_input_func = OpFunction %sb_f32 None %selector_func_type\n"
1488 "%is_neg_param = OpFunctionParameter %bool\n"
1489 "%first_ptr_param = OpFunctionParameter %sb_f32\n"
1490 "%second_ptr_param = OpFunctionParameter %sb_f32\n"
1491 "%selector_func_begin = OpLabel\n"
1492 "%result_ptr = OpSelect %sb_f32 %is_neg_param %first_ptr_param %second_ptr_param\n"
1493 "OpReturnValue %result_ptr\n"
1494 "OpFunctionEnd\n");
1495
1496 const StringTemplate decoration (
1497 "OpMemberDecorate %buf 0 Offset 0\n"
1498 "OpDecorate %buf Block\n"
1499 "OpDecorate %ra_f32 ArrayStride 4\n"
1500 "OpDecorate %sb_f32 ArrayStride 4\n"
1501 "OpDecorate %indata_a DescriptorSet 0\n"
1502 "OpDecorate %indata_b DescriptorSet 0\n"
1503 "OpDecorate %indata_s DescriptorSet 0\n"
1504 "OpDecorate %outdata DescriptorSet 0\n"
1505 "OpDecorate %indata_a Binding 0\n"
1506 "OpDecorate %indata_b Binding 1\n"
1507 "OpDecorate %indata_s Binding 2\n"
1508 "OpDecorate %outdata Binding 3\n");
1509
1510 const StringTemplate testFunction (
1511 "%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
1512 "%param = OpFunctionParameter %v4f32\n"
1513 "%entry = OpLabel\n"
1514
1515 "${ExtraFunctionScopeVars}"
1516
1517 "%i = OpVariable %fp_i32 Function\n"
1518
1519 "%should_run = OpFunctionCall %bool %isUniqueIdZero\n"
1520 " OpSelectionMerge %end_if None\n"
1521 " OpBranchConditional %should_run %run_test %end_if\n"
1522
1523 "%run_test = OpLabel\n"
1524 " OpStore %i %c_i32_0\n"
1525 " OpBranch %loop\n"
1526 // loop header
1527 "%loop = OpLabel\n"
1528 "%15 = OpLoad %i32 %i\n"
1529 "%lt = OpSLessThan %bool %15 %c_i32_limit\n"
1530 " OpLoopMerge %merge %inc None\n"
1531 " OpBranchConditional %lt %write %merge\n"
1532 // loop body
1533 "%write = OpLabel\n"
1534 "%30 = OpLoad %i32 %i\n"
1535 "%two_i = OpIAdd %i32 %30 %30\n"
1536 "%two_i_plus_1 = OpIAdd %i32 %two_i %c_i32_1\n"
1537 "%loc_s_i = OpAccessChain %sb_f32 %indata_s %c_i32_0 %30\n"
1538 "%loc_a_i = OpAccessChain %sb_f32 %indata_a %c_i32_0 %30\n"
1539 "%loc_b_i = OpAccessChain %sb_f32 %indata_b %c_i32_0 %30\n"
1540 "%loc_a_2i = OpAccessChain %sb_f32 %indata_a %c_i32_0 %two_i\n"
1541 "%loc_a_2i_plus_1 = OpAccessChain %sb_f32 %indata_a %c_i32_0 %two_i_plus_1\n"
1542 "%loc_outdata_i = OpAccessChain %sb_f32 %outdata %c_i32_0 %30\n"
1543 "%val_s_i = OpLoad %f32 %loc_s_i\n"
1544 "%is_neg = OpFOrdLessThan %bool %val_s_i %c_f32_0\n"
1545
1546 // select using a strategy.
1547 "${ResultStrategy}"
1548
1549 // load through the variable pointer
1550 "%mux_output = OpLoad %f32 ${VarPtrName}\n"
1551
1552 // store to the output vector.
1553 " OpStore %loc_outdata_i %mux_output\n"
1554 " OpBranch %inc\n"
1555 // ++i
1556 " %inc = OpLabel\n"
1557 " %37 = OpLoad %i32 %i\n"
1558 " %39 = OpIAdd %i32 %37 %c_i32_1\n"
1559 " OpStore %i %39\n"
1560 " OpBranch %loop\n"
1561
1562 // Return and FunctionEnd
1563 "%merge = OpLabel\n"
1564 " OpBranch %end_if\n"
1565 "%end_if = OpLabel\n"
1566 "OpReturnValue %param\n"
1567 "OpFunctionEnd\n");
1568
1569 const bool singleInputBuffer[] = { true, false };
1570 for (int inputBufferTypeIndex = 0 ; inputBufferTypeIndex < 2; ++inputBufferTypeIndex)
1571 {
1572 const bool isSingleInputBuffer = singleInputBuffer[inputBufferTypeIndex];
1573 const string cap = isSingleInputBuffer ? "OpCapability VariablePointersStorageBuffer\n" : "OpCapability VariablePointers\n";
1574 const vector<float>& expectedOutput = isSingleInputBuffer ? AmuxAOutputFloats : AmuxBOutputFloats;
1575 const vector<float>& expectedIncrOutput = isSingleInputBuffer ? incrAmuxAOutputFloats : incrAmuxBOutputFloats;
1576 const string bufferType = isSingleInputBuffer ? "single_buffer" : "two_buffers";
1577 const string muxInput1 = isSingleInputBuffer ? " %loc_a_2i " : " %loc_a_i ";
1578 const string muxInput2 = isSingleInputBuffer ? " %loc_a_2i_plus_1 " : " %loc_b_i ";
1579
1580 // Set the proper extension features required for the test
1581 if (isSingleInputBuffer)
1582 requiredFeatures.extVariablePointers.variablePointersStorageBuffer = true;
1583 else
1584 requiredFeatures.extVariablePointers.variablePointers = true;
1585
1586 // All of the following tests write their results into an output SSBO, therefore they require the following features.
1587 requiredFeatures.coreFeatures.vertexPipelineStoresAndAtomics = DE_TRUE;
1588 requiredFeatures.coreFeatures.fragmentStoresAndAtomics = DE_TRUE;
1589
1590 { // Variable Pointer Reads (using OpSelect)
1591 GraphicsResources resources;
1592 map<string, string> specs;
1593 string name = "reads_opselect_" + bufferType;
1594 specs["ExtraTypes"] = "";
1595 specs["ExtraGlobalScopeVars"] = "";
1596 specs["ExtraFunctionScopeVars"] = "";
1597 specs["ExtraFunctions"] = "";
1598 specs["VarPtrName"] = "%mux_output_var_ptr";
1599 specs["ResultStrategy"] = "%mux_output_var_ptr = OpSelect %sb_f32 %is_neg" + muxInput1 + muxInput2 + "\n";
1600
1601 fragments["capability"] = cap;
1602 fragments["decoration"] = decoration.specialize(specs);
1603 fragments["pre_main"] = preMain.specialize(specs);
1604 fragments["testfun"] = testFunction.specialize(specs);
1605
1606 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1607 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1608 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1609 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1610 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1611 }
1612 { // Variable Pointer Reads (using OpFunctionCall)
1613 GraphicsResources resources;
1614 map<string, string> specs;
1615 string name = "reads_opfunctioncall_" + bufferType;
1616 specs["ExtraTypes"] = "";
1617 specs["ExtraGlobalScopeVars"] = "";
1618 specs["ExtraFunctionScopeVars"] = "";
1619 specs["ExtraFunctions"] = selectorFunction;
1620 specs["VarPtrName"] = "%mux_output_var_ptr";
1621 specs["ResultStrategy"] = "%mux_output_var_ptr = OpFunctionCall %sb_f32 %choose_input_func %is_neg" + muxInput1 + muxInput2 + "\n";
1622
1623 fragments["capability"] = cap;
1624 fragments["decoration"] = decoration.specialize(specs);
1625 fragments["pre_main"] = preMain.specialize(specs);
1626 fragments["testfun"] = testFunction.specialize(specs);
1627
1628 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1629 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1630 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1631 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1632 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1633 }
1634 { // Variable Pointer Reads (using OpPhi)
1635 GraphicsResources resources;
1636 map<string, string> specs;
1637 string name = "reads_opphi_" + bufferType;
1638 specs["ExtraTypes"] = "";
1639 specs["ExtraGlobalScopeVars"] = "";
1640 specs["ExtraFunctionScopeVars"] = "";
1641 specs["ExtraFunctions"] = "";
1642 specs["VarPtrName"] = "%mux_output_var_ptr";
1643 specs["ResultStrategy"] =
1644 " OpSelectionMerge %end_label None\n"
1645 " OpBranchConditional %is_neg %take_mux_input_1 %take_mux_input_2\n"
1646 "%take_mux_input_1 = OpLabel\n"
1647 " OpBranch %end_label\n"
1648 "%take_mux_input_2 = OpLabel\n"
1649 " OpBranch %end_label\n"
1650 "%end_label = OpLabel\n"
1651 "%mux_output_var_ptr = OpPhi %sb_f32" + muxInput1 + "%take_mux_input_1" + muxInput2 + "%take_mux_input_2\n";
1652
1653 fragments["capability"] = cap;
1654 fragments["decoration"] = decoration.specialize(specs);
1655 fragments["pre_main"] = preMain.specialize(specs);
1656 fragments["testfun"] = testFunction.specialize(specs);
1657
1658 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1659 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1660 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1661 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1662 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1663 }
1664 { // Variable Pointer Reads (using OpCopyObject)
1665 GraphicsResources resources;
1666 map<string, string> specs;
1667 string name = "reads_opcopyobject_" + bufferType;
1668 specs["ExtraTypes"] = "";
1669 specs["ExtraGlobalScopeVars"] = "";
1670 specs["ExtraFunctionScopeVars"] = "";
1671 specs["ExtraFunctions"] = "";
1672 specs["VarPtrName"] = "%mux_output_var_ptr";
1673 specs["ResultStrategy"] =
1674 "%mux_input_1_copy = OpCopyObject %sb_f32" + muxInput1 + "\n"
1675 "%mux_input_2_copy = OpCopyObject %sb_f32" + muxInput2 + "\n"
1676 "%mux_output_var_ptr = OpSelect %sb_f32 %is_neg %mux_input_1_copy %mux_input_2_copy\n";
1677
1678 fragments["capability"] = cap;
1679 fragments["decoration"] = decoration.specialize(specs);
1680 fragments["pre_main"] = preMain.specialize(specs);
1681 fragments["testfun"] = testFunction.specialize(specs);
1682
1683 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1684 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1685 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1686 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1687 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1688 }
1689 { // Test storing into Private variables.
1690 const char* storageClasses[] = {"Private", "Function"};
1691 for (int classId = 0; classId < 2; ++classId)
1692 {
1693 GraphicsResources resources;
1694 map<string, string> specs;
1695 std::string storageClass = storageClasses[classId];
1696 std::string name = "stores_" + string(de::toLower(storageClass)) + "_" + bufferType;
1697 std::string extraVariable = "%mux_output_copy = OpVariable %sb_f32ptrptr " + storageClass + "\n";
1698 specs["ExtraTypes"] = "%sb_f32ptrptr = OpTypePointer " + storageClass + " %sb_f32\n";
1699 specs["ExtraGlobalScopeVars"] = (classId == 0) ? extraVariable : "";
1700 specs["ExtraFunctionScopeVars"] = (classId == 1) ? extraVariable : "";
1701 specs["ExtraFunctions"] = "";
1702 specs["VarPtrName"] = "%mux_output_var_ptr";
1703 specs["ResultStrategy"] =
1704 "%opselect_result = OpSelect %sb_f32 %is_neg" + muxInput1 + muxInput2 + "\n"
1705 " OpStore %mux_output_copy %opselect_result\n"
1706 "%mux_output_var_ptr = OpLoad %sb_f32 %mux_output_copy\n";
1707
1708 fragments["capability"] = cap;
1709 fragments["decoration"] = decoration.specialize(specs);
1710 fragments["pre_main"] = preMain.specialize(specs);
1711 fragments["testfun"] = testFunction.specialize(specs);
1712
1713 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1714 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1715 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1716 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1717 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1718 }
1719 }
1720 { // Variable Pointer Reads (using OpPtrAccessChain)
1721 GraphicsResources resources;
1722 map<string, string> specs;
1723 std::string name = "reads_opptraccesschain_" + bufferType;
1724 std::string in_1 = isSingleInputBuffer ? " %a_2i_ptr " : " %a_i_ptr ";
1725 std::string in_2 = isSingleInputBuffer ? " %a_2i_plus_1_ptr " : " %b_i_ptr ";
1726 specs["ExtraTypes"] = "";
1727 specs["ExtraGlobalScopeVars"] = "";
1728 specs["ExtraFunctionScopeVars"] = "";
1729 specs["ExtraFunctions"] = "";
1730 specs["VarPtrName"] = "%mux_output_var_ptr";
1731 specs["ResultStrategy"] =
1732 "%a_ptr = OpAccessChain %sb_f32 %indata_a %c_i32_0 %c_i32_0\n"
1733 "%b_ptr = OpAccessChain %sb_f32 %indata_b %c_i32_0 %c_i32_0\n"
1734 "%s_ptr = OpAccessChain %sb_f32 %indata_s %c_i32_0 %c_i32_0\n"
1735 "%out_ptr = OpAccessChain %sb_f32 %outdata %c_i32_0 %c_i32_0\n"
1736 "%a_i_ptr = OpPtrAccessChain %sb_f32 %a_ptr %30\n"
1737 "%b_i_ptr = OpPtrAccessChain %sb_f32 %b_ptr %30\n"
1738 "%s_i_ptr = OpPtrAccessChain %sb_f32 %s_ptr %30\n"
1739 "%a_2i_ptr = OpPtrAccessChain %sb_f32 %a_ptr %two_i\n"
1740 "%a_2i_plus_1_ptr = OpPtrAccessChain %sb_f32 %a_ptr %two_i_plus_1\n"
1741 "%mux_output_var_ptr = OpSelect %sb_f32 %is_neg " + in_1 + in_2 + "\n";
1742
1743 fragments["decoration"] = decoration.specialize(specs);
1744 fragments["pre_main"] = preMain.specialize(specs);
1745 fragments["testfun"] = testFunction.specialize(specs);
1746 fragments["capability"] = cap;
1747
1748 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1749 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1750 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1751 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1752 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1753 }
1754 { // Variable Pointer Writes
1755 GraphicsResources resources;
1756 map<string, string> specs;
1757 std::string name = "writes_" + bufferType;
1758 specs["ExtraTypes"] = "";
1759 specs["ExtraGlobalScopeVars"] = "";
1760 specs["ExtraFunctionScopeVars"] = "";
1761 specs["ExtraFunctions"] = "";
1762 specs["VarPtrName"] = "%mux_output_var_ptr";
1763 specs["ResultStrategy"] =
1764 "%mux_output_var_ptr = OpSelect %sb_f32 %is_neg" + muxInput1 + muxInput2 + "\n" +
1765 " %val = OpLoad %f32 %mux_output_var_ptr Aligned 4\n"
1766 " %val_plus_1 = OpFAdd %f32 %val %c_f32_1\n"
1767 " OpStore %mux_output_var_ptr %val_plus_1\n";
1768 fragments["capability"] = cap;
1769 fragments["decoration"] = decoration.specialize(specs);
1770 fragments["pre_main"] = preMain.specialize(specs);
1771 fragments["testfun"] = testFunction.specialize(specs);
1772
1773 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1774 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1775 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1776 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedIncrOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1777 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1778 }
1779 }
1780 }
1781
1782 // Modifies the 'red channel' of the input color to the given float value.
1783 // Returns the modified color.
getExpectedOutputColor(RGBA (& inputColors)[4],RGBA (& expectedOutputColors)[4],float val)1784 void getExpectedOutputColor(RGBA (&inputColors)[4], RGBA (&expectedOutputColors)[4], float val)
1785 {
1786 Vec4 inColor0 = inputColors[0].toVec();
1787 Vec4 inColor1 = inputColors[1].toVec();
1788 Vec4 inColor2 = inputColors[2].toVec();
1789 Vec4 inColor3 = inputColors[3].toVec();
1790 inColor0[0] = val;
1791 inColor1[0] = val;
1792 inColor2[0] = val;
1793 inColor3[0] = val;
1794 expectedOutputColors[0] = RGBA(inColor0);
1795 expectedOutputColors[1] = RGBA(inColor1);
1796 expectedOutputColors[2] = RGBA(inColor2);
1797 expectedOutputColors[3] = RGBA(inColor3);
1798 }
1799
addTwoInputBufferReadOnlyVariablePointersGraphicsGroup(tcu::TestCaseGroup * testGroup)1800 void addTwoInputBufferReadOnlyVariablePointersGraphicsGroup (tcu::TestCaseGroup* testGroup)
1801 {
1802 const int numFloatsPerInput = 64;
1803 vector<float> inputA (numFloatsPerInput, 0);
1804 vector<float> inputB (numFloatsPerInput, 0);
1805 deUint32 baseOffset = -1;
1806 VulkanFeatures requiredFeatures;
1807 map<string, string> fragments;
1808 RGBA defaultColors[4];
1809 RGBA expectedColors[4];
1810 vector<string> extensions;
1811
1812 getDefaultColors(defaultColors);
1813
1814 // Set the proper extension features required for the tests.
1815 requiredFeatures.extVariablePointers.variablePointers = true;
1816
1817 // Set the required extension.
1818 extensions.push_back("VK_KHR_variable_pointers");
1819
1820 // These tests exercise variable pointers into various levels of the following data-structure:
1821 // struct struct inner_struct {
1822 // vec4 x[2]; // array of 2 vectors. Each vector is 4 floats.
1823 // vec4 y[2]; // array of 2 vectors. Each vector is 4 floats.
1824 // };
1825 //
1826 // struct outer_struct {
1827 // inner_struct r[2][2];
1828 // };
1829 //
1830 // The inner_struct contains 16 floats, and the outer_struct contains 64 floats.
1831 // Therefore the input can be an array of 64 floats.
1832
1833 // Populate the first input (inputA) to contain: {0, 4, ... , 252} / 255.f
1834 // Populate the second input (inputB) to contain: {3, 7, ... , 255} / 255.f
1835 for (size_t i = 0; i < numFloatsPerInput; ++i)
1836 {
1837 inputA[i] = 4*float(i) / 255;
1838 inputB[i] = ((4*float(i)) + 3) / 255;
1839 }
1840
1841 // In the following tests we use variable pointers to point to different types:
1842 // nested structures, matrices of structures, arrays of structures, arrays of vectors, vectors of scalars, and scalars.
1843 // outer_structure.inner_structure[?][?].x[?][?];
1844 // ^ ^ ^ ^ ^ ^ ^
1845 //
1846 // 1. inputA or inputB = nested structure
1847 // 2. inputA.r or inputB.r = matrices of structures
1848 // 3. inputA.r[?] or inputB.r[?] = arrays of structures
1849 // 4. inputA.r[?][?] or inputB.r[?][?] = structures
1850 // 5. inputA.r[?][?].(x|y) or inputB.r[?][?].(x|y) = arrays of vectors
1851 // 6. inputA.r[?][?].(x|y)[?] or inputB.r[?][?].(x|y)[?] = vectors of scalars
1852 // 7. inputA.r[?][?].(x|y)[?][?] or inputB.r[?][?].(x|y)[?][?] = scalars
1853 const int numLevels = 7;
1854
1855 fragments["capability"] = "OpCapability VariablePointers \n";
1856 fragments["extension"] = "OpExtension \"SPV_KHR_variable_pointers\" \n"
1857 "OpExtension \"SPV_KHR_storage_buffer_storage_class\" \n";
1858
1859 const StringTemplate decoration (
1860 // Set the Offsets
1861 "OpMemberDecorate %outer_struct 0 Offset 0 \n"
1862 "OpMemberDecorate %inner_struct 0 Offset 0 \n"
1863 "OpMemberDecorate %inner_struct 1 Offset 32 \n"
1864
1865 // Set the ArrayStrides
1866 "OpDecorate %arr2_v4float ArrayStride 16 \n"
1867 "OpDecorate %arr2_inner_struct ArrayStride 64 \n"
1868 "OpDecorate %mat2x2_inner_struct ArrayStride 128 \n"
1869 "OpDecorate %mat2x2_ptr ArrayStride 128 \n"
1870 "OpDecorate %sb_buf ArrayStride 256 \n"
1871 "OpDecorate %v4f32_ptr ArrayStride 16 \n"
1872
1873 "OpDecorate %outer_struct Block \n"
1874
1875 "OpDecorate %in_a DescriptorSet 0 \n"
1876 "OpDecorate %in_b DescriptorSet 0 \n"
1877 "OpDecorate %in_a Binding 0 \n"
1878 "OpDecorate %in_b Binding 1 \n"
1879 "OpDecorate %in_a NonWritable \n"
1880 "OpDecorate %in_b NonWritable \n"
1881 );
1882
1883 const StringTemplate preMain (
1884 ///////////
1885 // TYPES //
1886 ///////////
1887
1888 // struct struct inner_struct {
1889 // vec4 x[2]; // array of 2 vectors
1890 // vec4 y[2]; // array of 2 vectors
1891 // };
1892 "%arr2_v4float = OpTypeArray %v4f32 %c_u32_2 \n"
1893 "%inner_struct = OpTypeStruct %arr2_v4float %arr2_v4float \n"
1894
1895 // struct outer_struct {
1896 // inner_struct r[2][2];
1897 // };
1898 "%arr2_inner_struct = OpTypeArray %inner_struct %c_u32_2 \n"
1899 "%mat2x2_inner_struct = OpTypeArray %arr2_inner_struct %c_u32_2 \n"
1900 "%outer_struct = OpTypeStruct %mat2x2_inner_struct \n"
1901
1902 ///////////////////
1903 // POINTER TYPES //
1904 ///////////////////
1905 "%sb_buf = OpTypePointer StorageBuffer %outer_struct \n"
1906 "%mat2x2_ptr = OpTypePointer StorageBuffer %mat2x2_inner_struct \n"
1907 "%arr2_ptr = OpTypePointer StorageBuffer %arr2_inner_struct \n"
1908 "%inner_struct_ptr = OpTypePointer StorageBuffer %inner_struct \n"
1909 "%arr_v4f32_ptr = OpTypePointer StorageBuffer %arr2_v4float \n"
1910 "%v4f32_ptr = OpTypePointer StorageBuffer %v4f32 \n"
1911 "%sb_f32ptr = OpTypePointer StorageBuffer %f32 \n"
1912
1913 ///////////////
1914 // VARIABLES //
1915 ///////////////
1916 "%in_a = OpVariable %sb_buf StorageBuffer \n"
1917 "%in_b = OpVariable %sb_buf StorageBuffer \n"
1918
1919 ///////////////
1920 // CONSTANTS //
1921 ///////////////
1922 "%c_bool_true = OpConstantTrue %bool \n"
1923 "%c_bool_false = OpConstantFalse %bool \n"
1924
1925 //////////////////////
1926 // HELPER FUNCTIONS //
1927 //////////////////////
1928 "${helper_functions} \n"
1929 );
1930
1931 const StringTemplate selectorFunctions (
1932 // This selector function returns a variable pointer.
1933 // These functions are used by tests that use OpFunctionCall to obtain the variable pointer.
1934 "%selector_func_type = OpTypeFunction ${selected_type} %bool ${selected_type} ${selected_type}\n"
1935 "%choose_input_func = OpFunction ${selected_type} None %selector_func_type\n"
1936 "%choose_first_param = OpFunctionParameter %bool\n"
1937 "%first_param = OpFunctionParameter ${selected_type}\n"
1938 "%second_param = OpFunctionParameter ${selected_type}\n"
1939 "%selector_func_begin = OpLabel\n"
1940 "%result_ptr = OpSelect ${selected_type} %choose_first_param %first_param %second_param\n"
1941 " OpReturnValue %result_ptr\n"
1942 " OpFunctionEnd\n"
1943 );
1944
1945 const StringTemplate testFunction (
1946 "%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
1947 "%param = OpFunctionParameter %v4f32\n"
1948 "%entry = OpLabel\n"
1949
1950 // Define base pointers for OpPtrAccessChain
1951 "%in_a_matptr = OpAccessChain %mat2x2_ptr %in_a %c_i32_0\n"
1952 "%in_b_matptr = OpAccessChain %mat2x2_ptr %in_b %c_i32_0\n"
1953
1954 // Define the 2 pointers from which we're going to choose one.
1955 "${a_loc} \n"
1956 "${b_loc} \n"
1957
1958 // Choose between the 2 pointers / variable pointers
1959 "${selection_strategy} \n"
1960
1961 // OpAccessChain into the variable pointer until you get to the float.
1962 "%result_loc = OpAccessChain %sb_f32ptr %var_ptr ${remaining_indexes} \n"
1963
1964 // Now load from the result_loc
1965 "%result_val = OpLoad %f32 %result_loc\n"
1966
1967 // Modify the 'RED channel' of the output color to the chosen value
1968 "%output_color = OpCompositeInsert %v4f32 %result_val %param 0 \n"
1969
1970 // Return and FunctionEnd
1971 "OpReturnValue %output_color\n"
1972 "OpFunctionEnd\n");
1973
1974 // When select is 0, the variable pointer should point to a value in the first input (inputA).
1975 // When select is 1, the variable pointer should point to a value in the second input (inputB).
1976 for (int selectInputA = 0; selectInputA < 2; ++selectInputA)
1977 {
1978 const string selectedInputStr = selectInputA ? "first_input" : "second_input";
1979 const string spirvSelectInputA = selectInputA ? "%c_bool_true" : "%c_bool_false";
1980 vector<float>& selectedInput = selectInputA ? inputA : inputB;
1981
1982 // The indexes chosen at each level. At any level, any given offset is exercised.
1983 // The first index is always zero as the outer structure has only 1 member.
1984 const int indexesForLevel[numLevels][6]= {{0, 0, 0, 0, 0, 1},
1985 {0, 1, 0, 1, 0, 2},
1986 {0, 0, 1, 0, 1, 3},
1987 {0, 1, 1, 1, 0, 0},
1988 {0, 0, 0, 1, 1, 1},
1989 {0, 1, 0, 0, 0, 2},
1990 {0, 1, 1, 1, 1, 3}};
1991
1992 const string indexLevelNames[] = {"_outer_struct_", "_matrices_", "_arrays_", "_inner_structs_", "_vec4arr_", "_vec4_", "_float_"};
1993 const string inputALocations[] = { "",
1994 "%a_loc = OpAccessChain %mat2x2_ptr %in_a %c_i32_0",
1995 "%a_loc = OpAccessChain %arr2_ptr %in_a %c_i32_0 %c_i32_0",
1996 "%a_loc = OpAccessChain %inner_struct_ptr %in_a %c_i32_0 %c_i32_1 %c_i32_1",
1997 "%a_loc = OpAccessChain %arr_v4f32_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
1998 "%a_loc = OpAccessChain %v4f32_ptr %in_a %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
1999 "%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"};
2000
2001 const string inputBLocations[] = { "",
2002 "%b_loc = OpAccessChain %mat2x2_ptr %in_b %c_i32_0",
2003 "%b_loc = OpAccessChain %arr2_ptr %in_b %c_i32_0 %c_i32_0",
2004 "%b_loc = OpAccessChain %inner_struct_ptr %in_b %c_i32_0 %c_i32_1 %c_i32_1",
2005 "%b_loc = OpAccessChain %arr_v4f32_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2006 "%b_loc = OpAccessChain %v4f32_ptr %in_b %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
2007 "%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"};
2008
2009 const string inputAPtrAccessChain[] = { "",
2010 "%a_loc = OpPtrAccessChain %mat2x2_ptr %in_a_matptr %c_i32_0",
2011 "%a_loc = OpPtrAccessChain %arr2_ptr %in_a_matptr %c_i32_0 %c_i32_0",
2012 "%a_loc = OpPtrAccessChain %inner_struct_ptr %in_a_matptr %c_i32_0 %c_i32_1 %c_i32_1",
2013 "%a_loc = OpPtrAccessChain %arr_v4f32_ptr %in_a_matptr %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2014 "%a_loc = OpPtrAccessChain %v4f32_ptr %in_a_matptr %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
2015 // Next case emulates:
2016 // %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
2017 // But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
2018 // %a_loc_arr is a pointer to an array that we want to index with 1.
2019 // But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
2020 // get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
2021 "%a_loc_arr = OpPtrAccessChain %arr_v4f32_ptr %in_a_matptr %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
2022 "%a_loc_first_elem = OpAccessChain %v4f32_ptr %a_loc_arr %c_i32_0 "
2023 "%a_loc = OpPtrAccessChain %sb_f32ptr %a_loc_first_elem %c_i32_1 %c_i32_3"};
2024
2025 const string inputBPtrAccessChain[] = { "",
2026 "%b_loc = OpPtrAccessChain %mat2x2_ptr %in_b_matptr %c_i32_0",
2027 "%b_loc = OpPtrAccessChain %arr2_ptr %in_b_matptr %c_i32_0 %c_i32_0",
2028 "%b_loc = OpPtrAccessChain %inner_struct_ptr %in_b_matptr %c_i32_0 %c_i32_1 %c_i32_1",
2029 "%b_loc = OpPtrAccessChain %arr_v4f32_ptr %in_b_matptr %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2030 "%b_loc = OpPtrAccessChain %v4f32_ptr %in_b_matptr %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
2031 // Next case emulates:
2032 // %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
2033 // But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
2034 // %b_loc_arr is a pointer to an array that we want to index with 1.
2035 // But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
2036 // get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
2037 "%b_loc_arr = OpPtrAccessChain %arr_v4f32_ptr %in_b_matptr %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
2038 "%b_loc_first_elem = OpAccessChain %v4f32_ptr %b_loc_arr %c_i32_0 "
2039 "%b_loc = OpPtrAccessChain %sb_f32ptr %b_loc_first_elem %c_i32_1 %c_i32_3"};
2040
2041
2042 const string remainingIndexesAtLevel[]= {"%c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2043 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_2",
2044 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_3",
2045 "%c_i32_1 %c_i32_0 %c_i32_0",
2046 "%c_i32_1 %c_i32_1",
2047 "%c_i32_2",
2048 ""};
2049
2050 const string pointerTypeAtLevel[] = {"%sb_buf", "%mat2x2_ptr", "%arr2_ptr", "%inner_struct_ptr", "%arr_v4f32_ptr", "%v4f32_ptr", "%sb_f32ptr"};
2051 const string baseANameAtLevel[] = {"%in_a", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc"};
2052 const string baseBNameAtLevel[] = {"%in_b", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc"};
2053
2054 for (int indexLevel = 0; indexLevel < numLevels; ++indexLevel)
2055 {
2056 // index into the outer structure must be zero since the outer structure has only 1 member.
2057 DE_ASSERT(indexesForLevel[indexLevel][0] == 0);
2058
2059 baseOffset = getBaseOffset(indexesForLevel[indexLevel][1],
2060 indexesForLevel[indexLevel][2],
2061 indexesForLevel[indexLevel][3],
2062 indexesForLevel[indexLevel][4],
2063 indexesForLevel[indexLevel][5]);
2064
2065 // Use OpSelect to choose between 2 pointers
2066 {
2067 GraphicsResources resources;
2068 map<string, string> specs;
2069 string opCodeForTests = "opselect";
2070 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2071 specs["select_inputA"] = spirvSelectInputA;
2072 specs["helper_functions"] = "";
2073 specs["a_loc"] = inputALocations[indexLevel];
2074 specs["b_loc"] = inputBLocations[indexLevel];
2075 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2076 specs["selection_strategy"] = "%var_ptr = OpSelect " +
2077 pointerTypeAtLevel[indexLevel] + " " +
2078 spirvSelectInputA + " " +
2079 baseANameAtLevel[indexLevel] + " " +
2080 baseBNameAtLevel[indexLevel] + "\n";
2081 fragments["decoration"] = decoration.specialize(specs);
2082 fragments["pre_main"] = preMain.specialize(specs);
2083 fragments["testfun"] = testFunction.specialize(specs);
2084 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2085 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2086 getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
2087 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2088 }
2089 // Use OpCopyObject to get variable pointers
2090 {
2091 GraphicsResources resources;
2092 map<string, string> specs;
2093 string opCodeForTests = "opcopyobject";
2094 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2095 specs["select_inputA"] = spirvSelectInputA;
2096 specs["helper_functions"] = "";
2097 specs["a_loc"] = inputALocations[indexLevel];
2098 specs["b_loc"] = inputBLocations[indexLevel];
2099 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2100 specs["selection_strategy"] =
2101 "%in_a_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseANameAtLevel[indexLevel] + "\n"
2102 "%in_b_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseBNameAtLevel[indexLevel] + "\n"
2103 "%var_ptr = OpSelect " + pointerTypeAtLevel[indexLevel] + " " + spirvSelectInputA + " %in_a_copy %in_b_copy\n";
2104 fragments["decoration"] = decoration.specialize(specs);
2105 fragments["pre_main"] = preMain.specialize(specs);
2106 fragments["testfun"] = testFunction.specialize(specs);
2107 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2108 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2109 getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
2110 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2111 }
2112 // Use OpPhi to choose between 2 pointers
2113 {
2114 GraphicsResources resources;
2115 map<string, string> specs;
2116 string opCodeForTests = "opphi";
2117 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2118 specs["select_inputA"] = spirvSelectInputA;
2119 specs["helper_functions"] = "";
2120 specs["a_loc"] = inputALocations[indexLevel];
2121 specs["b_loc"] = inputBLocations[indexLevel];
2122 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2123 specs["selection_strategy"] =
2124 " OpSelectionMerge %end_label None\n"
2125 " OpBranchConditional " + spirvSelectInputA + " %take_input_a %take_input_b\n"
2126 "%take_input_a = OpLabel\n"
2127 " OpBranch %end_label\n"
2128 "%take_input_b = OpLabel\n"
2129 " OpBranch %end_label\n"
2130 "%end_label = OpLabel\n"
2131 "%var_ptr = OpPhi "
2132 + pointerTypeAtLevel[indexLevel] + " "
2133 + baseANameAtLevel[indexLevel]
2134 + " %take_input_a "
2135 + baseBNameAtLevel[indexLevel]
2136 + " %take_input_b\n";
2137 fragments["decoration"] = decoration.specialize(specs);
2138 fragments["pre_main"] = preMain.specialize(specs);
2139 fragments["testfun"] = testFunction.specialize(specs);
2140 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2141 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2142 getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
2143 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2144 }
2145 // Use OpFunctionCall to choose between 2 pointers
2146 {
2147 GraphicsResources resources;
2148 map<string, string> functionSpecs;
2149 map<string, string> specs;
2150 string opCodeForTests = "opfunctioncall";
2151 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2152 functionSpecs["selected_type"] = pointerTypeAtLevel[indexLevel];
2153 specs["helper_functions"] = selectorFunctions.specialize(functionSpecs);
2154 specs["select_inputA"] = spirvSelectInputA;
2155 specs["a_loc"] = inputALocations[indexLevel];
2156 specs["b_loc"] = inputBLocations[indexLevel];
2157 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2158 specs["selection_strategy"] = "%var_ptr = OpFunctionCall "
2159 + pointerTypeAtLevel[indexLevel]
2160 + " %choose_input_func "
2161 + spirvSelectInputA + " "
2162 + baseANameAtLevel[indexLevel] + " "
2163 + baseBNameAtLevel[indexLevel] + "\n";
2164 fragments["decoration"] = decoration.specialize(specs);
2165 fragments["pre_main"] = preMain.specialize(specs);
2166 fragments["testfun"] = testFunction.specialize(specs);
2167 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2168 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2169 getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
2170 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2171 }
2172 // Use OpPtrAccessChain to get variable pointers
2173 {
2174 GraphicsResources resources;
2175 map<string, string> specs;
2176 string opCodeForTests = "opptraccesschain";
2177 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2178 specs["select_inputA"] = spirvSelectInputA;
2179 specs["helper_functions"] = "";
2180 specs["a_loc"] = inputAPtrAccessChain[indexLevel];
2181 specs["b_loc"] = inputBPtrAccessChain[indexLevel];
2182 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2183 specs["selection_strategy"] = "%var_ptr = OpSelect "
2184 + pointerTypeAtLevel[indexLevel] + " "
2185 + spirvSelectInputA + " "
2186 + baseANameAtLevel[indexLevel] + " "
2187 + baseBNameAtLevel[indexLevel] + "\n";
2188 fragments["decoration"] = decoration.specialize(specs);
2189 fragments["pre_main"] = preMain.specialize(specs);
2190 fragments["testfun"] = testFunction.specialize(specs);
2191 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2192 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2193 getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
2194 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2195 }
2196 }
2197 }
2198 }
2199
addSingleInputBufferReadOnlyVariablePointersGraphicsGroup(tcu::TestCaseGroup * testGroup)2200 void addSingleInputBufferReadOnlyVariablePointersGraphicsGroup (tcu::TestCaseGroup* testGroup)
2201 {
2202 const int numFloatsPerInnerStruct = 64;
2203 vector<float> inputBuffer (2 * numFloatsPerInnerStruct, 0);
2204 deUint32 baseOffset = -1;
2205 VulkanFeatures requiredFeatures;
2206 map<string, string> fragments;
2207 RGBA defaultColors[4];
2208 RGBA expectedColors[4];
2209 vector<string> extensions;
2210
2211 // Set the proper extension features required for the tests.
2212 // The following tests use variable pointers confined withing a single buffer.
2213 requiredFeatures.extVariablePointers.variablePointersStorageBuffer = true;
2214
2215 // Set the required extension.
2216 extensions.push_back("VK_KHR_variable_pointers");
2217
2218 getDefaultColors(defaultColors);
2219
2220 // These tests exercise variable pointers into various levels of the following data-structure:
2221 // struct struct inner_struct {
2222 // vec4 x[2]; // array of 2 vectors. Each vector is 4 floats.
2223 // vec4 y[2]; // array of 2 vectors. Each vector is 4 floats.
2224 // };
2225 //
2226 // struct outer_struct {
2227 // inner_struct r[2][2];
2228 // };
2229 //
2230 // struct input_buffer {
2231 // outer_struct a;
2232 // outer_struct b;
2233 // }
2234 //
2235 // The inner_struct contains 16 floats, and the outer_struct contains 64 floats.
2236 // Therefore the input_buffer can be an array of 128 floats.
2237
2238 // Populate input_buffer's first member (a) to contain: {0, 4, ... , 252} / 255.f
2239 // Populate input_buffer's second member (b) to contain: {3, 7, ... , 255} / 255.f
2240 for (size_t i = 0; i < numFloatsPerInnerStruct; ++i)
2241 {
2242 inputBuffer[i] = 4*float(i) / 255;
2243 inputBuffer[i + numFloatsPerInnerStruct] = ((4*float(i)) + 3) / 255;
2244 }
2245
2246 // In the following tests we use variable pointers to point to different types:
2247 // nested structures, matrices of structures, arrays of structures, arrays of vectors, vectors of scalars, and scalars.
2248 // outer_struct.inner_struct[?][?].x[?][?];
2249 // ^ ^ ^ ^ ^ ^ ^
2250 //
2251 // 1. inputBuffer.a or inputBuffer.b = nested structure
2252 // 2. inputBuffer.a.r or inputBuffer.b.r = matrices of structures
2253 // 3. inputBuffer.a.r[?] or inputBuffer.b.r[?] = arrays of structures
2254 // 4. inputBuffer.a.r[?][?] or inputBuffer.b.r[?][?] = structures
2255 // 5. inputBuffer.a.r[?][?].(x|y) or inputBuffer.b.r[?][?].(x|y) = arrays of vectors
2256 // 6. inputBuffer.a.r[?][?].(x|y)[?] or inputBuffer.b.r[?][?].(x|y)[?] = vectors of scalars
2257 // 7. inputBuffer.a.r[?][?].(x|y)[?][?] or inputBuffer.b.r[?][?].(x|y)[?][?] = scalars
2258 const int numLevels = 7;
2259
2260 fragments["capability"] = "OpCapability VariablePointersStorageBuffer \n";
2261 fragments["extension"] = "OpExtension \"SPV_KHR_variable_pointers\" \n"
2262 "OpExtension \"SPV_KHR_storage_buffer_storage_class\" \n";
2263 const StringTemplate decoration (
2264 // Set the ArrayStrides
2265 "OpDecorate %arr2_v4float ArrayStride 16 \n"
2266 "OpDecorate %arr2_inner_struct ArrayStride 64 \n"
2267 "OpDecorate %mat2x2_inner_struct ArrayStride 128 \n"
2268 "OpDecorate %outer_struct_ptr ArrayStride 256 \n"
2269 "OpDecorate %v4f32_ptr ArrayStride 16 \n"
2270
2271 // Set the Offsets
2272 "OpMemberDecorate %input_buffer 0 Offset 0 \n"
2273 "OpMemberDecorate %input_buffer 1 Offset 256 \n"
2274 "OpMemberDecorate %outer_struct 0 Offset 0 \n"
2275 "OpMemberDecorate %inner_struct 0 Offset 0 \n"
2276 "OpMemberDecorate %inner_struct 1 Offset 32 \n"
2277
2278 "OpDecorate %input_buffer Block \n"
2279
2280 "OpDecorate %input DescriptorSet 0 \n"
2281 "OpDecorate %input Binding 0 \n"
2282 "OpDecorate %input NonWritable \n"
2283 );
2284
2285 const StringTemplate preMain (
2286 ///////////
2287 // TYPES //
2288 ///////////
2289
2290 // struct struct inner_struct {
2291 // vec4 x[2]; // array of 2 vectors
2292 // vec4 y[2]; // array of 2 vectors
2293 // };
2294 "%arr2_v4float = OpTypeArray %v4f32 %c_u32_2 \n"
2295 "%inner_struct = OpTypeStruct %arr2_v4float %arr2_v4float \n"
2296
2297 // struct outer_struct {
2298 // inner_struct r[2][2];
2299 // };
2300 "%arr2_inner_struct = OpTypeArray %inner_struct %c_u32_2 \n"
2301 "%mat2x2_inner_struct = OpTypeArray %arr2_inner_struct %c_u32_2 \n"
2302 "%outer_struct = OpTypeStruct %mat2x2_inner_struct \n"
2303
2304 // struct input_buffer {
2305 // outer_struct a;
2306 // outer_struct b;
2307 // }
2308 "%input_buffer = OpTypeStruct %outer_struct %outer_struct \n"
2309
2310 ///////////////////
2311 // POINTER TYPES //
2312 ///////////////////
2313 "%input_buffer_ptr = OpTypePointer StorageBuffer %input_buffer \n"
2314 "%outer_struct_ptr = OpTypePointer StorageBuffer %outer_struct \n"
2315 "%mat2x2_ptr = OpTypePointer StorageBuffer %mat2x2_inner_struct \n"
2316 "%arr2_ptr = OpTypePointer StorageBuffer %arr2_inner_struct \n"
2317 "%inner_struct_ptr = OpTypePointer StorageBuffer %inner_struct \n"
2318 "%arr_v4f32_ptr = OpTypePointer StorageBuffer %arr2_v4float \n"
2319 "%v4f32_ptr = OpTypePointer StorageBuffer %v4f32 \n"
2320 "%sb_f32ptr = OpTypePointer StorageBuffer %f32 \n"
2321
2322 ///////////////
2323 // VARIABLES //
2324 ///////////////
2325 "%input = OpVariable %input_buffer_ptr StorageBuffer \n"
2326
2327 ///////////////
2328 // CONSTANTS //
2329 ///////////////
2330 "%c_bool_true = OpConstantTrue %bool \n"
2331 "%c_bool_false = OpConstantFalse %bool \n"
2332
2333 //////////////////////
2334 // HELPER FUNCTIONS //
2335 //////////////////////
2336 "${helper_functions} \n"
2337 );
2338
2339 const StringTemplate selectorFunctions (
2340 // These selector functions return variable pointers.
2341 // These functions are used by tests that use OpFunctionCall to obtain the variable pointer
2342 "%selector_func_type = OpTypeFunction ${selected_type} %bool ${selected_type} ${selected_type}\n"
2343 "%choose_input_func = OpFunction ${selected_type} None %selector_func_type\n"
2344 "%choose_first_param = OpFunctionParameter %bool\n"
2345 "%first_param = OpFunctionParameter ${selected_type}\n"
2346 "%second_param = OpFunctionParameter ${selected_type}\n"
2347 "%selector_func_begin = OpLabel\n"
2348 "%result_ptr = OpSelect ${selected_type} %choose_first_param %first_param %second_param\n"
2349 "OpReturnValue %result_ptr\n"
2350 "OpFunctionEnd\n"
2351 );
2352
2353 const StringTemplate testFunction (
2354 "%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
2355 "%param = OpFunctionParameter %v4f32\n"
2356 "%entry = OpLabel\n"
2357
2358 // Here are the 2 nested structures:
2359 "%in_a = OpAccessChain %outer_struct_ptr %input %c_i32_0\n"
2360 "%in_b = OpAccessChain %outer_struct_ptr %input %c_i32_1\n"
2361
2362 // Define the 2 pointers from which we're going to choose one.
2363 "${a_loc} \n"
2364 "${b_loc} \n"
2365
2366 // Choose between the 2 pointers / variable pointers
2367 "${selection_strategy} \n"
2368
2369 // OpAccessChain into the variable pointer until you get to the float.
2370 "%result_loc = OpAccessChain %sb_f32ptr %var_ptr ${remaining_indexes} \n"
2371
2372
2373 // Now load from the result_loc
2374 "%result_val = OpLoad %f32 %result_loc\n"
2375
2376 // Modify the 'RED channel' of the output color to the chosen value
2377 "%output_color = OpCompositeInsert %v4f32 %result_val %param 0 \n"
2378
2379 // Return and FunctionEnd
2380 "OpReturnValue %output_color\n"
2381 "OpFunctionEnd\n");
2382
2383 // When select is 0, the variable pointer should point to a value in the first input_buffer member (a).
2384 // When select is 1, the variable pointer should point to a value in the second input_buffer member (b).
2385 // Since the 2 members of the input_buffer (a and b) are of type outer_struct, we can conveniently use
2386 // the same indexing scheme that we used for the 2-input-buffer tests.
2387 for (int selectInputA = 0; selectInputA < 2; ++selectInputA)
2388 {
2389 const string selectedInputStr = selectInputA ? "first_input" : "second_input";
2390 const string spirvSelectInputA = selectInputA ? "%c_bool_true" : "%c_bool_false";
2391 const int outerStructIndex = selectInputA ? 0 : 1;
2392
2393 // The indexes chosen at each level. At any level, any given offset is exercised.
2394 // outerStructIndex is 0 for member (a) and 1 for member (b).
2395 const int indexesForLevel[numLevels][6]= {{outerStructIndex, 0, 0, 0, 0, 1},
2396 {outerStructIndex, 1, 0, 1, 0, 2},
2397 {outerStructIndex, 0, 1, 0, 1, 3},
2398 {outerStructIndex, 1, 1, 1, 0, 0},
2399 {outerStructIndex, 0, 0, 1, 1, 1},
2400 {outerStructIndex, 1, 0, 0, 0, 2},
2401 {outerStructIndex, 1, 1, 1, 1, 3}};
2402
2403 const string indexLevelNames[] = {"_outer_struct_", "_matrices_", "_arrays_", "_inner_structs_", "_vec4arr_", "_vec4_", "_float_"};
2404 const string inputALocations[] = { "",
2405 "%a_loc = OpAccessChain %mat2x2_ptr %in_a %c_i32_0",
2406 "%a_loc = OpAccessChain %arr2_ptr %in_a %c_i32_0 %c_i32_0",
2407 "%a_loc = OpAccessChain %inner_struct_ptr %in_a %c_i32_0 %c_i32_1 %c_i32_1",
2408 "%a_loc = OpAccessChain %arr_v4f32_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2409 "%a_loc = OpAccessChain %v4f32_ptr %in_a %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
2410 "%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"};
2411
2412 const string inputBLocations[] = { "",
2413 "%b_loc = OpAccessChain %mat2x2_ptr %in_b %c_i32_0",
2414 "%b_loc = OpAccessChain %arr2_ptr %in_b %c_i32_0 %c_i32_0",
2415 "%b_loc = OpAccessChain %inner_struct_ptr %in_b %c_i32_0 %c_i32_1 %c_i32_1",
2416 "%b_loc = OpAccessChain %arr_v4f32_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2417 "%b_loc = OpAccessChain %v4f32_ptr %in_b %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
2418 "%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"};
2419
2420 const string inputAPtrAccessChain[] = { "",
2421 "%a_loc = OpPtrAccessChain %mat2x2_ptr %in_a %c_i32_0 %c_i32_0",
2422 "%a_loc = OpPtrAccessChain %arr2_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_0",
2423 "%a_loc = OpPtrAccessChain %inner_struct_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1",
2424 "%a_loc = OpPtrAccessChain %arr_v4f32_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2425 "%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",
2426 // Next case emulates:
2427 // %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
2428 // But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
2429 // %a_loc_arr is a pointer to an array that we want to index with 1.
2430 // But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
2431 // get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
2432 "%a_loc_arr = OpPtrAccessChain %arr_v4f32_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
2433 "%a_loc_first_elem = OpAccessChain %v4f32_ptr %a_loc_arr %c_i32_0 "
2434 "%a_loc = OpPtrAccessChain %sb_f32ptr %a_loc_first_elem %c_i32_1 %c_i32_3"};
2435
2436 const string inputBPtrAccessChain[] = { "",
2437 "%b_loc = OpPtrAccessChain %mat2x2_ptr %in_b %c_i32_0 %c_i32_0",
2438 "%b_loc = OpPtrAccessChain %arr2_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_0",
2439 "%b_loc = OpPtrAccessChain %inner_struct_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1",
2440 "%b_loc = OpPtrAccessChain %arr_v4f32_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2441 "%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",
2442 // Next case emulates:
2443 // %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
2444 // But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
2445 // %b_loc_arr is a pointer to an array that we want to index with 1.
2446 // But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
2447 // get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
2448 "%b_loc_arr = OpPtrAccessChain %arr_v4f32_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
2449 "%b_loc_first_elem = OpAccessChain %v4f32_ptr %b_loc_arr %c_i32_0 "
2450 "%b_loc = OpPtrAccessChain %sb_f32ptr %b_loc_first_elem %c_i32_1 %c_i32_3"};
2451
2452
2453 const string remainingIndexesAtLevel[]= {"%c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2454 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_2",
2455 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_3",
2456 "%c_i32_1 %c_i32_0 %c_i32_0",
2457 "%c_i32_1 %c_i32_1",
2458 "%c_i32_2",
2459 ""};
2460
2461 const string pointerTypeAtLevel[] = {"%outer_struct_ptr", "%mat2x2_ptr", "%arr2_ptr", "%inner_struct_ptr", "%arr_v4f32_ptr", "%v4f32_ptr", "%sb_f32ptr"};
2462 const string baseANameAtLevel[] = {"%in_a", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc"};
2463 const string baseBNameAtLevel[] = {"%in_b", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc"};
2464
2465 for (int indexLevel = 0; indexLevel < numLevels; ++indexLevel)
2466 {
2467 // Use OpSelect to choose between 2 pointers
2468 {
2469 GraphicsResources resources;
2470 map<string, string> specs;
2471 string opCodeForTests = "opselect";
2472 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2473 specs["select_inputA"] = spirvSelectInputA;
2474 specs["helper_functions"] = "";
2475 specs["a_loc"] = inputALocations[indexLevel];
2476 specs["b_loc"] = inputBLocations[indexLevel];
2477 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2478 specs["selection_strategy"] = "%var_ptr = OpSelect " +
2479 pointerTypeAtLevel[indexLevel] + " " +
2480 spirvSelectInputA + " " +
2481 baseANameAtLevel[indexLevel] + " " +
2482 baseBNameAtLevel[indexLevel] + "\n";
2483 baseOffset = getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2484 indexesForLevel[indexLevel][1],
2485 indexesForLevel[indexLevel][2],
2486 indexesForLevel[indexLevel][3],
2487 indexesForLevel[indexLevel][4],
2488 indexesForLevel[indexLevel][5]);
2489 fragments["decoration"] = decoration.specialize(specs);
2490 fragments["pre_main"] = preMain.specialize(specs);
2491 fragments["testfun"] = testFunction.specialize(specs);
2492 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2493 getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2494 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2495 }
2496 // Use OpCopyObject to get variable pointers
2497 {
2498 GraphicsResources resources;
2499 map<string, string> specs;
2500 string opCodeForTests = "opcopyobject";
2501 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2502 specs["select_inputA"] = spirvSelectInputA;
2503 specs["helper_functions"] = "";
2504 specs["a_loc"] = inputALocations[indexLevel];
2505 specs["b_loc"] = inputBLocations[indexLevel];
2506 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2507 specs["selection_strategy"] =
2508 "%in_a_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseANameAtLevel[indexLevel] + "\n"
2509 "%in_b_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseBNameAtLevel[indexLevel] + "\n"
2510 "%var_ptr = OpSelect " + pointerTypeAtLevel[indexLevel] + " " + spirvSelectInputA + " %in_a_copy %in_b_copy\n";
2511 baseOffset = getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2512 indexesForLevel[indexLevel][1],
2513 indexesForLevel[indexLevel][2],
2514 indexesForLevel[indexLevel][3],
2515 indexesForLevel[indexLevel][4],
2516 indexesForLevel[indexLevel][5]);
2517 fragments["decoration"] = decoration.specialize(specs);
2518 fragments["pre_main"] = preMain.specialize(specs);
2519 fragments["testfun"] = testFunction.specialize(specs);
2520 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2521 getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2522 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2523 }
2524 // Use OpPhi to choose between 2 pointers
2525 {
2526 GraphicsResources resources;
2527 map<string, string> specs;
2528 string opCodeForTests = "opphi";
2529 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2530 specs["select_inputA"] = spirvSelectInputA;
2531 specs["helper_functions"] = "";
2532 specs["a_loc"] = inputALocations[indexLevel];
2533 specs["b_loc"] = inputBLocations[indexLevel];
2534 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2535 specs["selection_strategy"] =
2536 " OpSelectionMerge %end_label None\n"
2537 " OpBranchConditional " + spirvSelectInputA + " %take_input_a %take_input_b\n"
2538 "%take_input_a = OpLabel\n"
2539 " OpBranch %end_label\n"
2540 "%take_input_b = OpLabel\n"
2541 " OpBranch %end_label\n"
2542 "%end_label = OpLabel\n"
2543 "%var_ptr = OpPhi "
2544 + pointerTypeAtLevel[indexLevel] + " "
2545 + baseANameAtLevel[indexLevel]
2546 + " %take_input_a "
2547 + baseBNameAtLevel[indexLevel]
2548 + " %take_input_b\n";
2549 baseOffset = getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2550 indexesForLevel[indexLevel][1],
2551 indexesForLevel[indexLevel][2],
2552 indexesForLevel[indexLevel][3],
2553 indexesForLevel[indexLevel][4],
2554 indexesForLevel[indexLevel][5]);
2555 fragments["decoration"] = decoration.specialize(specs);
2556 fragments["pre_main"] = preMain.specialize(specs);
2557 fragments["testfun"] = testFunction.specialize(specs);
2558 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2559 getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2560 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2561 }
2562 // Use OpFunctionCall to choose between 2 pointers
2563 {
2564 GraphicsResources resources;
2565 map<string, string> functionSpecs;
2566 map<string, string> specs;
2567 string opCodeForTests = "opfunctioncall";
2568 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2569 //string selectedType = "%mat2x2_ptr";
2570 functionSpecs["selected_type"] = pointerTypeAtLevel[indexLevel];
2571 specs["helper_functions"] = selectorFunctions.specialize(functionSpecs);
2572 specs["select_inputA"] = spirvSelectInputA;
2573 specs["a_loc"] = inputALocations[indexLevel];
2574 specs["b_loc"] = inputBLocations[indexLevel];
2575 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2576 specs["selection_strategy"] = "%var_ptr = OpFunctionCall "
2577 + pointerTypeAtLevel[indexLevel]
2578 + " %choose_input_func "
2579 + spirvSelectInputA + " "
2580 + baseANameAtLevel[indexLevel] + " "
2581 + baseBNameAtLevel[indexLevel] + "\n";
2582 baseOffset = getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2583 indexesForLevel[indexLevel][1],
2584 indexesForLevel[indexLevel][2],
2585 indexesForLevel[indexLevel][3],
2586 indexesForLevel[indexLevel][4],
2587 indexesForLevel[indexLevel][5]);
2588 fragments["decoration"] = decoration.specialize(specs);
2589 fragments["pre_main"] = preMain.specialize(specs);
2590 fragments["testfun"] = testFunction.specialize(specs);
2591 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2592 getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2593 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2594 }
2595 // Use OpPtrAccessChain to get variable pointers
2596 {
2597 GraphicsResources resources;
2598 map<string, string> specs;
2599 string opCodeForTests = "opptraccesschain";
2600 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2601 specs["select_inputA"] = spirvSelectInputA;
2602 specs["helper_functions"] = "";
2603 specs["a_loc"] = inputAPtrAccessChain[indexLevel];
2604 specs["b_loc"] = inputBPtrAccessChain[indexLevel];
2605 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2606 specs["selection_strategy"] = "%var_ptr = OpSelect "
2607 + pointerTypeAtLevel[indexLevel] + " "
2608 + spirvSelectInputA + " "
2609 + baseANameAtLevel[indexLevel] + " "
2610 + baseBNameAtLevel[indexLevel] + "\n";
2611 baseOffset = getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2612 indexesForLevel[indexLevel][1],
2613 indexesForLevel[indexLevel][2],
2614 indexesForLevel[indexLevel][3],
2615 indexesForLevel[indexLevel][4],
2616 indexesForLevel[indexLevel][5]);
2617 fragments["decoration"] = decoration.specialize(specs);
2618 fragments["pre_main"] = preMain.specialize(specs);
2619 fragments["testfun"] = testFunction.specialize(specs);
2620 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2621 getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2622 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2623 }
2624 }
2625 }
2626 }
2627
addNullptrVariablePointersGraphicsGroup(tcu::TestCaseGroup * testGroup)2628 void addNullptrVariablePointersGraphicsGroup (tcu::TestCaseGroup* testGroup)
2629 {
2630 float someFloat = 78 / 255.f;
2631 vector<float> input (1, someFloat);
2632 vector<float> expectedOutput (1, someFloat);
2633 VulkanFeatures requiredFeatures;
2634 map<string, string> fragments;
2635 RGBA defaultColors[4];
2636 RGBA expectedColors[4];
2637 vector<string> extensions;
2638
2639 getDefaultColors(defaultColors);
2640 getExpectedOutputColor(defaultColors, expectedColors, someFloat);
2641
2642 // Set the required extension.
2643 extensions.push_back("VK_KHR_variable_pointers");
2644
2645 // Requires the variable pointers feature.
2646 requiredFeatures.extVariablePointers.variablePointers = true;
2647
2648 fragments["capability"] = "OpCapability VariablePointers \n";
2649 fragments["extension"] = "OpExtension \"SPV_KHR_variable_pointers\" \n"
2650 "OpExtension \"SPV_KHR_storage_buffer_storage_class\" \n";
2651 const StringTemplate decoration (
2652 // Decorations
2653 "OpDecorate %input DescriptorSet 0 \n"
2654 "OpDecorate %input Binding 0 \n"
2655 "OpDecorate %input NonWritable \n"
2656
2657 // Set the Block decoration
2658 "OpDecorate %float_struct Block \n"
2659
2660 // Set the Offsets
2661 "OpMemberDecorate %float_struct 0 Offset 0 \n"
2662 );
2663
2664 const StringTemplate preMain (
2665 // struct float_struct {
2666 // float x;
2667 // };
2668 "%float_struct = OpTypeStruct %f32 \n"
2669
2670 // POINTER TYPES
2671 "%float_struct_ptr = OpTypePointer StorageBuffer %float_struct \n"
2672 "%sb_f32ptr = OpTypePointer StorageBuffer %f32 \n"
2673 "%func_f32ptrptr = OpTypePointer Function %sb_f32ptr \n"
2674
2675 // CONSTANTS
2676 "%c_bool_true = OpConstantTrue %bool \n"
2677 "%c_null_ptr = OpConstantNull %sb_f32ptr \n"
2678
2679 // VARIABLES
2680 "%input = OpVariable %float_struct_ptr StorageBuffer \n"
2681 );
2682
2683 const StringTemplate testFunction (
2684 "%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
2685 "%param = OpFunctionParameter %v4f32\n"
2686 "%entry = OpLabel\n"
2687
2688 // Note that the Variable Pointers extension allows creation
2689 // of a pointer variable with storage class of Private or Function.
2690 "%f32_ptr_var = OpVariable %func_f32ptrptr Function %c_null_ptr\n"
2691
2692 "%input_loc = OpAccessChain %sb_f32ptr %input %c_i32_0\n"
2693
2694 // Null testing strategy
2695 "${NullptrTestingStrategy}\n"
2696 // Modify the 'RED channel' of the output color to the chosen value
2697 "%output_color = OpCompositeInsert %v4f32 %result_val %param 0 \n"
2698 // Return and FunctionEnd
2699 "OpReturnValue %output_color\n"
2700 "OpFunctionEnd\n");
2701
2702 // f32_ptr_var has been inintialized to NULL.
2703 // Now set it to the input variable and return it as output
2704 {
2705 GraphicsResources resources;
2706 map<string, string> specs;
2707 specs["NullptrTestingStrategy"] =
2708 " OpStore %f32_ptr_var %input_loc \n"
2709 "%loaded_f32_ptr = OpLoad %sb_f32ptr %f32_ptr_var \n"
2710 "%result_val = OpLoad %f32 %loaded_f32_ptr \n";
2711 fragments["decoration"] = decoration.specialize(specs);
2712 fragments["pre_main"] = preMain.specialize(specs);
2713 fragments["testfun"] = testFunction.specialize(specs);
2714 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(input)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2715 createTestsForAllStages("opvariable_initialized_null", defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2716 }
2717 // Use OpSelect to choose between nullptr and a valid pointer. Since we can't dereference nullptr,
2718 // it is forced to always choose the valid pointer.
2719 {
2720 GraphicsResources resources;
2721 map<string, string> specs;
2722 specs["NullptrTestingStrategy"] = "%selected_ptr = OpSelect %sb_f32ptr %c_bool_true %input_loc %c_null_ptr\n"
2723 "%result_val = OpLoad %f32 %selected_ptr\n";
2724 fragments["decoration"] = decoration.specialize(specs);
2725 fragments["pre_main"] = preMain.specialize(specs);
2726 fragments["testfun"] = testFunction.specialize(specs);
2727 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(input)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2728 createTestsForAllStages("opselect_null_or_valid_ptr", defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2729 }
2730 }
2731
2732 } // anonymous
2733
createVariablePointersComputeGroup(tcu::TestContext & testCtx)2734 tcu::TestCaseGroup* createVariablePointersComputeGroup (tcu::TestContext& testCtx)
2735 {
2736 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "variable_pointers", "Compute tests for SPV_KHR_variable_pointers extension"));
2737 addTestGroup(group.get(), "compute", "Test the variable pointer extension using a compute shader", addVariablePointersComputeGroup);
2738 addTestGroup(group.get(),
2739 "complex_types_compute",
2740 "Testing Variable Pointers pointing to various types in different input buffers",
2741 addComplexTypesVariablePointersComputeGroup);
2742 addTestGroup(group.get(),
2743 "nullptr_compute",
2744 "Test the usage of nullptr using the variable pointers extension in a compute shader",
2745 addNullptrVariablePointersComputeGroup);
2746 addTestGroup(group.get(), "dynamic_offset",
2747 "Testing variable pointers referring to descriptors using dynamic offset",
2748 addDynamicOffsetComputeGroup);
2749
2750 return group.release();
2751 }
2752
createPhysicalPointersComputeGroup(tcu::TestContext & testCtx)2753 tcu::TestCaseGroup* createPhysicalPointersComputeGroup (tcu::TestContext& testCtx)
2754 {
2755 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "physical_pointers", "Compute tests for SPV_KHR_physical_storage_buffer extension"));
2756 addTestGroup(group.get(), "compute", "Test the physical storage buffer extension using a compute shader", addPhysicalPointersComputeGroup);
2757 addTestGroup(group.get(),
2758 "complex_types_compute",
2759 "Testing physical pointers pointing to various types in different input buffers",
2760 addComplexTypesPhysicalPointersComputeGroup);
2761
2762 return group.release();
2763 }
2764
createVariablePointersGraphicsGroup(tcu::TestContext & testCtx)2765 tcu::TestCaseGroup* createVariablePointersGraphicsGroup (tcu::TestContext& testCtx)
2766 {
2767 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "variable_pointers", "Graphics tests for SPV_KHR_variable_pointers extension"));
2768
2769 addTestGroup(group.get(), "graphics", "Testing Variable Pointers in graphics pipeline", addVariablePointersGraphicsGroup);
2770 addTestGroup(group.get(),
2771 "multi_buffer_read_only_graphics",
2772 "Testing Variable Pointers pointing to different input buffers in graphics pipeline (no SSBO writes)",
2773 addTwoInputBufferReadOnlyVariablePointersGraphicsGroup);
2774 addTestGroup(group.get(),
2775 "single_buffer_read_only_graphics",
2776 "Testing Variable Pointers confined to a single input buffer in graphics pipeline (no SSBO writes)",
2777 addSingleInputBufferReadOnlyVariablePointersGraphicsGroup);
2778 addTestGroup(group.get(),
2779 "nullptr_graphics",
2780 "Test the usage of nullptr using the variable pointers extension in graphics pipeline",
2781 addNullptrVariablePointersGraphicsGroup);
2782
2783 return group.release();
2784 }
2785
2786 } // SpirVAssembly
2787 } // vkt
2788