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