1 /*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
5 * Copyright (c) 2017 Google Inc.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief SPIR-V Assembly Tests for the SPV_KHR_variable_pointers extension
22 *//*--------------------------------------------------------------------*/
24 #include "tcuFloat.hpp"
25 #include "tcuRGBA.hpp"
26 #include "tcuStringTemplate.hpp"
27 #include "tcuTestLog.hpp"
28 #include "tcuVectorUtil.hpp"
31 #include "vkDeviceUtil.hpp"
32 #include "vkMemUtil.hpp"
33 #include "vkPlatform.hpp"
34 #include "vkPrograms.hpp"
35 #include "vkQueryUtil.hpp"
37 #include "vkRefUtil.hpp"
38 #include "vkStrUtil.hpp"
39 #include "vkTypeUtil.hpp"
41 #include "deRandom.hpp"
42 #include "deStringUtil.hpp"
43 #include "deUniquePtr.hpp"
46 #include "vktSpvAsmComputeShaderCase.hpp"
47 #include "vktSpvAsmComputeShaderTestUtil.hpp"
48 #include "vktSpvAsmGraphicsShaderTestUtil.hpp"
49 #include "vktSpvAsmVariablePointersTests.hpp"
50 #include "vktTestCaseUtil.hpp"
51 #include "vktTestGroupUtil.hpp"
61 namespace SpirVAssembly
72 using tcu::TestStatus;
75 using tcu::StringTemplate;
82 void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
84 T* const typedPtr = (T*)dst;
85 for (int ndx = 0; ndx < numValues; ndx++)
86 typedPtr[offset + ndx] = randomScalar<T>(rnd, minValue, maxValue);
89 // The following structure (outer_struct) is passed as a vector of 64 32-bit floats into some shaders.
91 // struct struct inner_struct {
96 // struct outer_struct {
97 // inner_struct r[2][2];
100 // This method finds the correct offset from the base of a vector<float32> given the indexes into the structure.
101 // Returns the index in the inclusive range of 0 and 63. Each unit of the offset represents offset by the size of a 32-bit float.
102 deUint32 getBaseOffset (deUint32 indexMatrixRow,
103 deUint32 indexMatrixCol,
104 deUint32 indexInnerStruct,
105 deUint32 indexVec4Array,
108 DE_ASSERT(indexMatrixRow < 2);
109 DE_ASSERT(indexMatrixCol < 2);
110 DE_ASSERT(indexInnerStruct < 2);
111 DE_ASSERT(indexVec4Array < 2);
112 DE_ASSERT(indexVec4 < 4);
116 // We have a matrix of 2 rows and 2 columns (total of 4 inner_structs). Each inner_struct contains 16 floats.
117 // So, offset by 1 row means offset by 32 floats, and offset by 1 column means offset by 16 floats.
118 offset += indexMatrixRow * 32;
119 offset += indexMatrixCol * 16;
121 // The inner structure contains 2 members, each having 8 floats.
122 // So offset by 1 in the inner struct means offset by 8 floats.
123 offset += indexInnerStruct * 8;
125 // Each member (x|y) have 2 vectors of 4 floats. So, offset by 1 int the vec4 array means an offset by 4 floats.
126 offset += indexVec4Array * 4;
128 // Each vec4 contains 4 floats, so each offset in the vec4 means offset by 1 float.
134 // The following structure (input_buffer) is passed as a vector of 128 32-bit floats into some shaders.
136 // struct struct inner_struct {
141 // struct outer_struct {
142 // inner_struct r[2][2];
145 // struct input_buffer {
150 // This method finds the correct offset from the base of a vector<float32> given the indexes into the structure.
151 // Returns the index in the inclusive range of 0 and 127.
152 deUint32 getBaseOffsetForSingleInputBuffer (deUint32 indexOuterStruct,
153 deUint32 indexMatrixRow,
154 deUint32 indexMatrixCol,
155 deUint32 indexInnerStruct,
156 deUint32 indexVec4Array,
159 DE_ASSERT(indexOuterStruct < 2);
160 DE_ASSERT(indexMatrixRow < 2);
161 DE_ASSERT(indexMatrixCol < 2);
162 DE_ASSERT(indexInnerStruct < 2);
163 DE_ASSERT(indexVec4Array < 2);
164 DE_ASSERT(indexVec4 < 4);
166 // Get the offset assuming you have only one outer_struct.
167 deUint32 offset = getBaseOffset(indexMatrixRow, indexMatrixCol, indexInnerStruct, indexVec4Array, indexVec4);
169 // If the second outer structure (b) is chosen in the input_buffer, we need to add an offset of 64 since
170 // each outer_struct contains 64 floats.
171 if (indexOuterStruct == 1)
177 void addVariablePointersComputeGroup (tcu::TestCaseGroup* group)
179 tcu::TestContext& testCtx = group->getTestContext();
180 de::Random rnd (deStringHash(group->getName()));
181 const int seed = testCtx.getCommandLine().getBaseSeed();
182 const int numMuxes = 100;
183 std::string inputArraySize = "200";
184 vector<float> inputAFloats (2*numMuxes, 0);
185 vector<float> inputBFloats (2*numMuxes, 0);
186 vector<float> inputSFloats (numMuxes, 0);
187 vector<float> AmuxAOutputFloats (numMuxes, 0);
188 vector<float> AmuxBOutputFloats (numMuxes, 0);
189 vector<float> incrAmuxAOutputFloats (numMuxes, 0);
190 vector<float> incrAmuxBOutputFloats (numMuxes, 0);
191 VulkanFeatures requiredFeatures;
193 // Each output entry is chosen as follows: ( 0 <= i < numMuxes)
194 // 1) For tests with one input buffer: output[i] = (s[i] < 0) ? A[2*i] : A[2*i+1];
195 // 2) For tests with two input buffers: output[i] = (s[i] < 0) ? A[i] : B[i];
197 fillRandomScalars(rnd, -100.f, 100.f, &inputAFloats[0], 2*numMuxes);
198 fillRandomScalars(rnd, -100.f, 100.f, &inputBFloats[0], 2*numMuxes);
200 // We want to guarantee that the S input has some positive and some negative values.
201 // We choose random negative numbers for the first half, random positive numbers for the second half, and then shuffle.
202 fillRandomScalars(rnd, -100.f, -1.f , &inputSFloats[0], numMuxes / 2);
203 fillRandomScalars(rnd, 1.f , 100.f, &inputSFloats[numMuxes / 2], numMuxes / 2);
204 de::Random(seed).shuffle(inputSFloats.begin(), inputSFloats.end());
206 for (size_t i = 0; i < numMuxes; ++i)
208 AmuxAOutputFloats[i] = (inputSFloats[i] < 0) ? inputAFloats[2*i] : inputAFloats[2*i+1];
209 AmuxBOutputFloats[i] = (inputSFloats[i] < 0) ? inputAFloats[i] : inputBFloats[i];
210 incrAmuxAOutputFloats[i] = (inputSFloats[i] < 0) ? 1 + inputAFloats[2*i] : 1 + inputAFloats[2*i+1];
211 incrAmuxBOutputFloats[i] = (inputSFloats[i] < 0) ? 1 + inputAFloats[i] : 1 + inputBFloats[i];
214 const StringTemplate shaderTemplate (
215 "OpCapability Shader\n"
217 "${ExtraCapability}\n"
219 "OpExtension \"SPV_KHR_variable_pointers\"\n"
220 "OpExtension \"SPV_KHR_storage_buffer_storage_class\"\n"
221 "OpMemoryModel Logical GLSL450\n"
222 "OpEntryPoint GLCompute %main \"main\" %id\n"
223 "OpExecutionMode %main LocalSize 1 1 1\n"
225 "OpSource GLSL 430\n"
226 "OpName %main \"main\"\n"
227 "OpName %id \"gl_GlobalInvocationID\"\n"
230 "OpDecorate %id BuiltIn GlobalInvocationId\n"
231 "OpDecorate %indata_a DescriptorSet 0\n"
232 "OpDecorate %indata_a Binding 0\n"
233 "OpDecorate %indata_b DescriptorSet 0\n"
234 "OpDecorate %indata_b Binding 1\n"
235 "OpDecorate %indata_s DescriptorSet 0\n"
236 "OpDecorate %indata_s Binding 2\n"
237 "OpDecorate %outdata DescriptorSet 0\n"
238 "OpDecorate %outdata Binding 3\n"
239 "OpDecorate %f32arr ArrayStride 4\n"
240 "OpDecorate %sb_f32ptr ArrayStride 4\n"
241 "OpDecorate %buf Block\n"
242 "OpMemberDecorate %buf 0 Offset 0\n"
244 + string(getComputeAsmCommonTypes()) +
246 "%sb_f32ptr = OpTypePointer StorageBuffer %f32\n"
247 "%buf = OpTypeStruct %f32arr\n"
248 "%bufptr = OpTypePointer StorageBuffer %buf\n"
249 "%indata_a = OpVariable %bufptr StorageBuffer\n"
250 "%indata_b = OpVariable %bufptr StorageBuffer\n"
251 "%indata_s = OpVariable %bufptr StorageBuffer\n"
252 "%outdata = OpVariable %bufptr StorageBuffer\n"
253 "%id = OpVariable %uvec3ptr Input\n"
254 "%zero = OpConstant %i32 0\n"
255 "%one = OpConstant %i32 1\n"
256 "%fzero = OpConstant %f32 0\n"
257 "%fone = OpConstant %f32 1\n"
261 "${ExtraGlobalScopeVars}"
263 // We're going to put the "selector" function here.
264 // This function type is needed tests that use OpFunctionCall.
265 "%selector_func_type = OpTypeFunction %sb_f32ptr %bool %sb_f32ptr %sb_f32ptr\n"
266 "%choose_input_func = OpFunction %sb_f32ptr None %selector_func_type\n"
267 "%is_neg_param = OpFunctionParameter %bool\n"
268 "%first_ptr_param = OpFunctionParameter %sb_f32ptr\n"
269 "%second_ptr_param = OpFunctionParameter %sb_f32ptr\n"
270 "%selector_func_begin = OpLabel\n"
271 "%result_ptr = OpSelect %sb_f32ptr %is_neg_param %first_ptr_param %second_ptr_param\n"
272 "OpReturnValue %result_ptr\n"
275 // main function is the entry_point
276 "%main = OpFunction %void None %voidf\n"
279 "${ExtraFunctionScopeVars}"
281 "%idval = OpLoad %uvec3 %id\n"
282 "%i = OpCompositeExtract %u32 %idval 0\n"
283 "%two_i = OpIAdd %u32 %i %i\n"
284 "%two_i_plus_1 = OpIAdd %u32 %two_i %one\n"
285 "%inloc_a_i = OpAccessChain %sb_f32ptr %indata_a %zero %i\n"
286 "%inloc_b_i = OpAccessChain %sb_f32ptr %indata_b %zero %i\n"
287 "%inloc_s_i = OpAccessChain %sb_f32ptr %indata_s %zero %i\n"
288 "%outloc_i = OpAccessChain %sb_f32ptr %outdata %zero %i\n"
289 "%inloc_a_2i = OpAccessChain %sb_f32ptr %indata_a %zero %two_i\n"
290 "%inloc_a_2i_plus_1 = OpAccessChain %sb_f32ptr %indata_a %zero %two_i_plus_1\n"
291 "%inval_s_i = OpLoad %f32 %inloc_s_i\n"
292 "%is_neg = OpFOrdLessThan %bool %inval_s_i %fzero\n"
294 "${ExtraSetupComputations}"
298 "%mux_output = OpLoad %f32 ${VarPtrName}\n"
299 " OpStore %outloc_i %mux_output\n"
303 const bool singleInputBuffer[] = { true, false };
304 for (int inputBufferTypeIndex = 0 ; inputBufferTypeIndex < 2; ++inputBufferTypeIndex)
306 const bool isSingleInputBuffer = singleInputBuffer[inputBufferTypeIndex];
307 const string extraCap = isSingleInputBuffer ? "OpCapability VariablePointersStorageBuffer\n" : "OpCapability VariablePointers\n";
308 const vector<float>& expectedOutput = isSingleInputBuffer ? AmuxAOutputFloats : AmuxBOutputFloats;
309 const vector<float>& expectedIncrOutput = isSingleInputBuffer ? incrAmuxAOutputFloats : incrAmuxBOutputFloats;
310 const string bufferType = isSingleInputBuffer ? "single_buffer" : "two_buffers";
311 const string muxInput1 = isSingleInputBuffer ? " %inloc_a_2i " : " %inloc_a_i ";
312 const string muxInput2 = isSingleInputBuffer ? " %inloc_a_2i_plus_1 " : " %inloc_b_i ";
314 // Set the proper extension features required for the test
315 if (isSingleInputBuffer)
316 requiredFeatures.extVariablePointers = EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS_STORAGEBUFFER;
318 requiredFeatures.extVariablePointers = EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS;
320 { // Variable Pointer Reads (using OpSelect)
321 ComputeShaderSpec spec;
322 map<string, string> specs;
323 string name = "reads_opselect_" + bufferType;
324 specs["ExtraCapability"] = extraCap;
325 specs["ExtraTypes"] = "";
326 specs["ExtraGlobalScopeVars"] = "";
327 specs["ExtraFunctionScopeVars"] = "";
328 specs["ExtraSetupComputations"] = "";
329 specs["VarPtrName"] = "%mux_output_var_ptr";
330 specs["ResultStrategy"] = "%mux_output_var_ptr = OpSelect %sb_f32ptr %is_neg" + muxInput1 + muxInput2 + "\n";
331 spec.assembly = shaderTemplate.specialize(specs);
332 spec.numWorkGroups = IVec3(numMuxes, 1, 1);
333 spec.requestedVulkanFeatures = requiredFeatures;
334 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
335 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
336 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
337 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
338 spec.extensions.push_back("VK_KHR_variable_pointers");
339 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
341 { // Variable Pointer Reads (using OpFunctionCall)
342 ComputeShaderSpec spec;
343 map<string, string> specs;
344 string name = "reads_opfunctioncall_" + bufferType;
345 specs["ExtraCapability"] = extraCap;
346 specs["ExtraTypes"] = "";
347 specs["ExtraGlobalScopeVars"] = "";
348 specs["ExtraFunctionScopeVars"] = "";
349 specs["ExtraSetupComputations"] = "";
350 specs["VarPtrName"] = "%mux_output_var_ptr";
351 specs["ResultStrategy"] = "%mux_output_var_ptr = OpFunctionCall %sb_f32ptr %choose_input_func %is_neg" + muxInput1 + muxInput2 + "\n";
352 spec.assembly = shaderTemplate.specialize(specs);
353 spec.numWorkGroups = IVec3(numMuxes, 1, 1);
354 spec.requestedVulkanFeatures = requiredFeatures;
355 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
356 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
357 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
358 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
359 spec.extensions.push_back("VK_KHR_variable_pointers");
360 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
362 { // Variable Pointer Reads (using OpPhi)
363 ComputeShaderSpec spec;
364 map<string, string> specs;
365 string name = "reads_opphi_" + bufferType;
366 specs["ExtraCapability"] = extraCap;
367 specs["ExtraTypes"] = "";
368 specs["ExtraGlobalScopeVars"] = "";
369 specs["ExtraFunctionScopeVars"] = "";
370 specs["ExtraSetupComputations"] = "";
371 specs["VarPtrName"] = "%mux_output_var_ptr";
372 specs["ResultStrategy"] =
373 " OpSelectionMerge %end_label None\n"
374 " OpBranchConditional %is_neg %take_mux_input_1 %take_mux_input_2\n"
375 "%take_mux_input_1 = OpLabel\n"
376 " OpBranch %end_label\n"
377 "%take_mux_input_2 = OpLabel\n"
378 " OpBranch %end_label\n"
379 "%end_label = OpLabel\n"
380 "%mux_output_var_ptr = OpPhi %sb_f32ptr" + muxInput1 + "%take_mux_input_1" + muxInput2 + "%take_mux_input_2\n";
381 spec.assembly = shaderTemplate.specialize(specs);
382 spec.numWorkGroups = IVec3(numMuxes, 1, 1);
383 spec.requestedVulkanFeatures = requiredFeatures;
384 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
385 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
386 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
387 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
388 spec.extensions.push_back("VK_KHR_variable_pointers");
389 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
391 { // Variable Pointer Reads (using OpCopyObject)
392 ComputeShaderSpec spec;
393 map<string, string> specs;
394 string name = "reads_opcopyobject_" + bufferType;
395 specs["ExtraCapability"] = extraCap;
396 specs["ExtraTypes"] = "";
397 specs["ExtraGlobalScopeVars"] = "";
398 specs["ExtraFunctionScopeVars"] = "";
399 specs["ExtraSetupComputations"] = "";
400 specs["VarPtrName"] = "%mux_output_var_ptr";
401 specs["ResultStrategy"] =
402 "%mux_input_1_copy = OpCopyObject %sb_f32ptr" + muxInput1 + "\n"
403 "%mux_input_2_copy = OpCopyObject %sb_f32ptr" + muxInput2 + "\n"
404 "%mux_output_var_ptr = OpSelect %sb_f32ptr %is_neg %mux_input_1_copy %mux_input_2_copy\n";
405 spec.assembly = shaderTemplate.specialize(specs);
406 spec.numWorkGroups = IVec3(numMuxes, 1, 1);
407 spec.requestedVulkanFeatures = requiredFeatures;
408 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
409 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
410 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
411 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
412 spec.extensions.push_back("VK_KHR_variable_pointers");
413 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
415 { // Test storing into Private variables.
416 const char* storageClasses[] = {"Private", "Function"};
417 for (int classId = 0; classId < 2; ++classId)
419 ComputeShaderSpec spec;
420 map<string, string> specs;
421 std::string storageClass = storageClasses[classId];
422 std::string name = "stores_" + string(de::toLower(storageClass)) + "_" + bufferType;
423 std::string description = "Test storing variable pointer into " + storageClass + " variable.";
424 std::string extraVariable = "%mux_output_copy = OpVariable %sb_f32ptrptr " + storageClass + "\n";
425 specs["ExtraTypes"] = "%sb_f32ptrptr = OpTypePointer " + storageClass + " %sb_f32ptr\n";
426 specs["ExtraCapability"] = extraCap;
427 specs["ExtraGlobalScopeVars"] = (classId == 0) ? extraVariable : "";
428 specs["ExtraFunctionScopeVars"] = (classId == 1) ? extraVariable : "";
429 specs["ExtraSetupComputations"] = "";
430 specs["VarPtrName"] = "%mux_output_var_ptr";
431 specs["ResultStrategy"] =
432 "%opselect_result = OpSelect %sb_f32ptr %is_neg" + muxInput1 + muxInput2 + "\n"
433 " OpStore %mux_output_copy %opselect_result\n"
434 "%mux_output_var_ptr = OpLoad %sb_f32ptr %mux_output_copy\n";
435 spec.assembly = shaderTemplate.specialize(specs);
436 spec.numWorkGroups = IVec3(numMuxes, 1, 1);
437 spec.requestedVulkanFeatures = requiredFeatures;
438 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
439 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
440 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
441 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
442 spec.extensions.push_back("VK_KHR_variable_pointers");
443 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), description.c_str(), spec));
446 { // Variable Pointer Reads (Using OpPtrAccessChain)
447 ComputeShaderSpec spec;
448 map<string, string> specs;
449 std::string name = "reads_opptraccesschain_" + bufferType;
450 std::string in_1 = isSingleInputBuffer ? " %a_2i_ptr " : " %a_i_ptr ";
451 std::string in_2 = isSingleInputBuffer ? " %a_2i_plus_1_ptr " : " %b_i_ptr ";
452 specs["ExtraTypes"] = "";
453 specs["ExtraCapability"] = extraCap;
454 specs["ExtraGlobalScopeVars"] = "";
455 specs["ExtraFunctionScopeVars"] = "";
456 specs["ExtraSetupComputations"] = "";
457 specs["VarPtrName"] = "%mux_output_var_ptr";
458 specs["ResultStrategy"] =
459 "%a_ptr = OpAccessChain %sb_f32ptr %indata_a %zero %zero\n"
460 "%b_ptr = OpAccessChain %sb_f32ptr %indata_b %zero %zero\n"
461 "%s_ptr = OpAccessChain %sb_f32ptr %indata_s %zero %zero\n"
462 "%out_ptr = OpAccessChain %sb_f32ptr %outdata %zero %zero\n"
463 "%a_i_ptr = OpPtrAccessChain %sb_f32ptr %a_ptr %i\n"
464 "%b_i_ptr = OpPtrAccessChain %sb_f32ptr %b_ptr %i\n"
465 "%s_i_ptr = OpPtrAccessChain %sb_f32ptr %s_ptr %i\n"
466 "%a_2i_ptr = OpPtrAccessChain %sb_f32ptr %a_ptr %two_i\n"
467 "%a_2i_plus_1_ptr = OpPtrAccessChain %sb_f32ptr %a_ptr %two_i_plus_1\n"
468 "%mux_output_var_ptr = OpSelect %sb_f32ptr %is_neg " + in_1 + in_2 + "\n";
469 spec.assembly = shaderTemplate.specialize(specs);
470 spec.numWorkGroups = IVec3(numMuxes, 1, 1);
471 spec.requestedVulkanFeatures = requiredFeatures;
472 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
473 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
474 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
475 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
476 spec.extensions.push_back("VK_KHR_variable_pointers");
477 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
479 { // Variable Pointer Writes
480 ComputeShaderSpec spec;
481 map<string, string> specs;
482 std::string name = "writes_" + bufferType;
483 specs["ExtraCapability"] = extraCap;
484 specs["ExtraTypes"] = "";
485 specs["ExtraGlobalScopeVars"] = "";
486 specs["ExtraFunctionScopeVars"] = "";
487 specs["ExtraSetupComputations"] = "";
488 specs["VarPtrName"] = "%mux_output_var_ptr";
489 specs["ResultStrategy"] = "%mux_output_var_ptr = OpSelect %sb_f32ptr %is_neg" + muxInput1 + muxInput2 + "\n" +
490 " %val = OpLoad %f32 %mux_output_var_ptr\n"
491 " %val_plus_1 = OpFAdd %f32 %val %fone\n"
492 " OpStore %mux_output_var_ptr %val_plus_1\n";
493 spec.assembly = shaderTemplate.specialize(specs);
494 spec.numWorkGroups = IVec3(numMuxes, 1, 1);
495 spec.requestedVulkanFeatures = requiredFeatures;
496 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
497 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
498 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
499 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedIncrOutput))));
500 spec.extensions.push_back("VK_KHR_variable_pointers");
501 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
504 // If we only have VariablePointersStorageBuffer, then the extension does not apply to Workgroup storage class.
505 // Therefore the Workgroup tests apply to cases where the VariablePointers capability is used (when 2 input buffers are used).
506 if (!isSingleInputBuffer)
508 // VariablePointers on Workgroup
509 ComputeShaderSpec spec;
510 map<string, string> specs;
511 std::string name = "workgroup_" + bufferType;
512 specs["ExtraCapability"] = extraCap;
513 specs["ExtraTypes"] =
514 "%c_i32_N = OpConstant %i32 " + inputArraySize + " \n"
515 "%f32arr_N = OpTypeArray %f32 %c_i32_N\n"
516 "%f32arr_wrkgrp_ptr = OpTypePointer Workgroup %f32arr_N\n"
517 "%f32_wrkgrp_ptr = OpTypePointer Workgroup %f32\n";
518 specs["ExtraGlobalScopeVars"] =
519 "%AW = OpVariable %f32arr_wrkgrp_ptr Workgroup\n"
520 "%BW = OpVariable %f32arr_wrkgrp_ptr Workgroup\n";
521 specs["ExtraFunctionScopeVars"] = "";
522 specs["ExtraSetupComputations"] =
523 "%loc_AW_i = OpAccessChain %f32_wrkgrp_ptr %AW %i\n"
524 "%loc_BW_i = OpAccessChain %f32_wrkgrp_ptr %BW %i\n"
525 "%inval_a_i = OpLoad %f32 %inloc_a_i\n"
526 "%inval_b_i = OpLoad %f32 %inloc_b_i\n"
527 "%inval_a_2i = OpLoad %f32 %inloc_a_2i\n"
528 "%inval_a_2i_plus_1 = OpLoad %f32 %inloc_a_2i_plus_1\n";
529 specs["VarPtrName"] = "%output_var_ptr";
530 specs["ResultStrategy"] =
531 " OpStore %loc_AW_i %inval_a_i\n"
532 " OpStore %loc_BW_i %inval_b_i\n"
533 "%output_var_ptr = OpSelect %f32_wrkgrp_ptr %is_neg %loc_AW_i %loc_BW_i\n";
534 spec.assembly = shaderTemplate.specialize(specs);
535 spec.numWorkGroups = IVec3(numMuxes, 1, 1);
536 spec.requestedVulkanFeatures = requiredFeatures;
537 spec.inputs.push_back (Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
538 spec.inputs.push_back (Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
539 spec.inputs.push_back (Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
540 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
541 spec.extensions.push_back("VK_KHR_variable_pointers");
542 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
547 void addComplexTypesVariablePointersComputeGroup (tcu::TestCaseGroup* group)
549 tcu::TestContext& testCtx = group->getTestContext();
550 const int numFloats = 64;
551 vector<float> inputA (numFloats, 0);
552 vector<float> inputB (numFloats, 0);
553 vector<float> inputC (2*numFloats, 0);
554 vector<float> expectedOutput (1, 0);
555 VulkanFeatures requiredFeatures;
557 // These tests exercise variable pointers into various levels of the following data-structures.
559 // struct struct inner_struct {
560 // vec4 x[2]; // array of 2 vectors. Each vector is 4 floats.
561 // vec4 y[2]; // array of 2 vectors. Each vector is 4 floats.
564 // struct outer_struct {
565 // inner_struct r[2][2];
568 // struct input_buffer {
573 // inputA is of type outer_struct.
574 // inputB is of type outer_struct.
575 // inputC is of type input_buffer.
577 // inputA and inputB are of the same size. When testing variable pointers pointing to
578 // two different input buffers, we use inputA and inputB.
580 // inputC is twice the size of inputA. When testing the VariablePointersStorageBuffer capability,
581 // the variable pointer must be confined to a single buffer. These tests will use inputC.
583 // The inner_struct contains 16 floats.
584 // The outer_struct contains 64 floats.
585 // The input_buffer contains 128 floats.
586 // Populate the first input (inputA) to contain: {0, 4, ... , 252}
587 // Populate the second input (inputB) to contain: {3, 7, ... , 255}
588 // Populate the third input (inputC) to contain: {0, 4, ... , 252, 3, 7, ... , 255}
589 // Note that the first half of inputC is the same as inputA and the second half is the same as inputB.
590 for (size_t i = 0; i < numFloats; ++i)
592 inputA[i] = 4*float(i) / 255;
593 inputB[i] = ((4*float(i)) + 3) / 255;
594 inputC[i] = inputA[i];
595 inputC[i + numFloats] = inputB[i];
598 // In the following tests we use variable pointers to point to different types:
599 // nested structures, matrices of structures, arrays of structures, arrays of vectors, vectors of scalars, and scalars.
600 // outer_structure.inner_structure[?][?].x[?][?];
603 // For tests with 2 input buffers:
604 // 1. inputA or inputB = nested structure
605 // 2. inputA.r or inputB.r = matrices of structures
606 // 3. inputA.r[?] or inputB.r[?] = arrays of structures
607 // 4. inputA.r[?][?] or inputB.r[?][?] = structures
608 // 5. inputA.r[?][?].(x|y) or inputB.r[?][?].(x|y) = arrays of vectors
609 // 6. inputA.r[?][?].(x|y)[?] or inputB.r[?][?].(x|y)[?] = vectors of scalars
610 // 7. inputA.r[?][?].(x|y)[?][?] or inputB.r[?][?].(x|y)[?][?] = scalars
611 // For tests with 1 input buffer:
612 // 1. inputC.a or inputC.b = nested structure
613 // 2. inputC.a.r or inputC.b.r = matrices of structures
614 // 3. inputC.a.r[?] or inputC.b.r[?] = arrays of structures
615 // 4. inputC.a.r[?][?] or inputC.b.r[?][?] = structures
616 // 5. inputC.a.r[?][?].(x|y) or inputC.b.r[?][?].(x|y) = arrays of vectors
617 // 6. inputC.a.r[?][?].(x|y)[?] or inputC.b.r[?][?].(x|y)[?] = vectors of scalars
618 // 7. inputC.a.r[?][?].(x|y)[?][?] or inputC.b.r[?][?].(x|y)[?][?] = scalars
619 const int numLevels = 7;
621 const string decorations (
623 "OpDecorate %id BuiltIn GlobalInvocationId \n"
624 "OpDecorate %inputA DescriptorSet 0 \n"
625 "OpDecorate %inputB DescriptorSet 0 \n"
626 "OpDecorate %inputC DescriptorSet 0 \n"
627 "OpDecorate %outdata DescriptorSet 0 \n"
628 "OpDecorate %inputA Binding 0 \n"
629 "OpDecorate %inputB Binding 1 \n"
630 "OpDecorate %inputC Binding 2 \n"
631 "OpDecorate %outdata Binding 3 \n"
633 // Set the Block decoration
634 "OpDecorate %outer_struct Block \n"
635 "OpDecorate %input_buffer Block \n"
636 "OpDecorate %output_buffer Block \n"
639 "OpMemberDecorate %output_buffer 0 Offset 0 \n"
640 "OpMemberDecorate %input_buffer 0 Offset 0 \n"
641 "OpMemberDecorate %input_buffer 1 Offset 256 \n"
642 "OpMemberDecorate %outer_struct 0 Offset 0 \n"
643 "OpMemberDecorate %inner_struct 0 Offset 0 \n"
644 "OpMemberDecorate %inner_struct 1 Offset 32 \n"
646 // Set the ArrayStrides
647 "OpDecorate %arr2_v4float ArrayStride 16 \n"
648 "OpDecorate %arr2_inner_struct ArrayStride 64 \n"
649 "OpDecorate %mat2x2_inner_struct ArrayStride 128 \n"
650 "OpDecorate %outer_struct_ptr ArrayStride 256 \n"
651 "OpDecorate %v4f32_ptr ArrayStride 16 \n"
658 "%c_bool_true = OpConstantTrue %bool \n"
659 "%c_bool_false = OpConstantFalse %bool \n"
660 "%c_i32_0 = OpConstant %i32 0 \n"
661 "%c_i32_1 = OpConstant %i32 1 \n"
662 "%c_i32_2 = OpConstant %i32 2 \n"
663 "%c_i32_3 = OpConstant %i32 3 \n"
664 "%c_u32_2 = OpConstant %u32 2 \n"
669 "%v4f32 = OpTypeVector %f32 4 \n"
671 // struct struct inner_struct {
672 // vec4 x[2]; // array of 2 vectors
673 // vec4 y[2]; // array of 2 vectors
675 "%arr2_v4float = OpTypeArray %v4f32 %c_u32_2 \n"
676 "%inner_struct = OpTypeStruct %arr2_v4float %arr2_v4float \n"
678 // struct outer_struct {
679 // inner_struct r[2][2];
681 "%arr2_inner_struct = OpTypeArray %inner_struct %c_u32_2 \n"
682 "%mat2x2_inner_struct = OpTypeArray %arr2_inner_struct %c_u32_2 \n"
683 "%outer_struct = OpTypeStruct %mat2x2_inner_struct \n"
685 // struct input_buffer {
689 "%input_buffer = OpTypeStruct %outer_struct %outer_struct \n"
691 // struct output_struct {
694 "%output_buffer = OpTypeStruct %f32 \n"
699 "%output_buffer_ptr = OpTypePointer StorageBuffer %output_buffer \n"
700 "%input_buffer_ptr = OpTypePointer StorageBuffer %input_buffer \n"
701 "%outer_struct_ptr = OpTypePointer StorageBuffer %outer_struct \n"
702 "%mat2x2_ptr = OpTypePointer StorageBuffer %mat2x2_inner_struct \n"
703 "%arr2_ptr = OpTypePointer StorageBuffer %arr2_inner_struct \n"
704 "%inner_struct_ptr = OpTypePointer StorageBuffer %inner_struct \n"
705 "%arr_v4f32_ptr = OpTypePointer StorageBuffer %arr2_v4float \n"
706 "%v4f32_ptr = OpTypePointer StorageBuffer %v4f32 \n"
707 "%sb_f32ptr = OpTypePointer StorageBuffer %f32 \n"
712 "%id = OpVariable %uvec3ptr Input \n"
713 "%inputA = OpVariable %outer_struct_ptr StorageBuffer \n"
714 "%inputB = OpVariable %outer_struct_ptr StorageBuffer \n"
715 "%inputC = OpVariable %input_buffer_ptr StorageBuffer \n"
716 "%outdata = OpVariable %output_buffer_ptr StorageBuffer \n"
719 const StringTemplate shaderTemplate (
720 "OpCapability Shader\n"
722 "${extra_capability}\n"
724 "OpExtension \"SPV_KHR_variable_pointers\"\n"
725 "OpExtension \"SPV_KHR_storage_buffer_storage_class\"\n"
726 "OpMemoryModel Logical GLSL450\n"
727 "OpEntryPoint GLCompute %main \"main\" %id\n"
728 "OpExecutionMode %main LocalSize 1 1 1\n"
730 "OpSource GLSL 430\n"
731 "OpName %main \"main\"\n"
732 "OpName %id \"gl_GlobalInvocationID\"\n"
736 + string(getComputeAsmCommonTypes())
740 // These selector functions return variable pointers.
741 // These functions are used by tests that use OpFunctionCall to obtain the variable pointer
742 "%selector_func_type = OpTypeFunction ${selected_type} %bool ${selected_type} ${selected_type}\n"
743 "%choose_input_func = OpFunction ${selected_type} None %selector_func_type\n"
744 "%choose_first_param = OpFunctionParameter %bool\n"
745 "%first_param = OpFunctionParameter ${selected_type}\n"
746 "%second_param = OpFunctionParameter ${selected_type}\n"
747 "%selector_func_begin = OpLabel\n"
748 "%result_ptr = OpSelect ${selected_type} %choose_first_param %first_param %second_param\n"
749 "OpReturnValue %result_ptr\n"
752 // main function is the entry_point
753 "%main = OpFunction %void None %voidf\n"
756 // Here are the 2 nested structures within InputC.
757 "%inputC_a = OpAccessChain %outer_struct_ptr %inputC %c_i32_0\n"
758 "%inputC_b = OpAccessChain %outer_struct_ptr %inputC %c_i32_1\n"
760 // Define the 2 pointers from which we're going to choose one.
764 // Choose between the 2 pointers / variable pointers.
765 "${selection_strategy} \n"
767 // OpAccessChain into the variable pointer until you get to the float.
768 "%result_loc = OpAccessChain %sb_f32ptr %var_ptr ${remaining_indexes} \n"
770 // Now load from the result_loc
771 "%result_val = OpLoad %f32 %result_loc \n"
773 // Store the chosen value to the output buffer.
774 "%outdata_loc = OpAccessChain %sb_f32ptr %outdata %c_i32_0\n"
775 " OpStore %outdata_loc %result_val\n"
779 for (int isSingleInputBuffer = 0 ; isSingleInputBuffer < 2; ++isSingleInputBuffer)
781 // Set the proper extension features required for the test
782 if (isSingleInputBuffer)
783 requiredFeatures.extVariablePointers = EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS_STORAGEBUFFER;
785 requiredFeatures.extVariablePointers = EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS;
787 for (int selectInputA = 0; selectInputA < 2; ++selectInputA)
789 const string extraCap = isSingleInputBuffer ? "OpCapability VariablePointersStorageBuffer\n" : "OpCapability VariablePointers\n";
790 const vector<float>& selectedInput = isSingleInputBuffer ? inputC : (selectInputA ? inputA : inputB);
791 const string bufferType = isSingleInputBuffer ? "single_buffer_" : "two_buffers_";
792 const string baseA = isSingleInputBuffer ? "%inputC_a" : "%inputA";
793 const string baseB = isSingleInputBuffer ? "%inputC_b" : "%inputB";
794 const string selectedInputStr = selectInputA ? "first_input" : "second_input";
795 const string spirvSelectInputA = selectInputA ? "%c_bool_true" : "%c_bool_false";
796 const int outerStructIndex = isSingleInputBuffer ? (selectInputA ? 0 : 1) : 0;
798 // The indexes chosen at each level. At any level, any given offset is exercised.
799 // outerStructIndex is 0 for inputA and inputB (because outer_struct has only 1 member).
800 // outerStructIndex is 0 for member <a> of inputC and 1 for member <b>.
801 const int indexesForLevel[numLevels][6]= {{outerStructIndex, 0, 0, 0, 0, 1},
802 {outerStructIndex, 1, 0, 1, 0, 2},
803 {outerStructIndex, 0, 1, 0, 1, 3},
804 {outerStructIndex, 1, 1, 1, 0, 0},
805 {outerStructIndex, 0, 0, 1, 1, 1},
806 {outerStructIndex, 1, 0, 0, 0, 2},
807 {outerStructIndex, 1, 1, 1, 1, 3}};
809 const string indexLevelNames[] = {"_outer_struct_", "_matrices_", "_arrays_", "_inner_structs_", "_vec4arr_", "_vec4_", "_float_"};
810 const string inputALocations[] = { "",
811 "%a_loc = OpAccessChain %mat2x2_ptr " + baseA + " %c_i32_0",
812 "%a_loc = OpAccessChain %arr2_ptr " + baseA + " %c_i32_0 %c_i32_0",
813 "%a_loc = OpAccessChain %inner_struct_ptr " + baseA + " %c_i32_0 %c_i32_1 %c_i32_1",
814 "%a_loc = OpAccessChain %arr_v4f32_ptr " + baseA + " %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
815 "%a_loc = OpAccessChain %v4f32_ptr " + baseA + " %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
816 "%a_loc = OpAccessChain %sb_f32ptr " + baseA + " %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3"};
818 const string inputBLocations[] = { "",
819 "%b_loc = OpAccessChain %mat2x2_ptr " + baseB + " %c_i32_0",
820 "%b_loc = OpAccessChain %arr2_ptr " + baseB + " %c_i32_0 %c_i32_0",
821 "%b_loc = OpAccessChain %inner_struct_ptr " + baseB + " %c_i32_0 %c_i32_1 %c_i32_1",
822 "%b_loc = OpAccessChain %arr_v4f32_ptr " + baseB + " %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
823 "%b_loc = OpAccessChain %v4f32_ptr " + baseB + " %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
824 "%b_loc = OpAccessChain %sb_f32ptr " + baseB + " %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_3"};
826 const string inputAPtrAccessChain[] = { "",
827 "%a_loc = OpPtrAccessChain %mat2x2_ptr " + baseA + " %c_i32_0 %c_i32_0",
828 "%a_loc = OpPtrAccessChain %arr2_ptr " + baseA + " %c_i32_0 %c_i32_0 %c_i32_0",
829 "%a_loc = OpPtrAccessChain %inner_struct_ptr " + baseA + " %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1",
830 "%a_loc = OpPtrAccessChain %arr_v4f32_ptr " + baseA + " %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
831 "%a_loc = OpPtrAccessChain %v4f32_ptr " + baseA + " %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
832 // Next case emulates:
833 // %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
834 // But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
835 // %a_loc_arr is a pointer to an array that we want to index with 1.
836 // But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
837 // get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
838 "%a_loc_arr = OpPtrAccessChain %arr_v4f32_ptr " + baseA + " %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
839 "%a_loc_first_elem = OpAccessChain %v4f32_ptr %a_loc_arr %c_i32_0 "
840 "%a_loc = OpPtrAccessChain %sb_f32ptr %a_loc_first_elem %c_i32_1 %c_i32_3"};
842 const string inputBPtrAccessChain[] = { "",
843 "%b_loc = OpPtrAccessChain %mat2x2_ptr " + baseB + " %c_i32_0 %c_i32_0",
844 "%b_loc = OpPtrAccessChain %arr2_ptr " + baseB + " %c_i32_0 %c_i32_0 %c_i32_0",
845 "%b_loc = OpPtrAccessChain %inner_struct_ptr " + baseB + " %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1",
846 "%b_loc = OpPtrAccessChain %arr_v4f32_ptr " + baseB + " %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
847 "%b_loc = OpPtrAccessChain %v4f32_ptr " + baseB + " %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
848 // Next case emulates:
849 // %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
850 // But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
851 // %b_loc_arr is a pointer to an array that we want to index with 1.
852 // But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
853 // get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
854 "%b_loc_arr = OpPtrAccessChain %arr_v4f32_ptr " + baseB + " %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
855 "%b_loc_first_elem = OpAccessChain %v4f32_ptr %b_loc_arr %c_i32_0 "
856 "%b_loc = OpPtrAccessChain %sb_f32ptr %b_loc_first_elem %c_i32_1 %c_i32_3"};
859 const string remainingIndexesAtLevel[]= {"%c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
860 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_2",
861 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_3",
862 "%c_i32_1 %c_i32_0 %c_i32_0",
867 const string pointerTypeAtLevel[] = {"%outer_struct_ptr", "%mat2x2_ptr", "%arr2_ptr", "%inner_struct_ptr", "%arr_v4f32_ptr", "%v4f32_ptr", "%sb_f32ptr"};
868 const string baseANameAtLevel[] = {baseA, "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc"};
869 const string baseBNameAtLevel[] = {baseB, "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc"};
871 for (int indexLevel = 0; indexLevel < numLevels; ++indexLevel)
873 const int baseOffset = getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
874 indexesForLevel[indexLevel][1],
875 indexesForLevel[indexLevel][2],
876 indexesForLevel[indexLevel][3],
877 indexesForLevel[indexLevel][4],
878 indexesForLevel[indexLevel][5]);
879 // Use OpSelect to choose between 2 pointers
881 ComputeShaderSpec spec;
882 map<string, string> specs;
883 string opCodeForTests = "opselect";
884 string name = opCodeForTests + indexLevelNames[indexLevel] + bufferType + selectedInputStr;
885 specs["extra_capability"] = extraCap;
886 specs["selected_type"] = pointerTypeAtLevel[indexLevel];
887 specs["select_inputA"] = spirvSelectInputA;
888 specs["a_loc"] = inputALocations[indexLevel];
889 specs["b_loc"] = inputBLocations[indexLevel];
890 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
891 specs["selection_strategy"] = "%var_ptr = OpSelect "
892 + pointerTypeAtLevel[indexLevel] + " "
893 + spirvSelectInputA + " "
894 + baseANameAtLevel[indexLevel] + " "
895 + baseBNameAtLevel[indexLevel] + "\n";
896 expectedOutput[0] = selectedInput[baseOffset];
897 spec.assembly = shaderTemplate.specialize(specs);
898 spec.numWorkGroups = IVec3(1, 1, 1);
899 spec.requestedVulkanFeatures = requiredFeatures;
900 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
901 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
902 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputC)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
903 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
904 spec.extensions.push_back("VK_KHR_variable_pointers");
905 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
908 // Use OpFunctionCall to choose between 2 pointers
910 ComputeShaderSpec spec;
911 map<string, string> specs;
912 string opCodeForTests = "opfunctioncall";
913 string name = opCodeForTests + indexLevelNames[indexLevel] + bufferType + selectedInputStr;
914 specs["extra_capability"] = extraCap;
915 specs["selected_type"] = pointerTypeAtLevel[indexLevel];
916 specs["select_inputA"] = spirvSelectInputA;
917 specs["a_loc"] = inputALocations[indexLevel];
918 specs["b_loc"] = inputBLocations[indexLevel];
919 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
920 specs["selection_strategy"] = "%var_ptr = OpFunctionCall "
921 + pointerTypeAtLevel[indexLevel]
922 + " %choose_input_func "
923 + spirvSelectInputA + " "
924 + baseANameAtLevel[indexLevel] + " "
925 + baseBNameAtLevel[indexLevel] + "\n";
926 expectedOutput[0] = selectedInput[baseOffset];
927 spec.assembly = shaderTemplate.specialize(specs);
928 spec.numWorkGroups = IVec3(1, 1, 1);
929 spec.requestedVulkanFeatures = requiredFeatures;
930 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
931 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
932 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputC)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
933 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
934 spec.extensions.push_back("VK_KHR_variable_pointers");
935 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
938 // Use OpPhi to choose between 2 pointers
941 ComputeShaderSpec spec;
942 map<string, string> specs;
943 string opCodeForTests = "opphi";
944 string name = opCodeForTests + indexLevelNames[indexLevel] + bufferType + selectedInputStr;
945 specs["extra_capability"] = extraCap;
946 specs["selected_type"] = pointerTypeAtLevel[indexLevel];
947 specs["select_inputA"] = spirvSelectInputA;
948 specs["a_loc"] = inputALocations[indexLevel];
949 specs["b_loc"] = inputBLocations[indexLevel];
950 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
951 specs["selection_strategy"] =
952 " OpSelectionMerge %end_label None\n"
953 " OpBranchConditional " + spirvSelectInputA + " %take_input_a %take_input_b\n"
954 "%take_input_a = OpLabel\n"
955 " OpBranch %end_label\n"
956 "%take_input_b = OpLabel\n"
957 " OpBranch %end_label\n"
958 "%end_label = OpLabel\n"
960 + pointerTypeAtLevel[indexLevel] + " "
961 + baseANameAtLevel[indexLevel]
963 + baseBNameAtLevel[indexLevel]
964 + " %take_input_b\n";
965 expectedOutput[0] = selectedInput[baseOffset];
966 spec.assembly = shaderTemplate.specialize(specs);
967 spec.numWorkGroups = IVec3(1, 1, 1);
968 spec.requestedVulkanFeatures = requiredFeatures;
969 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
970 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
971 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputC)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
972 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
973 spec.extensions.push_back("VK_KHR_variable_pointers");
974 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
977 // Use OpCopyObject to get variable pointers
979 ComputeShaderSpec spec;
980 map<string, string> specs;
981 string opCodeForTests = "opcopyobject";
982 string name = opCodeForTests + indexLevelNames[indexLevel] + bufferType + selectedInputStr;
983 specs["extra_capability"] = extraCap;
984 specs["selected_type"] = pointerTypeAtLevel[indexLevel];
985 specs["select_inputA"] = spirvSelectInputA;
986 specs["a_loc"] = inputALocations[indexLevel];
987 specs["b_loc"] = inputBLocations[indexLevel];
988 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
989 specs["selection_strategy"] =
990 "%in_a_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseANameAtLevel[indexLevel] + "\n"
991 "%in_b_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseBNameAtLevel[indexLevel] + "\n"
992 "%var_ptr = OpSelect " + pointerTypeAtLevel[indexLevel] + " " + spirvSelectInputA + " %in_a_copy %in_b_copy\n";
993 expectedOutput[0] = selectedInput[baseOffset];
994 spec.assembly = shaderTemplate.specialize(specs);
995 spec.numWorkGroups = IVec3(1, 1, 1);
996 spec.requestedVulkanFeatures = requiredFeatures;
997 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
998 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
999 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputC)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1000 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
1001 spec.extensions.push_back("VK_KHR_variable_pointers");
1002 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
1005 // Use OpPtrAccessChain to get variable pointers
1007 ComputeShaderSpec spec;
1008 map<string, string> specs;
1009 string opCodeForTests = "opptraccesschain";
1010 string name = opCodeForTests + indexLevelNames[indexLevel] + bufferType + selectedInputStr;
1011 specs["extra_capability"] = extraCap;
1012 specs["selected_type"] = pointerTypeAtLevel[indexLevel];
1013 specs["select_inputA"] = spirvSelectInputA;
1014 specs["a_loc"] = inputAPtrAccessChain[indexLevel];
1015 specs["b_loc"] = inputBPtrAccessChain[indexLevel];
1016 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
1017 specs["selection_strategy"] = "%var_ptr = OpSelect "
1018 + pointerTypeAtLevel[indexLevel] + " "
1019 + spirvSelectInputA + " "
1020 + baseANameAtLevel[indexLevel] + " "
1021 + baseBNameAtLevel[indexLevel] + "\n";
1022 expectedOutput[0] = selectedInput[baseOffset];
1023 spec.assembly = shaderTemplate.specialize(specs);
1024 spec.numWorkGroups = IVec3(1, 1, 1);
1025 spec.requestedVulkanFeatures = requiredFeatures;
1026 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1027 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1028 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputC)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1029 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
1030 spec.extensions.push_back("VK_KHR_variable_pointers");
1031 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
1038 void addNullptrVariablePointersComputeGroup (tcu::TestCaseGroup* group)
1040 tcu::TestContext& testCtx = group->getTestContext();
1041 float someFloat = 78;
1042 vector<float> input (1, someFloat);
1043 vector<float> expectedOutput (1, someFloat);
1044 VulkanFeatures requiredFeatures;
1046 // Requires the variable pointers feature.
1047 requiredFeatures.extVariablePointers = EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS;
1049 const string decorations (
1051 "OpDecorate %id BuiltIn GlobalInvocationId \n"
1052 "OpDecorate %input DescriptorSet 0 \n"
1053 "OpDecorate %outdata DescriptorSet 0 \n"
1054 "OpDecorate %input Binding 0 \n"
1055 "OpDecorate %outdata Binding 1 \n"
1057 // Set the Block decoration
1058 "OpDecorate %float_struct Block \n"
1061 "OpMemberDecorate %float_struct 0 Offset 0 \n"
1064 const string types (
1068 // struct float_struct {
1071 "%float_struct = OpTypeStruct %f32 \n"
1076 "%float_struct_ptr = OpTypePointer StorageBuffer %float_struct \n"
1077 "%sb_f32ptr = OpTypePointer StorageBuffer %f32 \n"
1078 "%func_f32ptrptr = OpTypePointer Function %sb_f32ptr \n"
1083 "%c_bool_true = OpConstantTrue %bool \n"
1084 "%c_i32_0 = OpConstant %i32 0 \n"
1085 "%c_null_ptr = OpConstantNull %sb_f32ptr \n"
1090 "%id = OpVariable %uvec3ptr Input \n"
1091 "%input = OpVariable %float_struct_ptr StorageBuffer \n"
1092 "%outdata = OpVariable %float_struct_ptr StorageBuffer \n"
1095 const StringTemplate shaderTemplate (
1096 "OpCapability Shader\n"
1097 "OpCapability VariablePointers\n"
1099 "OpExtension \"SPV_KHR_variable_pointers\"\n"
1100 "OpExtension \"SPV_KHR_storage_buffer_storage_class\"\n"
1101 "OpMemoryModel Logical GLSL450\n"
1102 "OpEntryPoint GLCompute %main \"main\" %id\n"
1103 "OpExecutionMode %main LocalSize 1 1 1\n"
1105 "OpSource GLSL 430\n"
1106 "OpName %main \"main\"\n"
1107 "OpName %id \"gl_GlobalInvocationID\"\n"
1111 + string(getComputeAsmCommonTypes())
1115 // main function is the entry_point
1116 "%main = OpFunction %void None %voidf\n"
1117 "%label = OpLabel\n"
1119 // Note that the Variable Pointers extension allows creation
1120 // of a pointer variable with storage class of Private or Function.
1121 "%f32_ptr_var = OpVariable %func_f32ptrptr Function %c_null_ptr\n"
1123 "%input_loc = OpAccessChain %sb_f32ptr %input %c_i32_0\n"
1124 "%output_loc = OpAccessChain %sb_f32ptr %outdata %c_i32_0\n"
1126 "${NullptrTestingStrategy}\n"
1129 " OpFunctionEnd\n");
1131 // f32_ptr_var has been inintialized to NULL.
1132 // Now set it to point to the float variable that holds the input value
1134 ComputeShaderSpec spec;
1135 map<string, string> specs;
1136 string name = "opvariable_initialized_null";
1137 specs["NullptrTestingStrategy"] =
1138 " OpStore %f32_ptr_var %input_loc \n"
1139 "%loaded_f32_ptr = OpLoad %sb_f32ptr %f32_ptr_var \n"
1140 "%loaded_f32 = OpLoad %f32 %loaded_f32_ptr \n"
1141 " OpStore %output_loc %loaded_f32 \n";
1143 spec.assembly = shaderTemplate.specialize(specs);
1144 spec.numWorkGroups = IVec3(1, 1, 1);
1145 spec.requestedVulkanFeatures = requiredFeatures;
1146 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(input)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1147 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
1148 spec.extensions.push_back("VK_KHR_variable_pointers");
1149 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
1151 // Use OpSelect to choose between nullptr and valid pointer. Since we can't dereference nullptr,
1152 // it is forced to always choose the valid pointer.
1154 ComputeShaderSpec spec;
1155 map<string, string> specs;
1156 string name = "opselect_null_or_valid_ptr";
1157 specs["NullptrTestingStrategy"] =
1158 "%selected_ptr = OpSelect %sb_f32ptr %c_bool_true %input_loc %c_null_ptr\n"
1159 "%loaded_var = OpLoad %f32 %selected_ptr\n"
1160 "OpStore %output_loc %loaded_var\n";
1162 spec.assembly = shaderTemplate.specialize(specs);
1163 spec.numWorkGroups = IVec3(1, 1, 1);
1164 spec.requestedVulkanFeatures = requiredFeatures;
1165 spec.inputs.push_back(Resource(BufferSp(new Float32Buffer(input)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1166 spec.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput))));
1167 spec.extensions.push_back("VK_KHR_variable_pointers");
1168 group->addChild(new SpvAsmComputeShaderCase(testCtx, name.c_str(), name.c_str(), spec));
1172 void addVariablePointersGraphicsGroup (tcu::TestCaseGroup* testGroup)
1174 tcu::TestContext& testCtx = testGroup->getTestContext();
1175 de::Random rnd (deStringHash(testGroup->getName()));
1176 map<string, string> fragments;
1177 RGBA defaultColors[4];
1178 vector<string> extensions;
1179 const int seed = testCtx.getCommandLine().getBaseSeed();
1180 const int numMuxes = 100;
1181 const std::string numMuxesStr = "100";
1182 vector<float> inputAFloats (2*numMuxes, 0);
1183 vector<float> inputBFloats (2*numMuxes, 0);
1184 vector<float> inputSFloats (numMuxes, 0);
1185 vector<float> AmuxAOutputFloats (numMuxes, 0);
1186 vector<float> AmuxBOutputFloats (numMuxes, 0);
1187 vector<float> incrAmuxAOutputFloats (numMuxes, 0);
1188 vector<float> incrAmuxBOutputFloats (numMuxes, 0);
1189 VulkanFeatures requiredFeatures;
1191 extensions.push_back("VK_KHR_variable_pointers");
1192 getDefaultColors(defaultColors);
1194 // Each output entry is chosen as follows: ( 0 <= i < numMuxes)
1195 // 1) For tests with one input buffer: output[i] = (s[i] < 0) ? A[2*i] : A[2*i+1];
1196 // 2) For tests with two input buffers: output[i] = (s[i] < 0) ? A[i] : B[i];
1198 fillRandomScalars(rnd, -100.f, 100.f, &inputAFloats[0], 2*numMuxes);
1199 fillRandomScalars(rnd, -100.f, 100.f, &inputBFloats[0], 2*numMuxes);
1201 // We want to guarantee that the S input has some positive and some negative values.
1202 // We choose random negative numbers for the first half, random positive numbers for the second half, and then shuffle.
1203 fillRandomScalars(rnd, -100.f, -1.f , &inputSFloats[0], numMuxes / 2);
1204 fillRandomScalars(rnd, 1.f , 100.f, &inputSFloats[numMuxes / 2], numMuxes / 2);
1205 de::Random(seed).shuffle(inputSFloats.begin(), inputSFloats.end());
1207 for (size_t i = 0; i < numMuxes; ++i)
1209 AmuxAOutputFloats[i] = (inputSFloats[i] < 0) ? inputAFloats[2*i] : inputAFloats[2*i+1];
1210 AmuxBOutputFloats[i] = (inputSFloats[i] < 0) ? inputAFloats[i] : inputBFloats[i];
1211 incrAmuxAOutputFloats[i] = (inputSFloats[i] < 0) ? 1 + inputAFloats[2*i] : 1 + inputAFloats[2*i+1];
1212 incrAmuxBOutputFloats[i] = (inputSFloats[i] < 0) ? 1 + inputAFloats[i] : 1 + inputBFloats[i];
1215 fragments["extension"] = "OpExtension \"SPV_KHR_variable_pointers\"\n"
1216 "OpExtension \"SPV_KHR_storage_buffer_storage_class\"\n";
1218 const StringTemplate preMain (
1219 "%c_i32_limit = OpConstant %i32 " + numMuxesStr + "\n"
1220 " %sb_f32 = OpTypePointer StorageBuffer %f32\n"
1221 " %ra_f32 = OpTypeRuntimeArray %f32\n"
1222 " %buf = OpTypeStruct %ra_f32\n"
1223 " %sb_buf = OpTypePointer StorageBuffer %buf\n"
1227 " ${ExtraGlobalScopeVars}"
1229 " %indata_a = OpVariable %sb_buf StorageBuffer\n"
1230 " %indata_b = OpVariable %sb_buf StorageBuffer\n"
1231 " %indata_s = OpVariable %sb_buf StorageBuffer\n"
1232 " %outdata = OpVariable %sb_buf StorageBuffer\n"
1234 " ${ExtraFunctions} ");
1236 const std::string selectorFunction (
1237 // We're going to put the "selector" function here.
1238 // This function type is needed for tests that use OpFunctionCall.
1239 "%selector_func_type = OpTypeFunction %sb_f32 %bool %sb_f32 %sb_f32\n"
1240 "%choose_input_func = OpFunction %sb_f32 None %selector_func_type\n"
1241 "%is_neg_param = OpFunctionParameter %bool\n"
1242 "%first_ptr_param = OpFunctionParameter %sb_f32\n"
1243 "%second_ptr_param = OpFunctionParameter %sb_f32\n"
1244 "%selector_func_begin = OpLabel\n"
1245 "%result_ptr = OpSelect %sb_f32 %is_neg_param %first_ptr_param %second_ptr_param\n"
1246 "OpReturnValue %result_ptr\n"
1249 const StringTemplate decoration (
1250 "OpMemberDecorate %buf 0 Offset 0\n"
1251 "OpDecorate %buf Block\n"
1252 "OpDecorate %ra_f32 ArrayStride 4\n"
1253 "OpDecorate %sb_f32 ArrayStride 4\n"
1254 "OpDecorate %indata_a DescriptorSet 0\n"
1255 "OpDecorate %indata_b DescriptorSet 0\n"
1256 "OpDecorate %indata_s DescriptorSet 0\n"
1257 "OpDecorate %outdata DescriptorSet 0\n"
1258 "OpDecorate %indata_a Binding 0\n"
1259 "OpDecorate %indata_b Binding 1\n"
1260 "OpDecorate %indata_s Binding 2\n"
1261 "OpDecorate %outdata Binding 3\n");
1263 const StringTemplate testFunction (
1264 "%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
1265 "%param = OpFunctionParameter %v4f32\n"
1266 "%entry = OpLabel\n"
1268 "${ExtraFunctionScopeVars}"
1270 "%i = OpVariable %fp_i32 Function\n"
1272 "%should_run = OpFunctionCall %bool %isUniqueIdZero\n"
1273 " OpSelectionMerge %end_if None\n"
1274 " OpBranchConditional %should_run %run_test %end_if\n"
1276 "%run_test = OpLabel\n"
1277 " OpStore %i %c_i32_0\n"
1281 "%15 = OpLoad %i32 %i\n"
1282 "%lt = OpSLessThan %bool %15 %c_i32_limit\n"
1283 " OpLoopMerge %merge %inc None\n"
1284 " OpBranchConditional %lt %write %merge\n"
1286 "%write = OpLabel\n"
1287 "%30 = OpLoad %i32 %i\n"
1288 "%two_i = OpIAdd %i32 %30 %30\n"
1289 "%two_i_plus_1 = OpIAdd %i32 %two_i %c_i32_1\n"
1290 "%loc_s_i = OpAccessChain %sb_f32 %indata_s %c_i32_0 %30\n"
1291 "%loc_a_i = OpAccessChain %sb_f32 %indata_a %c_i32_0 %30\n"
1292 "%loc_b_i = OpAccessChain %sb_f32 %indata_b %c_i32_0 %30\n"
1293 "%loc_a_2i = OpAccessChain %sb_f32 %indata_a %c_i32_0 %two_i\n"
1294 "%loc_a_2i_plus_1 = OpAccessChain %sb_f32 %indata_a %c_i32_0 %two_i_plus_1\n"
1295 "%loc_outdata_i = OpAccessChain %sb_f32 %outdata %c_i32_0 %30\n"
1296 "%val_s_i = OpLoad %f32 %loc_s_i\n"
1297 "%is_neg = OpFOrdLessThan %bool %val_s_i %c_f32_0\n"
1299 // select using a strategy.
1302 // load through the variable pointer
1303 "%mux_output = OpLoad %f32 ${VarPtrName}\n"
1305 // store to the output vector.
1306 " OpStore %loc_outdata_i %mux_output\n"
1310 " %37 = OpLoad %i32 %i\n"
1311 " %39 = OpIAdd %i32 %37 %c_i32_1\n"
1315 // Return and FunctionEnd
1316 "%merge = OpLabel\n"
1317 " OpBranch %end_if\n"
1318 "%end_if = OpLabel\n"
1319 "OpReturnValue %param\n"
1322 const bool singleInputBuffer[] = { true, false };
1323 for (int inputBufferTypeIndex = 0 ; inputBufferTypeIndex < 2; ++inputBufferTypeIndex)
1325 const bool isSingleInputBuffer = singleInputBuffer[inputBufferTypeIndex];
1326 const string cap = isSingleInputBuffer ? "OpCapability VariablePointersStorageBuffer\n" : "OpCapability VariablePointers\n";
1327 const vector<float>& expectedOutput = isSingleInputBuffer ? AmuxAOutputFloats : AmuxBOutputFloats;
1328 const vector<float>& expectedIncrOutput = isSingleInputBuffer ? incrAmuxAOutputFloats : incrAmuxBOutputFloats;
1329 const string bufferType = isSingleInputBuffer ? "single_buffer" : "two_buffers";
1330 const string muxInput1 = isSingleInputBuffer ? " %loc_a_2i " : " %loc_a_i ";
1331 const string muxInput2 = isSingleInputBuffer ? " %loc_a_2i_plus_1 " : " %loc_b_i ";
1333 // Set the proper extension features required for the test
1334 if (isSingleInputBuffer)
1335 requiredFeatures.extVariablePointers = EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS_STORAGEBUFFER;
1337 requiredFeatures.extVariablePointers = EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS;
1339 // All of the following tests write their results into an output SSBO, therefore they require the following features.
1340 requiredFeatures.coreFeatures.vertexPipelineStoresAndAtomics = DE_TRUE;
1341 requiredFeatures.coreFeatures.fragmentStoresAndAtomics = DE_TRUE;
1343 { // Variable Pointer Reads (using OpSelect)
1344 GraphicsResources resources;
1345 map<string, string> specs;
1346 string name = "reads_opselect_" + bufferType;
1347 specs["ExtraTypes"] = "";
1348 specs["ExtraGlobalScopeVars"] = "";
1349 specs["ExtraFunctionScopeVars"] = "";
1350 specs["ExtraFunctions"] = "";
1351 specs["VarPtrName"] = "%mux_output_var_ptr";
1352 specs["ResultStrategy"] = "%mux_output_var_ptr = OpSelect %sb_f32 %is_neg" + muxInput1 + muxInput2 + "\n";
1354 fragments["capability"] = cap;
1355 fragments["decoration"] = decoration.specialize(specs);
1356 fragments["pre_main"] = preMain.specialize(specs);
1357 fragments["testfun"] = testFunction.specialize(specs);
1359 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1360 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1361 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1362 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1363 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1365 { // Variable Pointer Reads (using OpFunctionCall)
1366 GraphicsResources resources;
1367 map<string, string> specs;
1368 string name = "reads_opfunctioncall_" + bufferType;
1369 specs["ExtraTypes"] = "";
1370 specs["ExtraGlobalScopeVars"] = "";
1371 specs["ExtraFunctionScopeVars"] = "";
1372 specs["ExtraFunctions"] = selectorFunction;
1373 specs["VarPtrName"] = "%mux_output_var_ptr";
1374 specs["ResultStrategy"] = "%mux_output_var_ptr = OpFunctionCall %sb_f32 %choose_input_func %is_neg" + muxInput1 + muxInput2 + "\n";
1376 fragments["capability"] = cap;
1377 fragments["decoration"] = decoration.specialize(specs);
1378 fragments["pre_main"] = preMain.specialize(specs);
1379 fragments["testfun"] = testFunction.specialize(specs);
1381 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1382 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1383 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1384 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1385 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1387 { // Variable Pointer Reads (using OpPhi)
1388 GraphicsResources resources;
1389 map<string, string> specs;
1390 string name = "reads_opphi_" + bufferType;
1391 specs["ExtraTypes"] = "";
1392 specs["ExtraGlobalScopeVars"] = "";
1393 specs["ExtraFunctionScopeVars"] = "";
1394 specs["ExtraFunctions"] = "";
1395 specs["VarPtrName"] = "%mux_output_var_ptr";
1396 specs["ResultStrategy"] =
1397 " OpSelectionMerge %end_label None\n"
1398 " OpBranchConditional %is_neg %take_mux_input_1 %take_mux_input_2\n"
1399 "%take_mux_input_1 = OpLabel\n"
1400 " OpBranch %end_label\n"
1401 "%take_mux_input_2 = OpLabel\n"
1402 " OpBranch %end_label\n"
1403 "%end_label = OpLabel\n"
1404 "%mux_output_var_ptr = OpPhi %sb_f32" + muxInput1 + "%take_mux_input_1" + muxInput2 + "%take_mux_input_2\n";
1406 fragments["capability"] = cap;
1407 fragments["decoration"] = decoration.specialize(specs);
1408 fragments["pre_main"] = preMain.specialize(specs);
1409 fragments["testfun"] = testFunction.specialize(specs);
1411 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1412 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1413 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1414 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1415 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1417 { // Variable Pointer Reads (using OpCopyObject)
1418 GraphicsResources resources;
1419 map<string, string> specs;
1420 string name = "reads_opcopyobject_" + bufferType;
1421 specs["ExtraTypes"] = "";
1422 specs["ExtraGlobalScopeVars"] = "";
1423 specs["ExtraFunctionScopeVars"] = "";
1424 specs["ExtraFunctions"] = "";
1425 specs["VarPtrName"] = "%mux_output_var_ptr";
1426 specs["ResultStrategy"] =
1427 "%mux_input_1_copy = OpCopyObject %sb_f32" + muxInput1 + "\n"
1428 "%mux_input_2_copy = OpCopyObject %sb_f32" + muxInput2 + "\n"
1429 "%mux_output_var_ptr = OpSelect %sb_f32 %is_neg %mux_input_1_copy %mux_input_2_copy\n";
1431 fragments["capability"] = cap;
1432 fragments["decoration"] = decoration.specialize(specs);
1433 fragments["pre_main"] = preMain.specialize(specs);
1434 fragments["testfun"] = testFunction.specialize(specs);
1436 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1437 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1438 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1439 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1440 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1442 { // Test storing into Private variables.
1443 const char* storageClasses[] = {"Private", "Function"};
1444 for (int classId = 0; classId < 2; ++classId)
1446 GraphicsResources resources;
1447 map<string, string> specs;
1448 std::string storageClass = storageClasses[classId];
1449 std::string name = "stores_" + string(de::toLower(storageClass)) + "_" + bufferType;
1450 std::string extraVariable = "%mux_output_copy = OpVariable %sb_f32ptrptr " + storageClass + "\n";
1451 specs["ExtraTypes"] = "%sb_f32ptrptr = OpTypePointer " + storageClass + " %sb_f32\n";
1452 specs["ExtraGlobalScopeVars"] = (classId == 0) ? extraVariable : "";
1453 specs["ExtraFunctionScopeVars"] = (classId == 1) ? extraVariable : "";
1454 specs["ExtraFunctions"] = "";
1455 specs["VarPtrName"] = "%mux_output_var_ptr";
1456 specs["ResultStrategy"] =
1457 "%opselect_result = OpSelect %sb_f32 %is_neg" + muxInput1 + muxInput2 + "\n"
1458 " OpStore %mux_output_copy %opselect_result\n"
1459 "%mux_output_var_ptr = OpLoad %sb_f32 %mux_output_copy\n";
1461 fragments["capability"] = cap;
1462 fragments["decoration"] = decoration.specialize(specs);
1463 fragments["pre_main"] = preMain.specialize(specs);
1464 fragments["testfun"] = testFunction.specialize(specs);
1466 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1467 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1468 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1469 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1470 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1473 { // Variable Pointer Reads (using OpPtrAccessChain)
1474 GraphicsResources resources;
1475 map<string, string> specs;
1476 std::string name = "reads_opptraccesschain_" + bufferType;
1477 std::string in_1 = isSingleInputBuffer ? " %a_2i_ptr " : " %a_i_ptr ";
1478 std::string in_2 = isSingleInputBuffer ? " %a_2i_plus_1_ptr " : " %b_i_ptr ";
1479 specs["ExtraTypes"] = "";
1480 specs["ExtraGlobalScopeVars"] = "";
1481 specs["ExtraFunctionScopeVars"] = "";
1482 specs["ExtraFunctions"] = "";
1483 specs["VarPtrName"] = "%mux_output_var_ptr";
1484 specs["ResultStrategy"] =
1485 "%a_ptr = OpAccessChain %sb_f32 %indata_a %c_i32_0 %c_i32_0\n"
1486 "%b_ptr = OpAccessChain %sb_f32 %indata_b %c_i32_0 %c_i32_0\n"
1487 "%s_ptr = OpAccessChain %sb_f32 %indata_s %c_i32_0 %c_i32_0\n"
1488 "%out_ptr = OpAccessChain %sb_f32 %outdata %c_i32_0 %c_i32_0\n"
1489 "%a_i_ptr = OpPtrAccessChain %sb_f32 %a_ptr %30\n"
1490 "%b_i_ptr = OpPtrAccessChain %sb_f32 %b_ptr %30\n"
1491 "%s_i_ptr = OpPtrAccessChain %sb_f32 %s_ptr %30\n"
1492 "%a_2i_ptr = OpPtrAccessChain %sb_f32 %a_ptr %two_i\n"
1493 "%a_2i_plus_1_ptr = OpPtrAccessChain %sb_f32 %a_ptr %two_i_plus_1\n"
1494 "%mux_output_var_ptr = OpSelect %sb_f32 %is_neg " + in_1 + in_2 + "\n";
1496 fragments["decoration"] = decoration.specialize(specs);
1497 fragments["pre_main"] = preMain.specialize(specs);
1498 fragments["testfun"] = testFunction.specialize(specs);
1499 fragments["capability"] = cap;
1501 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1502 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1503 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1504 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1505 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1507 { // Variable Pointer Writes
1508 GraphicsResources resources;
1509 map<string, string> specs;
1510 std::string name = "writes_" + bufferType;
1511 specs["ExtraTypes"] = "";
1512 specs["ExtraGlobalScopeVars"] = "";
1513 specs["ExtraFunctionScopeVars"] = "";
1514 specs["ExtraFunctions"] = "";
1515 specs["VarPtrName"] = "%mux_output_var_ptr";
1516 specs["ResultStrategy"] =
1517 "%mux_output_var_ptr = OpSelect %sb_f32 %is_neg" + muxInput1 + muxInput2 + "\n" +
1518 " %val = OpLoad %f32 %mux_output_var_ptr\n"
1519 " %val_plus_1 = OpFAdd %f32 %val %c_f32_1\n"
1520 " OpStore %mux_output_var_ptr %val_plus_1\n";
1521 fragments["capability"] = cap;
1522 fragments["decoration"] = decoration.specialize(specs);
1523 fragments["pre_main"] = preMain.specialize(specs);
1524 fragments["testfun"] = testFunction.specialize(specs);
1526 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputAFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1527 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1528 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputSFloats)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1529 resources.outputs.push_back(Resource(BufferSp(new Float32Buffer(expectedIncrOutput)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1530 createTestsForAllStages(name.c_str(), defaultColors, defaultColors, fragments, resources, extensions, testGroup, requiredFeatures);
1535 // Modifies the 'red channel' of the input color to the given float value.
1536 // Returns the modified color.
1537 void getExpectedOutputColor(RGBA (&inputColors)[4], RGBA (&expectedOutputColors)[4], float val)
1539 Vec4 inColor0 = inputColors[0].toVec();
1540 Vec4 inColor1 = inputColors[1].toVec();
1541 Vec4 inColor2 = inputColors[2].toVec();
1542 Vec4 inColor3 = inputColors[3].toVec();
1547 expectedOutputColors[0] = RGBA(inColor0);
1548 expectedOutputColors[1] = RGBA(inColor1);
1549 expectedOutputColors[2] = RGBA(inColor2);
1550 expectedOutputColors[3] = RGBA(inColor3);
1553 void addTwoInputBufferReadOnlyVariablePointersGraphicsGroup (tcu::TestCaseGroup* testGroup)
1555 const int numFloatsPerInput = 64;
1556 vector<float> inputA (numFloatsPerInput, 0);
1557 vector<float> inputB (numFloatsPerInput, 0);
1558 deUint32 baseOffset = -1;
1559 VulkanFeatures requiredFeatures;
1560 map<string, string> fragments;
1561 RGBA defaultColors[4];
1562 RGBA expectedColors[4];
1563 vector<string> extensions;
1565 getDefaultColors(defaultColors);
1567 // Set the proper extension features required for the tests.
1568 requiredFeatures.extVariablePointers = EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS;
1570 // Set the required extension.
1571 extensions.push_back("VK_KHR_variable_pointers");
1573 // These tests exercise variable pointers into various levels of the following data-structure:
1574 // struct struct inner_struct {
1575 // vec4 x[2]; // array of 2 vectors. Each vector is 4 floats.
1576 // vec4 y[2]; // array of 2 vectors. Each vector is 4 floats.
1579 // struct outer_struct {
1580 // inner_struct r[2][2];
1583 // The inner_struct contains 16 floats, and the outer_struct contains 64 floats.
1584 // Therefore the input can be an array of 64 floats.
1586 // Populate the first input (inputA) to contain: {0, 4, ... , 252} / 255.f
1587 // Populate the second input (inputB) to contain: {3, 7, ... , 255} / 255.f
1588 for (size_t i = 0; i < numFloatsPerInput; ++i)
1590 inputA[i] = 4*float(i) / 255;
1591 inputB[i] = ((4*float(i)) + 3) / 255;
1594 // In the following tests we use variable pointers to point to different types:
1595 // nested structures, matrices of structures, arrays of structures, arrays of vectors, vectors of scalars, and scalars.
1596 // outer_structure.inner_structure[?][?].x[?][?];
1599 // 1. inputA or inputB = nested structure
1600 // 2. inputA.r or inputB.r = matrices of structures
1601 // 3. inputA.r[?] or inputB.r[?] = arrays of structures
1602 // 4. inputA.r[?][?] or inputB.r[?][?] = structures
1603 // 5. inputA.r[?][?].(x|y) or inputB.r[?][?].(x|y) = arrays of vectors
1604 // 6. inputA.r[?][?].(x|y)[?] or inputB.r[?][?].(x|y)[?] = vectors of scalars
1605 // 7. inputA.r[?][?].(x|y)[?][?] or inputB.r[?][?].(x|y)[?][?] = scalars
1606 const int numLevels = 7;
1608 fragments["capability"] = "OpCapability VariablePointers \n";
1609 fragments["extension"] = "OpExtension \"SPV_KHR_variable_pointers\" \n"
1610 "OpExtension \"SPV_KHR_storage_buffer_storage_class\" \n";
1612 const StringTemplate decoration (
1614 "OpMemberDecorate %outer_struct 0 Offset 0 \n"
1615 "OpMemberDecorate %inner_struct 0 Offset 0 \n"
1616 "OpMemberDecorate %inner_struct 1 Offset 32 \n"
1618 // Set the ArrayStrides
1619 "OpDecorate %arr2_v4float ArrayStride 16 \n"
1620 "OpDecorate %arr2_inner_struct ArrayStride 64 \n"
1621 "OpDecorate %mat2x2_inner_struct ArrayStride 128 \n"
1622 "OpDecorate %mat2x2_ptr ArrayStride 128 \n"
1623 "OpDecorate %sb_buf ArrayStride 256 \n"
1624 "OpDecorate %v4f32_ptr ArrayStride 16 \n"
1626 "OpDecorate %outer_struct Block \n"
1628 "OpDecorate %in_a DescriptorSet 0 \n"
1629 "OpDecorate %in_b DescriptorSet 0 \n"
1630 "OpDecorate %in_a Binding 0 \n"
1631 "OpDecorate %in_b Binding 1 \n"
1634 const StringTemplate preMain (
1639 // struct struct inner_struct {
1640 // vec4 x[2]; // array of 2 vectors
1641 // vec4 y[2]; // array of 2 vectors
1643 "%arr2_v4float = OpTypeArray %v4f32 %c_u32_2 \n"
1644 "%inner_struct = OpTypeStruct %arr2_v4float %arr2_v4float \n"
1646 // struct outer_struct {
1647 // inner_struct r[2][2];
1649 "%arr2_inner_struct = OpTypeArray %inner_struct %c_u32_2 \n"
1650 "%mat2x2_inner_struct = OpTypeArray %arr2_inner_struct %c_u32_2 \n"
1651 "%outer_struct = OpTypeStruct %mat2x2_inner_struct \n"
1656 "%sb_buf = OpTypePointer StorageBuffer %outer_struct \n"
1657 "%mat2x2_ptr = OpTypePointer StorageBuffer %mat2x2_inner_struct \n"
1658 "%arr2_ptr = OpTypePointer StorageBuffer %arr2_inner_struct \n"
1659 "%inner_struct_ptr = OpTypePointer StorageBuffer %inner_struct \n"
1660 "%arr_v4f32_ptr = OpTypePointer StorageBuffer %arr2_v4float \n"
1661 "%v4f32_ptr = OpTypePointer StorageBuffer %v4f32 \n"
1662 "%sb_f32ptr = OpTypePointer StorageBuffer %f32 \n"
1667 "%in_a = OpVariable %sb_buf StorageBuffer \n"
1668 "%in_b = OpVariable %sb_buf StorageBuffer \n"
1673 "%c_bool_true = OpConstantTrue %bool \n"
1674 "%c_bool_false = OpConstantFalse %bool \n"
1676 //////////////////////
1677 // HELPER FUNCTIONS //
1678 //////////////////////
1679 "${helper_functions} \n"
1682 const StringTemplate selectorFunctions (
1683 // This selector function returns a variable pointer.
1684 // These functions are used by tests that use OpFunctionCall to obtain the variable pointer.
1685 "%selector_func_type = OpTypeFunction ${selected_type} %bool ${selected_type} ${selected_type}\n"
1686 "%choose_input_func = OpFunction ${selected_type} None %selector_func_type\n"
1687 "%choose_first_param = OpFunctionParameter %bool\n"
1688 "%first_param = OpFunctionParameter ${selected_type}\n"
1689 "%second_param = OpFunctionParameter ${selected_type}\n"
1690 "%selector_func_begin = OpLabel\n"
1691 "%result_ptr = OpSelect ${selected_type} %choose_first_param %first_param %second_param\n"
1692 " OpReturnValue %result_ptr\n"
1696 const StringTemplate testFunction (
1697 "%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
1698 "%param = OpFunctionParameter %v4f32\n"
1699 "%entry = OpLabel\n"
1701 // Define base pointers for OpPtrAccessChain
1702 "%in_a_matptr = OpAccessChain %mat2x2_ptr %in_a %c_i32_0\n"
1703 "%in_b_matptr = OpAccessChain %mat2x2_ptr %in_b %c_i32_0\n"
1705 // Define the 2 pointers from which we're going to choose one.
1709 // Choose between the 2 pointers / variable pointers
1710 "${selection_strategy} \n"
1712 // OpAccessChain into the variable pointer until you get to the float.
1713 "%result_loc = OpAccessChain %sb_f32ptr %var_ptr ${remaining_indexes} \n"
1715 // Now load from the result_loc
1716 "%result_val = OpLoad %f32 %result_loc\n"
1718 // Modify the 'RED channel' of the output color to the chosen value
1719 "%output_color = OpCompositeInsert %v4f32 %result_val %param 0 \n"
1721 // Return and FunctionEnd
1722 "OpReturnValue %output_color\n"
1725 // When select is 0, the variable pointer should point to a value in the first input (inputA).
1726 // When select is 1, the variable pointer should point to a value in the second input (inputB).
1727 for (int selectInputA = 0; selectInputA < 2; ++selectInputA)
1729 const string selectedInputStr = selectInputA ? "first_input" : "second_input";
1730 const string spirvSelectInputA = selectInputA ? "%c_bool_true" : "%c_bool_false";
1731 vector<float>& selectedInput = selectInputA ? inputA : inputB;
1733 // The indexes chosen at each level. At any level, any given offset is exercised.
1734 // The first index is always zero as the outer structure has only 1 member.
1735 const int indexesForLevel[numLevels][6]= {{0, 0, 0, 0, 0, 1},
1741 {0, 1, 1, 1, 1, 3}};
1743 const string indexLevelNames[] = {"_outer_struct_", "_matrices_", "_arrays_", "_inner_structs_", "_vec4arr_", "_vec4_", "_float_"};
1744 const string inputALocations[] = { "",
1745 "%a_loc = OpAccessChain %mat2x2_ptr %in_a %c_i32_0",
1746 "%a_loc = OpAccessChain %arr2_ptr %in_a %c_i32_0 %c_i32_0",
1747 "%a_loc = OpAccessChain %inner_struct_ptr %in_a %c_i32_0 %c_i32_1 %c_i32_1",
1748 "%a_loc = OpAccessChain %arr_v4f32_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
1749 "%a_loc = OpAccessChain %v4f32_ptr %in_a %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
1750 "%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"};
1752 const string inputBLocations[] = { "",
1753 "%b_loc = OpAccessChain %mat2x2_ptr %in_b %c_i32_0",
1754 "%b_loc = OpAccessChain %arr2_ptr %in_b %c_i32_0 %c_i32_0",
1755 "%b_loc = OpAccessChain %inner_struct_ptr %in_b %c_i32_0 %c_i32_1 %c_i32_1",
1756 "%b_loc = OpAccessChain %arr_v4f32_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
1757 "%b_loc = OpAccessChain %v4f32_ptr %in_b %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
1758 "%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"};
1760 const string inputAPtrAccessChain[] = { "",
1761 "%a_loc = OpPtrAccessChain %mat2x2_ptr %in_a_matptr %c_i32_0",
1762 "%a_loc = OpPtrAccessChain %arr2_ptr %in_a_matptr %c_i32_0 %c_i32_0",
1763 "%a_loc = OpPtrAccessChain %inner_struct_ptr %in_a_matptr %c_i32_0 %c_i32_1 %c_i32_1",
1764 "%a_loc = OpPtrAccessChain %arr_v4f32_ptr %in_a_matptr %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
1765 "%a_loc = OpPtrAccessChain %v4f32_ptr %in_a_matptr %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
1766 // Next case emulates:
1767 // %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
1768 // But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
1769 // %a_loc_arr is a pointer to an array that we want to index with 1.
1770 // But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
1771 // get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
1772 "%a_loc_arr = OpPtrAccessChain %arr_v4f32_ptr %in_a_matptr %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
1773 "%a_loc_first_elem = OpAccessChain %v4f32_ptr %a_loc_arr %c_i32_0 "
1774 "%a_loc = OpPtrAccessChain %sb_f32ptr %a_loc_first_elem %c_i32_1 %c_i32_3"};
1776 const string inputBPtrAccessChain[] = { "",
1777 "%b_loc = OpPtrAccessChain %mat2x2_ptr %in_b_matptr %c_i32_0",
1778 "%b_loc = OpPtrAccessChain %arr2_ptr %in_b_matptr %c_i32_0 %c_i32_0",
1779 "%b_loc = OpPtrAccessChain %inner_struct_ptr %in_b_matptr %c_i32_0 %c_i32_1 %c_i32_1",
1780 "%b_loc = OpPtrAccessChain %arr_v4f32_ptr %in_b_matptr %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
1781 "%b_loc = OpPtrAccessChain %v4f32_ptr %in_b_matptr %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
1782 // Next case emulates:
1783 // %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
1784 // But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
1785 // %b_loc_arr is a pointer to an array that we want to index with 1.
1786 // But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
1787 // get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
1788 "%b_loc_arr = OpPtrAccessChain %arr_v4f32_ptr %in_b_matptr %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
1789 "%b_loc_first_elem = OpAccessChain %v4f32_ptr %b_loc_arr %c_i32_0 "
1790 "%b_loc = OpPtrAccessChain %sb_f32ptr %b_loc_first_elem %c_i32_1 %c_i32_3"};
1793 const string remainingIndexesAtLevel[]= {"%c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
1794 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_2",
1795 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_3",
1796 "%c_i32_1 %c_i32_0 %c_i32_0",
1797 "%c_i32_1 %c_i32_1",
1801 const string pointerTypeAtLevel[] = {"%sb_buf", "%mat2x2_ptr", "%arr2_ptr", "%inner_struct_ptr", "%arr_v4f32_ptr", "%v4f32_ptr", "%sb_f32ptr"};
1802 const string baseANameAtLevel[] = {"%in_a", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc"};
1803 const string baseBNameAtLevel[] = {"%in_b", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc"};
1805 for (int indexLevel = 0; indexLevel < numLevels; ++indexLevel)
1807 // index into the outer structure must be zero since the outer structure has only 1 member.
1808 DE_ASSERT(indexesForLevel[indexLevel][0] == 0);
1810 baseOffset = getBaseOffset(indexesForLevel[indexLevel][1],
1811 indexesForLevel[indexLevel][2],
1812 indexesForLevel[indexLevel][3],
1813 indexesForLevel[indexLevel][4],
1814 indexesForLevel[indexLevel][5]);
1816 // Use OpSelect to choose between 2 pointers
1818 GraphicsResources resources;
1819 map<string, string> specs;
1820 string opCodeForTests = "opselect";
1821 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
1822 specs["select_inputA"] = spirvSelectInputA;
1823 specs["helper_functions"] = "";
1824 specs["a_loc"] = inputALocations[indexLevel];
1825 specs["b_loc"] = inputBLocations[indexLevel];
1826 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
1827 specs["selection_strategy"] = "%var_ptr = OpSelect " +
1828 pointerTypeAtLevel[indexLevel] + " " +
1829 spirvSelectInputA + " " +
1830 baseANameAtLevel[indexLevel] + " " +
1831 baseBNameAtLevel[indexLevel] + "\n";
1832 fragments["decoration"] = decoration.specialize(specs);
1833 fragments["pre_main"] = preMain.specialize(specs);
1834 fragments["testfun"] = testFunction.specialize(specs);
1835 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1836 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1837 getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
1838 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
1840 // Use OpCopyObject to get variable pointers
1842 GraphicsResources resources;
1843 map<string, string> specs;
1844 string opCodeForTests = "opcopyobject";
1845 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
1846 specs["select_inputA"] = spirvSelectInputA;
1847 specs["helper_functions"] = "";
1848 specs["a_loc"] = inputALocations[indexLevel];
1849 specs["b_loc"] = inputBLocations[indexLevel];
1850 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
1851 specs["selection_strategy"] =
1852 "%in_a_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseANameAtLevel[indexLevel] + "\n"
1853 "%in_b_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseBNameAtLevel[indexLevel] + "\n"
1854 "%var_ptr = OpSelect " + pointerTypeAtLevel[indexLevel] + " " + spirvSelectInputA + " %in_a_copy %in_b_copy\n";
1855 fragments["decoration"] = decoration.specialize(specs);
1856 fragments["pre_main"] = preMain.specialize(specs);
1857 fragments["testfun"] = testFunction.specialize(specs);
1858 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1859 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1860 getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
1861 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
1863 // Use OpPhi to choose between 2 pointers
1865 GraphicsResources resources;
1866 map<string, string> specs;
1867 string opCodeForTests = "opphi";
1868 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
1869 specs["select_inputA"] = spirvSelectInputA;
1870 specs["helper_functions"] = "";
1871 specs["a_loc"] = inputALocations[indexLevel];
1872 specs["b_loc"] = inputBLocations[indexLevel];
1873 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
1874 specs["selection_strategy"] =
1875 " OpSelectionMerge %end_label None\n"
1876 " OpBranchConditional " + spirvSelectInputA + " %take_input_a %take_input_b\n"
1877 "%take_input_a = OpLabel\n"
1878 " OpBranch %end_label\n"
1879 "%take_input_b = OpLabel\n"
1880 " OpBranch %end_label\n"
1881 "%end_label = OpLabel\n"
1883 + pointerTypeAtLevel[indexLevel] + " "
1884 + baseANameAtLevel[indexLevel]
1886 + baseBNameAtLevel[indexLevel]
1887 + " %take_input_b\n";
1888 fragments["decoration"] = decoration.specialize(specs);
1889 fragments["pre_main"] = preMain.specialize(specs);
1890 fragments["testfun"] = testFunction.specialize(specs);
1891 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1892 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1893 getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
1894 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
1896 // Use OpFunctionCall to choose between 2 pointers
1898 GraphicsResources resources;
1899 map<string, string> functionSpecs;
1900 map<string, string> specs;
1901 string opCodeForTests = "opfunctioncall";
1902 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
1903 functionSpecs["selected_type"] = pointerTypeAtLevel[indexLevel];
1904 specs["helper_functions"] = selectorFunctions.specialize(functionSpecs);
1905 specs["select_inputA"] = spirvSelectInputA;
1906 specs["a_loc"] = inputALocations[indexLevel];
1907 specs["b_loc"] = inputBLocations[indexLevel];
1908 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
1909 specs["selection_strategy"] = "%var_ptr = OpFunctionCall "
1910 + pointerTypeAtLevel[indexLevel]
1911 + " %choose_input_func "
1912 + spirvSelectInputA + " "
1913 + baseANameAtLevel[indexLevel] + " "
1914 + baseBNameAtLevel[indexLevel] + "\n";
1915 fragments["decoration"] = decoration.specialize(specs);
1916 fragments["pre_main"] = preMain.specialize(specs);
1917 fragments["testfun"] = testFunction.specialize(specs);
1918 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1919 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1920 getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
1921 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
1923 // Use OpPtrAccessChain to get variable pointers
1925 GraphicsResources resources;
1926 map<string, string> specs;
1927 string opCodeForTests = "opptraccesschain";
1928 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
1929 specs["select_inputA"] = spirvSelectInputA;
1930 specs["helper_functions"] = "";
1931 specs["a_loc"] = inputAPtrAccessChain[indexLevel];
1932 specs["b_loc"] = inputBPtrAccessChain[indexLevel];
1933 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
1934 specs["selection_strategy"] = "%var_ptr = OpSelect "
1935 + pointerTypeAtLevel[indexLevel] + " "
1936 + spirvSelectInputA + " "
1937 + baseANameAtLevel[indexLevel] + " "
1938 + baseBNameAtLevel[indexLevel] + "\n";
1939 fragments["decoration"] = decoration.specialize(specs);
1940 fragments["pre_main"] = preMain.specialize(specs);
1941 fragments["testfun"] = testFunction.specialize(specs);
1942 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputA)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1943 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputB)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
1944 getExpectedOutputColor(defaultColors, expectedColors, selectedInput[baseOffset]);
1945 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
1951 void addSingleInputBufferReadOnlyVariablePointersGraphicsGroup (tcu::TestCaseGroup* testGroup)
1953 const int numFloatsPerInnerStruct = 64;
1954 vector<float> inputBuffer (2 * numFloatsPerInnerStruct, 0);
1955 deUint32 baseOffset = -1;
1956 VulkanFeatures requiredFeatures;
1957 map<string, string> fragments;
1958 RGBA defaultColors[4];
1959 RGBA expectedColors[4];
1960 vector<string> extensions;
1962 // Set the proper extension features required for the tests.
1963 // The following tests use variable pointers confined withing a single buffer.
1964 requiredFeatures.extVariablePointers = EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS_STORAGEBUFFER;
1966 // Set the required extension.
1967 extensions.push_back("VK_KHR_variable_pointers");
1969 getDefaultColors(defaultColors);
1971 // These tests exercise variable pointers into various levels of the following data-structure:
1972 // struct struct inner_struct {
1973 // vec4 x[2]; // array of 2 vectors. Each vector is 4 floats.
1974 // vec4 y[2]; // array of 2 vectors. Each vector is 4 floats.
1977 // struct outer_struct {
1978 // inner_struct r[2][2];
1981 // struct input_buffer {
1986 // The inner_struct contains 16 floats, and the outer_struct contains 64 floats.
1987 // Therefore the input_buffer can be an array of 128 floats.
1989 // Populate input_buffer's first member (a) to contain: {0, 4, ... , 252} / 255.f
1990 // Populate input_buffer's second member (b) to contain: {3, 7, ... , 255} / 255.f
1991 for (size_t i = 0; i < numFloatsPerInnerStruct; ++i)
1993 inputBuffer[i] = 4*float(i) / 255;
1994 inputBuffer[i + numFloatsPerInnerStruct] = ((4*float(i)) + 3) / 255;
1997 // In the following tests we use variable pointers to point to different types:
1998 // nested structures, matrices of structures, arrays of structures, arrays of vectors, vectors of scalars, and scalars.
1999 // outer_struct.inner_struct[?][?].x[?][?];
2002 // 1. inputBuffer.a or inputBuffer.b = nested structure
2003 // 2. inputBuffer.a.r or inputBuffer.b.r = matrices of structures
2004 // 3. inputBuffer.a.r[?] or inputBuffer.b.r[?] = arrays of structures
2005 // 4. inputBuffer.a.r[?][?] or inputBuffer.b.r[?][?] = structures
2006 // 5. inputBuffer.a.r[?][?].(x|y) or inputBuffer.b.r[?][?].(x|y) = arrays of vectors
2007 // 6. inputBuffer.a.r[?][?].(x|y)[?] or inputBuffer.b.r[?][?].(x|y)[?] = vectors of scalars
2008 // 7. inputBuffer.a.r[?][?].(x|y)[?][?] or inputBuffer.b.r[?][?].(x|y)[?][?] = scalars
2009 const int numLevels = 7;
2011 fragments["capability"] = "OpCapability VariablePointersStorageBuffer \n";
2012 fragments["extension"] = "OpExtension \"SPV_KHR_variable_pointers\" \n"
2013 "OpExtension \"SPV_KHR_storage_buffer_storage_class\" \n";
2014 const StringTemplate decoration (
2015 // Set the ArrayStrides
2016 "OpDecorate %arr2_v4float ArrayStride 16 \n"
2017 "OpDecorate %arr2_inner_struct ArrayStride 64 \n"
2018 "OpDecorate %mat2x2_inner_struct ArrayStride 128 \n"
2019 "OpDecorate %outer_struct_ptr ArrayStride 256 \n"
2020 "OpDecorate %v4f32_ptr ArrayStride 16 \n"
2023 "OpMemberDecorate %input_buffer 0 Offset 0 \n"
2024 "OpMemberDecorate %input_buffer 1 Offset 256 \n"
2025 "OpMemberDecorate %outer_struct 0 Offset 0 \n"
2026 "OpMemberDecorate %inner_struct 0 Offset 0 \n"
2027 "OpMemberDecorate %inner_struct 1 Offset 32 \n"
2029 "OpDecorate %input_buffer Block \n"
2031 "OpDecorate %input DescriptorSet 0 \n"
2032 "OpDecorate %input Binding 0 \n"
2035 const StringTemplate preMain (
2040 // struct struct inner_struct {
2041 // vec4 x[2]; // array of 2 vectors
2042 // vec4 y[2]; // array of 2 vectors
2044 "%arr2_v4float = OpTypeArray %v4f32 %c_u32_2 \n"
2045 "%inner_struct = OpTypeStruct %arr2_v4float %arr2_v4float \n"
2047 // struct outer_struct {
2048 // inner_struct r[2][2];
2050 "%arr2_inner_struct = OpTypeArray %inner_struct %c_u32_2 \n"
2051 "%mat2x2_inner_struct = OpTypeArray %arr2_inner_struct %c_u32_2 \n"
2052 "%outer_struct = OpTypeStruct %mat2x2_inner_struct \n"
2054 // struct input_buffer {
2058 "%input_buffer = OpTypeStruct %outer_struct %outer_struct \n"
2063 "%input_buffer_ptr = OpTypePointer StorageBuffer %input_buffer \n"
2064 "%outer_struct_ptr = OpTypePointer StorageBuffer %outer_struct \n"
2065 "%mat2x2_ptr = OpTypePointer StorageBuffer %mat2x2_inner_struct \n"
2066 "%arr2_ptr = OpTypePointer StorageBuffer %arr2_inner_struct \n"
2067 "%inner_struct_ptr = OpTypePointer StorageBuffer %inner_struct \n"
2068 "%arr_v4f32_ptr = OpTypePointer StorageBuffer %arr2_v4float \n"
2069 "%v4f32_ptr = OpTypePointer StorageBuffer %v4f32 \n"
2070 "%sb_f32ptr = OpTypePointer StorageBuffer %f32 \n"
2075 "%input = OpVariable %input_buffer_ptr StorageBuffer \n"
2080 "%c_bool_true = OpConstantTrue %bool \n"
2081 "%c_bool_false = OpConstantFalse %bool \n"
2083 //////////////////////
2084 // HELPER FUNCTIONS //
2085 //////////////////////
2086 "${helper_functions} \n"
2089 const StringTemplate selectorFunctions (
2090 // These selector functions return variable pointers.
2091 // These functions are used by tests that use OpFunctionCall to obtain the variable pointer
2092 "%selector_func_type = OpTypeFunction ${selected_type} %bool ${selected_type} ${selected_type}\n"
2093 "%choose_input_func = OpFunction ${selected_type} None %selector_func_type\n"
2094 "%choose_first_param = OpFunctionParameter %bool\n"
2095 "%first_param = OpFunctionParameter ${selected_type}\n"
2096 "%second_param = OpFunctionParameter ${selected_type}\n"
2097 "%selector_func_begin = OpLabel\n"
2098 "%result_ptr = OpSelect ${selected_type} %choose_first_param %first_param %second_param\n"
2099 "OpReturnValue %result_ptr\n"
2103 const StringTemplate testFunction (
2104 "%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
2105 "%param = OpFunctionParameter %v4f32\n"
2106 "%entry = OpLabel\n"
2108 // Here are the 2 nested structures:
2109 "%in_a = OpAccessChain %outer_struct_ptr %input %c_i32_0\n"
2110 "%in_b = OpAccessChain %outer_struct_ptr %input %c_i32_1\n"
2112 // Define the 2 pointers from which we're going to choose one.
2116 // Choose between the 2 pointers / variable pointers
2117 "${selection_strategy} \n"
2119 // OpAccessChain into the variable pointer until you get to the float.
2120 "%result_loc = OpAccessChain %sb_f32ptr %var_ptr ${remaining_indexes} \n"
2123 // Now load from the result_loc
2124 "%result_val = OpLoad %f32 %result_loc\n"
2126 // Modify the 'RED channel' of the output color to the chosen value
2127 "%output_color = OpCompositeInsert %v4f32 %result_val %param 0 \n"
2129 // Return and FunctionEnd
2130 "OpReturnValue %output_color\n"
2133 // When select is 0, the variable pointer should point to a value in the first input_buffer member (a).
2134 // When select is 1, the variable pointer should point to a value in the second input_buffer member (b).
2135 // Since the 2 members of the input_buffer (a and b) are of type outer_struct, we can conveniently use
2136 // the same indexing scheme that we used for the 2-input-buffer tests.
2137 for (int selectInputA = 0; selectInputA < 2; ++selectInputA)
2139 const string selectedInputStr = selectInputA ? "first_input" : "second_input";
2140 const string spirvSelectInputA = selectInputA ? "%c_bool_true" : "%c_bool_false";
2141 const int outerStructIndex = selectInputA ? 0 : 1;
2143 // The indexes chosen at each level. At any level, any given offset is exercised.
2144 // outerStructIndex is 0 for member (a) and 1 for member (b).
2145 const int indexesForLevel[numLevels][6]= {{outerStructIndex, 0, 0, 0, 0, 1},
2146 {outerStructIndex, 1, 0, 1, 0, 2},
2147 {outerStructIndex, 0, 1, 0, 1, 3},
2148 {outerStructIndex, 1, 1, 1, 0, 0},
2149 {outerStructIndex, 0, 0, 1, 1, 1},
2150 {outerStructIndex, 1, 0, 0, 0, 2},
2151 {outerStructIndex, 1, 1, 1, 1, 3}};
2153 const string indexLevelNames[] = {"_outer_struct_", "_matrices_", "_arrays_", "_inner_structs_", "_vec4arr_", "_vec4_", "_float_"};
2154 const string inputALocations[] = { "",
2155 "%a_loc = OpAccessChain %mat2x2_ptr %in_a %c_i32_0",
2156 "%a_loc = OpAccessChain %arr2_ptr %in_a %c_i32_0 %c_i32_0",
2157 "%a_loc = OpAccessChain %inner_struct_ptr %in_a %c_i32_0 %c_i32_1 %c_i32_1",
2158 "%a_loc = OpAccessChain %arr_v4f32_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2159 "%a_loc = OpAccessChain %v4f32_ptr %in_a %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
2160 "%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"};
2162 const string inputBLocations[] = { "",
2163 "%b_loc = OpAccessChain %mat2x2_ptr %in_b %c_i32_0",
2164 "%b_loc = OpAccessChain %arr2_ptr %in_b %c_i32_0 %c_i32_0",
2165 "%b_loc = OpAccessChain %inner_struct_ptr %in_b %c_i32_0 %c_i32_1 %c_i32_1",
2166 "%b_loc = OpAccessChain %arr_v4f32_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2167 "%b_loc = OpAccessChain %v4f32_ptr %in_b %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_0 %c_i32_0",
2168 "%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"};
2170 const string inputAPtrAccessChain[] = { "",
2171 "%a_loc = OpPtrAccessChain %mat2x2_ptr %in_a %c_i32_0 %c_i32_0",
2172 "%a_loc = OpPtrAccessChain %arr2_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_0",
2173 "%a_loc = OpPtrAccessChain %inner_struct_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1",
2174 "%a_loc = OpPtrAccessChain %arr_v4f32_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2175 "%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",
2176 // Next case emulates:
2177 // %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
2178 // But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
2179 // %a_loc_arr is a pointer to an array that we want to index with 1.
2180 // But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
2181 // get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
2182 "%a_loc_arr = OpPtrAccessChain %arr_v4f32_ptr %in_a %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
2183 "%a_loc_first_elem = OpAccessChain %v4f32_ptr %a_loc_arr %c_i32_0 "
2184 "%a_loc = OpPtrAccessChain %sb_f32ptr %a_loc_first_elem %c_i32_1 %c_i32_3"};
2186 const string inputBPtrAccessChain[] = { "",
2187 "%b_loc = OpPtrAccessChain %mat2x2_ptr %in_b %c_i32_0 %c_i32_0",
2188 "%b_loc = OpPtrAccessChain %arr2_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_0",
2189 "%b_loc = OpPtrAccessChain %inner_struct_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1",
2190 "%b_loc = OpPtrAccessChain %arr_v4f32_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2191 "%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",
2192 // Next case emulates:
2193 // %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
2194 // But rewrite it to exercise OpPtrAccessChain with a non-zero first index:
2195 // %b_loc_arr is a pointer to an array that we want to index with 1.
2196 // But instead of just using OpAccessChain with first index 1, use OpAccessChain with index 0 to
2197 // get a pointer to the first element, then send that into OpPtrAccessChain with index 1.
2198 "%b_loc_arr = OpPtrAccessChain %arr_v4f32_ptr %in_b %c_i32_0 %c_i32_0 %c_i32_1 %c_i32_1 %c_i32_1 "
2199 "%b_loc_first_elem = OpAccessChain %v4f32_ptr %b_loc_arr %c_i32_0 "
2200 "%b_loc = OpPtrAccessChain %sb_f32ptr %b_loc_first_elem %c_i32_1 %c_i32_3"};
2203 const string remainingIndexesAtLevel[]= {"%c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_1",
2204 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_0 %c_i32_2",
2205 "%c_i32_1 %c_i32_0 %c_i32_1 %c_i32_3",
2206 "%c_i32_1 %c_i32_0 %c_i32_0",
2207 "%c_i32_1 %c_i32_1",
2211 const string pointerTypeAtLevel[] = {"%outer_struct_ptr", "%mat2x2_ptr", "%arr2_ptr", "%inner_struct_ptr", "%arr_v4f32_ptr", "%v4f32_ptr", "%sb_f32ptr"};
2212 const string baseANameAtLevel[] = {"%in_a", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc", "%a_loc"};
2213 const string baseBNameAtLevel[] = {"%in_b", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc", "%b_loc"};
2215 for (int indexLevel = 0; indexLevel < numLevels; ++indexLevel)
2217 // Use OpSelect to choose between 2 pointers
2219 GraphicsResources resources;
2220 map<string, string> specs;
2221 string opCodeForTests = "opselect";
2222 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2223 specs["select_inputA"] = spirvSelectInputA;
2224 specs["helper_functions"] = "";
2225 specs["a_loc"] = inputALocations[indexLevel];
2226 specs["b_loc"] = inputBLocations[indexLevel];
2227 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2228 specs["selection_strategy"] = "%var_ptr = OpSelect " +
2229 pointerTypeAtLevel[indexLevel] + " " +
2230 spirvSelectInputA + " " +
2231 baseANameAtLevel[indexLevel] + " " +
2232 baseBNameAtLevel[indexLevel] + "\n";
2233 baseOffset = getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2234 indexesForLevel[indexLevel][1],
2235 indexesForLevel[indexLevel][2],
2236 indexesForLevel[indexLevel][3],
2237 indexesForLevel[indexLevel][4],
2238 indexesForLevel[indexLevel][5]);
2239 fragments["decoration"] = decoration.specialize(specs);
2240 fragments["pre_main"] = preMain.specialize(specs);
2241 fragments["testfun"] = testFunction.specialize(specs);
2242 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2243 getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2244 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2246 // Use OpCopyObject to get variable pointers
2248 GraphicsResources resources;
2249 map<string, string> specs;
2250 string opCodeForTests = "opcopyobject";
2251 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2252 specs["select_inputA"] = spirvSelectInputA;
2253 specs["helper_functions"] = "";
2254 specs["a_loc"] = inputALocations[indexLevel];
2255 specs["b_loc"] = inputBLocations[indexLevel];
2256 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2257 specs["selection_strategy"] =
2258 "%in_a_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseANameAtLevel[indexLevel] + "\n"
2259 "%in_b_copy = OpCopyObject " + pointerTypeAtLevel[indexLevel] + " " + baseBNameAtLevel[indexLevel] + "\n"
2260 "%var_ptr = OpSelect " + pointerTypeAtLevel[indexLevel] + " " + spirvSelectInputA + " %in_a_copy %in_b_copy\n";
2261 baseOffset = getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2262 indexesForLevel[indexLevel][1],
2263 indexesForLevel[indexLevel][2],
2264 indexesForLevel[indexLevel][3],
2265 indexesForLevel[indexLevel][4],
2266 indexesForLevel[indexLevel][5]);
2267 fragments["decoration"] = decoration.specialize(specs);
2268 fragments["pre_main"] = preMain.specialize(specs);
2269 fragments["testfun"] = testFunction.specialize(specs);
2270 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2271 getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2272 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2274 // Use OpPhi to choose between 2 pointers
2276 GraphicsResources resources;
2277 map<string, string> specs;
2278 string opCodeForTests = "opphi";
2279 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2280 specs["select_inputA"] = spirvSelectInputA;
2281 specs["helper_functions"] = "";
2282 specs["a_loc"] = inputALocations[indexLevel];
2283 specs["b_loc"] = inputBLocations[indexLevel];
2284 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2285 specs["selection_strategy"] =
2286 " OpSelectionMerge %end_label None\n"
2287 " OpBranchConditional " + spirvSelectInputA + " %take_input_a %take_input_b\n"
2288 "%take_input_a = OpLabel\n"
2289 " OpBranch %end_label\n"
2290 "%take_input_b = OpLabel\n"
2291 " OpBranch %end_label\n"
2292 "%end_label = OpLabel\n"
2294 + pointerTypeAtLevel[indexLevel] + " "
2295 + baseANameAtLevel[indexLevel]
2297 + baseBNameAtLevel[indexLevel]
2298 + " %take_input_b\n";
2299 baseOffset = getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2300 indexesForLevel[indexLevel][1],
2301 indexesForLevel[indexLevel][2],
2302 indexesForLevel[indexLevel][3],
2303 indexesForLevel[indexLevel][4],
2304 indexesForLevel[indexLevel][5]);
2305 fragments["decoration"] = decoration.specialize(specs);
2306 fragments["pre_main"] = preMain.specialize(specs);
2307 fragments["testfun"] = testFunction.specialize(specs);
2308 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2309 getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2310 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2312 // Use OpFunctionCall to choose between 2 pointers
2314 GraphicsResources resources;
2315 map<string, string> functionSpecs;
2316 map<string, string> specs;
2317 string opCodeForTests = "opfunctioncall";
2318 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2319 //string selectedType = "%mat2x2_ptr";
2320 functionSpecs["selected_type"] = pointerTypeAtLevel[indexLevel];
2321 specs["helper_functions"] = selectorFunctions.specialize(functionSpecs);
2322 specs["select_inputA"] = spirvSelectInputA;
2323 specs["a_loc"] = inputALocations[indexLevel];
2324 specs["b_loc"] = inputBLocations[indexLevel];
2325 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2326 specs["selection_strategy"] = "%var_ptr = OpFunctionCall "
2327 + pointerTypeAtLevel[indexLevel]
2328 + " %choose_input_func "
2329 + spirvSelectInputA + " "
2330 + baseANameAtLevel[indexLevel] + " "
2331 + baseBNameAtLevel[indexLevel] + "\n";
2332 baseOffset = getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2333 indexesForLevel[indexLevel][1],
2334 indexesForLevel[indexLevel][2],
2335 indexesForLevel[indexLevel][3],
2336 indexesForLevel[indexLevel][4],
2337 indexesForLevel[indexLevel][5]);
2338 fragments["decoration"] = decoration.specialize(specs);
2339 fragments["pre_main"] = preMain.specialize(specs);
2340 fragments["testfun"] = testFunction.specialize(specs);
2341 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2342 getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2343 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2345 // Use OpPtrAccessChain to get variable pointers
2347 GraphicsResources resources;
2348 map<string, string> specs;
2349 string opCodeForTests = "opptraccesschain";
2350 string name = opCodeForTests + indexLevelNames[indexLevel] + selectedInputStr;
2351 specs["select_inputA"] = spirvSelectInputA;
2352 specs["helper_functions"] = "";
2353 specs["a_loc"] = inputAPtrAccessChain[indexLevel];
2354 specs["b_loc"] = inputBPtrAccessChain[indexLevel];
2355 specs["remaining_indexes"] = remainingIndexesAtLevel[indexLevel];
2356 specs["selection_strategy"] = "%var_ptr = OpSelect "
2357 + pointerTypeAtLevel[indexLevel] + " "
2358 + spirvSelectInputA + " "
2359 + baseANameAtLevel[indexLevel] + " "
2360 + baseBNameAtLevel[indexLevel] + "\n";
2361 baseOffset = getBaseOffsetForSingleInputBuffer(indexesForLevel[indexLevel][0],
2362 indexesForLevel[indexLevel][1],
2363 indexesForLevel[indexLevel][2],
2364 indexesForLevel[indexLevel][3],
2365 indexesForLevel[indexLevel][4],
2366 indexesForLevel[indexLevel][5]);
2367 fragments["decoration"] = decoration.specialize(specs);
2368 fragments["pre_main"] = preMain.specialize(specs);
2369 fragments["testfun"] = testFunction.specialize(specs);
2370 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(inputBuffer)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2371 getExpectedOutputColor(defaultColors, expectedColors, inputBuffer[baseOffset]);
2372 createTestsForAllStages(name.c_str(), defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2378 void addNullptrVariablePointersGraphicsGroup (tcu::TestCaseGroup* testGroup)
2380 float someFloat = 78 / 255.f;
2381 vector<float> input (1, someFloat);
2382 vector<float> expectedOutput (1, someFloat);
2383 VulkanFeatures requiredFeatures;
2384 map<string, string> fragments;
2385 RGBA defaultColors[4];
2386 RGBA expectedColors[4];
2387 vector<string> extensions;
2389 getDefaultColors(defaultColors);
2390 getExpectedOutputColor(defaultColors, expectedColors, someFloat);
2392 // Set the required extension.
2393 extensions.push_back("VK_KHR_variable_pointers");
2395 // Requires the variable pointers feature.
2396 requiredFeatures.extVariablePointers = EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS;
2398 fragments["capability"] = "OpCapability VariablePointers \n";
2399 fragments["extension"] = "OpExtension \"SPV_KHR_variable_pointers\" \n"
2400 "OpExtension \"SPV_KHR_storage_buffer_storage_class\" \n";
2401 const StringTemplate decoration (
2403 "OpDecorate %input DescriptorSet 0 \n"
2404 "OpDecorate %input Binding 0 \n"
2406 // Set the Block decoration
2407 "OpDecorate %float_struct Block \n"
2410 "OpMemberDecorate %float_struct 0 Offset 0 \n"
2413 const StringTemplate preMain (
2414 // struct float_struct {
2417 "%float_struct = OpTypeStruct %f32 \n"
2420 "%float_struct_ptr = OpTypePointer StorageBuffer %float_struct \n"
2421 "%sb_f32ptr = OpTypePointer StorageBuffer %f32 \n"
2422 "%func_f32ptrptr = OpTypePointer Function %sb_f32ptr \n"
2425 "%c_bool_true = OpConstantTrue %bool \n"
2426 "%c_null_ptr = OpConstantNull %sb_f32ptr \n"
2429 "%input = OpVariable %float_struct_ptr StorageBuffer \n"
2432 const StringTemplate testFunction (
2433 "%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
2434 "%param = OpFunctionParameter %v4f32\n"
2435 "%entry = OpLabel\n"
2437 // Note that the Variable Pointers extension allows creation
2438 // of a pointer variable with storage class of Private or Function.
2439 "%f32_ptr_var = OpVariable %func_f32ptrptr Function %c_null_ptr\n"
2441 "%input_loc = OpAccessChain %sb_f32ptr %input %c_i32_0\n"
2443 // Null testing strategy
2444 "${NullptrTestingStrategy}\n"
2445 // Modify the 'RED channel' of the output color to the chosen value
2446 "%output_color = OpCompositeInsert %v4f32 %result_val %param 0 \n"
2447 // Return and FunctionEnd
2448 "OpReturnValue %output_color\n"
2451 // f32_ptr_var has been inintialized to NULL.
2452 // Now set it to the input variable and return it as output
2454 GraphicsResources resources;
2455 map<string, string> specs;
2456 specs["NullptrTestingStrategy"] =
2457 " OpStore %f32_ptr_var %input_loc \n"
2458 "%loaded_f32_ptr = OpLoad %sb_f32ptr %f32_ptr_var \n"
2459 "%result_val = OpLoad %f32 %loaded_f32_ptr \n";
2460 fragments["decoration"] = decoration.specialize(specs);
2461 fragments["pre_main"] = preMain.specialize(specs);
2462 fragments["testfun"] = testFunction.specialize(specs);
2463 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(input)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2464 createTestsForAllStages("opvariable_initialized_null", defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2466 // Use OpSelect to choose between nullptr and a valid pointer. Since we can't dereference nullptr,
2467 // it is forced to always choose the valid pointer.
2469 GraphicsResources resources;
2470 map<string, string> specs;
2471 specs["NullptrTestingStrategy"] = "%selected_ptr = OpSelect %sb_f32ptr %c_bool_true %input_loc %c_null_ptr\n"
2472 "%result_val = OpLoad %f32 %selected_ptr\n";
2473 fragments["decoration"] = decoration.specialize(specs);
2474 fragments["pre_main"] = preMain.specialize(specs);
2475 fragments["testfun"] = testFunction.specialize(specs);
2476 resources.inputs.push_back(Resource(BufferSp(new Float32Buffer(input)), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
2477 createTestsForAllStages("opselect_null_or_valid_ptr", defaultColors, expectedColors, fragments, resources, extensions, testGroup, requiredFeatures);
2483 tcu::TestCaseGroup* createVariablePointersComputeGroup (tcu::TestContext& testCtx)
2485 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "variable_pointers", "Compute tests for SPV_KHR_variable_pointers extension"));
2486 addTestGroup(group.get(), "compute", "Test the variable pointer extension using a compute shader", addVariablePointersComputeGroup);
2487 addTestGroup(group.get(),
2488 "complex_types_compute",
2489 "Testing Variable Pointers pointing to various types in different input buffers",
2490 addComplexTypesVariablePointersComputeGroup);
2491 addTestGroup(group.get(),
2493 "Test the usage of nullptr using the variable pointers extension in a compute shader",
2494 addNullptrVariablePointersComputeGroup);
2496 return group.release();
2499 tcu::TestCaseGroup* createVariablePointersGraphicsGroup (tcu::TestContext& testCtx)
2501 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "variable_pointers", "Graphics tests for SPV_KHR_variable_pointers extension"));
2503 addTestGroup(group.get(), "graphics", "Testing Variable Pointers in graphics pipeline", addVariablePointersGraphicsGroup);
2504 addTestGroup(group.get(),
2505 "multi_buffer_read_only_graphics",
2506 "Testing Variable Pointers pointing to different input buffers in graphics pipeline (no SSBO writes)",
2507 addTwoInputBufferReadOnlyVariablePointersGraphicsGroup);
2508 addTestGroup(group.get(),
2509 "single_buffer_read_only_graphics",
2510 "Testing Variable Pointers confined to a single input buffer in graphics pipeline (no SSBO writes)",
2511 addSingleInputBufferReadOnlyVariablePointersGraphicsGroup);
2512 addTestGroup(group.get(),
2514 "Test the usage of nullptr using the variable pointers extension in graphics pipeline",
2515 addNullptrVariablePointersGraphicsGroup);
2517 return group.release();