91066353211523254b3d184952c328381dd03671
[platform/upstream/VK-GL-CTS.git] / external / vulkancts / modules / vulkan / subgroups / vktSubgroupsBallotTests.cpp
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2017 The Khronos Group Inc.
6  * Copyright (c) 2017 Codeplay Software Ltd.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */ /*!
21  * \file
22  * \brief Subgroups Tests
23  */ /*--------------------------------------------------------------------*/
24
25 #include "vktSubgroupsBallotTests.hpp"
26 #include "vktSubgroupsTestsUtils.hpp"
27
28 #include <string>
29 #include <vector>
30
31 using namespace tcu;
32 using namespace std;
33 using namespace vk;
34 using namespace vkt;
35
36 namespace
37 {
38 static bool checkVertexPipelineStages(std::vector<const void*> datas,
39                                                                           deUint32 width, deUint32)
40 {
41         const deUint32* data =
42                 reinterpret_cast<const deUint32*>(datas[0]);
43         for (deUint32 x = 0; x < width; ++x)
44         {
45                 deUint32 val = data[x];
46
47                 if (0x7 != val)
48                 {
49                         return false;
50                 }
51         }
52
53         return true;
54 }
55
56 static bool checkFragment(std::vector<const void*> datas,
57                                                   deUint32 width, deUint32 height, deUint32)
58 {
59         const deUint32* data =
60                 reinterpret_cast<const deUint32*>(datas[0]);
61         for (deUint32 x = 0; x < width; ++x)
62         {
63                 for (deUint32 y = 0; y < height; ++y)
64                 {
65                         deUint32 val = data[x * height + y];
66
67                         if (0x7 != val)
68                         {
69                                 return false;
70                         }
71                 }
72         }
73
74         return true;
75 }
76
77 static bool checkCompute(std::vector<const void*> datas,
78                                                  const deUint32 numWorkgroups[3], const deUint32 localSize[3],
79                                                  deUint32)
80 {
81         const deUint32* data =
82                 reinterpret_cast<const deUint32*>(datas[0]);
83
84         for (deUint32 nX = 0; nX < numWorkgroups[0]; ++nX)
85         {
86                 for (deUint32 nY = 0; nY < numWorkgroups[1]; ++nY)
87                 {
88                         for (deUint32 nZ = 0; nZ < numWorkgroups[2]; ++nZ)
89                         {
90                                 for (deUint32 lX = 0; lX < localSize[0]; ++lX)
91                                 {
92                                         for (deUint32 lY = 0; lY < localSize[1]; ++lY)
93                                         {
94                                                 for (deUint32 lZ = 0; lZ < localSize[2];
95                                                                 ++lZ)
96                                                 {
97                                                         const deUint32 globalInvocationX =
98                                                                 nX * localSize[0] + lX;
99                                                         const deUint32 globalInvocationY =
100                                                                 nY * localSize[1] + lY;
101                                                         const deUint32 globalInvocationZ =
102                                                                 nZ * localSize[2] + lZ;
103
104                                                         const deUint32 globalSizeX =
105                                                                 numWorkgroups[0] * localSize[0];
106                                                         const deUint32 globalSizeY =
107                                                                 numWorkgroups[1] * localSize[1];
108
109                                                         const deUint32 offset =
110                                                                 globalSizeX *
111                                                                 ((globalSizeY *
112                                                                   globalInvocationZ) +
113                                                                  globalInvocationY) +
114                                                                 globalInvocationX;
115
116                                                         if (0x7 != data[offset])
117                                                         {
118                                                                 return false;
119                                                         }
120                                                 }
121                                         }
122                                 }
123                         }
124                 }
125         }
126
127         return true;
128 }
129
130 struct CaseDefinition
131 {
132         VkShaderStageFlags      shaderStage;
133         bool                            noSSBO;
134 };
135
136 void initFrameBufferPrograms(SourceCollections& programCollection, CaseDefinition caseDef)
137 {
138         std::ostringstream      fragmentSrc;
139
140         if (VK_SHADER_STAGE_VERTEX_BIT == caseDef.shaderStage)
141         {
142                 std::ostringstream      vertexSrc;
143
144                 vertexSrc << "#version 450\n"
145                         << "#extension GL_KHR_shader_subgroup_ballot: enable\n"
146                         << "layout(location = 0) in highp vec4 in_position;\n"
147                         << "layout(location = 0) out float out_color;\n"
148                         << "layout(set = 0, binding = 0) uniform Buffer1\n"
149                         << "{\n"
150                         << "  uint data[" << subgroups::maxSupportedSubgroupSize() << "];\n"
151                         << "};\n"
152                         << "\n"
153                         << "void main (void)\n"
154                         << "{\n"
155                         << "  uint tempResult = 0;\n"
156                         << "  tempResult |= !bool(uvec4(0) == subgroupBallot(true)) ? 0x1 : 0;\n"
157                         << "  bool bData = data[gl_SubgroupInvocationID] != 0;\n"
158                         << "  tempResult |= !bool(uvec4(0) == subgroupBallot(bData)) ? 0x2 : 0;\n"
159                         << "  tempResult |= uvec4(0) == subgroupBallot(false) ? 0x4 : 0;\n"
160                         << "  out_color = float(tempResult);\n"
161                         << "  gl_Position = in_position;\n"
162                         << "  gl_PointSize = 1.0f;\n"
163                         << "}\n";
164
165                 programCollection.glslSources.add("vert") << glu::VertexSource(vertexSrc.str()) << vk::ShaderBuildOptions(vk::SPIRV_VERSION_1_3, 0u);
166
167                 fragmentSrc << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450)<<"\n"
168                         << "layout(location = 0) in float in_color;\n"
169                         << "layout(location = 0) out uint out_color;\n"
170                         << "void main()\n"
171                         <<"{\n"
172                         << "    out_color = uint(in_color);\n"
173                         << "}\n";
174                 programCollection.glslSources.add("fragment") << glu::FragmentSource(fragmentSrc.str()) << vk::ShaderBuildOptions(vk::SPIRV_VERSION_1_3, 0u);
175         }
176         else
177         {
178                 DE_FATAL("Unsupported shader stage");
179         }
180 }
181
182 void initPrograms(SourceCollections& programCollection, CaseDefinition caseDef)
183 {
184         if (VK_SHADER_STAGE_COMPUTE_BIT == caseDef.shaderStage)
185         {
186                 std::ostringstream src;
187
188                 src << "#version 450\n"
189                         << "#extension GL_KHR_shader_subgroup_ballot: enable\n"
190                         << "layout (local_size_x_id = 0, local_size_y_id = 1, "
191                         "local_size_z_id = 2) in;\n"
192                         << "layout(set = 0, binding = 0, std430) buffer Buffer1\n"
193                         << "{\n"
194                         << "  uint result[];\n"
195                         << "};\n"
196                         << "layout(set = 0, binding = 1, std430) buffer Buffer2\n"
197                         << "{\n"
198                         << "  uint data[];\n"
199                         << "};\n"
200                         << "\n"
201                         << subgroups::getSharedMemoryBallotHelper()
202                         << "void main (void)\n"
203                         << "{\n"
204                         << "  uvec3 globalSize = gl_NumWorkGroups * gl_WorkGroupSize;\n"
205                         << "  highp uint offset = globalSize.x * ((globalSize.y * "
206                         "gl_GlobalInvocationID.z) + gl_GlobalInvocationID.y) + "
207                         "gl_GlobalInvocationID.x;\n"
208                         << "  uint tempResult = 0;\n"
209                         << "  tempResult |= sharedMemoryBallot(true) == subgroupBallot(true) ? 0x1 : 0;\n"
210                         << "  bool bData = data[gl_SubgroupInvocationID] != 0;\n"
211                         << "  tempResult |= sharedMemoryBallot(bData) == subgroupBallot(bData) ? 0x2 : 0;\n"
212                         << "  tempResult |= uvec4(0) == subgroupBallot(false) ? 0x4 : 0;\n"
213                         << "  result[offset] = tempResult;\n"
214                         << "}\n";
215
216                 programCollection.glslSources.add("comp")
217                                 << glu::ComputeSource(src.str()) << vk::ShaderBuildOptions(vk::SPIRV_VERSION_1_3, 0u);
218         }
219         else if (VK_SHADER_STAGE_FRAGMENT_BIT == caseDef.shaderStage)
220         {
221                 programCollection.glslSources.add("vert")
222                                 << glu::VertexSource(subgroups::getVertShaderForStage(caseDef.shaderStage)) << vk::ShaderBuildOptions(vk::SPIRV_VERSION_1_3, 0u);
223
224                 std::ostringstream frag;
225
226                 frag << "#version 450\n"
227                          << "#extension GL_KHR_shader_subgroup_ballot: enable\n"
228                          << "layout(location = 0) out uint result;\n"
229                          << "layout(set = 0, binding = 0, std430) readonly buffer Buffer1\n"
230                          << "{\n"
231                          << "  uint data[];\n"
232                          << "};\n"
233                          << "void main (void)\n"
234                          << "{\n"
235                          << "  uint tempResult = 0;\n"
236                          << "  tempResult |= !bool(uvec4(0) == subgroupBallot(true)) ? 0x1 : 0;\n"
237                          << "  bool bData = data[gl_SubgroupInvocationID] != 0;\n"
238                          << "  tempResult |= !bool(uvec4(0) == subgroupBallot(bData)) ? 0x2 : 0;\n"
239                          << "  tempResult |= uvec4(0) == subgroupBallot(false) ? 0x4 : 0;\n"
240                          << "  result = tempResult;\n"
241                          << "}\n";
242
243                 programCollection.glslSources.add("frag")
244                                 << glu::FragmentSource(frag.str()) << vk::ShaderBuildOptions(vk::SPIRV_VERSION_1_3, 0u);
245         }
246         else if (VK_SHADER_STAGE_VERTEX_BIT == caseDef.shaderStage)
247         {
248                 std::ostringstream src;
249
250                 src << "#version 450\n"
251                         << "#extension GL_KHR_shader_subgroup_ballot: enable\n"
252                         << "layout(set = 0, binding = 0, std430) buffer Buffer1\n"
253                         << "{\n"
254                         << "  uint result[];\n"
255                         << "};\n"
256                         << "layout(set = 0, binding = 1, std430) buffer Buffer2\n"
257                         << "{\n"
258                         << "  uint data[];\n"
259                         << "};\n"
260                         << "\n"
261                         << "void main (void)\n"
262                         << "{\n"
263                         << "  uint tempResult = 0;\n"
264                         << "  tempResult |= !bool(uvec4(0) == subgroupBallot(true)) ? 0x1 : 0;\n"
265                         << "  bool bData = data[gl_SubgroupInvocationID] != 0;\n"
266                         << "  tempResult |= !bool(uvec4(0) == subgroupBallot(bData)) ? 0x2 : 0;\n"
267                         << "  tempResult |= uvec4(0) == subgroupBallot(false) ? 0x4 : 0;\n"
268                         << "  result[gl_VertexIndex] = tempResult;\n"
269                         << "}\n";
270
271                 programCollection.glslSources.add("vert")
272                                 << glu::VertexSource(src.str()) << vk::ShaderBuildOptions(vk::SPIRV_VERSION_1_3, 0u);
273         }
274         else if (VK_SHADER_STAGE_GEOMETRY_BIT == caseDef.shaderStage)
275         {
276                 programCollection.glslSources.add("vert")
277                                 << glu::VertexSource(subgroups::getVertShaderForStage(caseDef.shaderStage)) << vk::ShaderBuildOptions(vk::SPIRV_VERSION_1_3, 0u);
278
279                 std::ostringstream src;
280
281                 src << "#version 450\n"
282                         << "#extension GL_KHR_shader_subgroup_ballot: enable\n"
283                         << "layout(points) in;\n"
284                         << "layout(points, max_vertices = 1) out;\n"
285                         << "layout(set = 0, binding = 0, std430) buffer Buffer1\n"
286                         << "{\n"
287                         << "  uint result[];\n"
288                         << "};\n"
289                         << "layout(set = 0, binding = 1, std430) buffer Buffer2\n"
290                         << "{\n"
291                         << "  uint data[];\n"
292                         << "};\n"
293                         << "\n"
294                         << "void main (void)\n"
295                         << "{\n"
296                         << "  uint tempResult = 0;\n"
297                         << "  tempResult |= !bool(uvec4(0) == subgroupBallot(true)) ? 0x1 : 0;\n"
298                         << "  bool bData = data[gl_SubgroupInvocationID] != 0;\n"
299                         << "  tempResult |= !bool(uvec4(0) == subgroupBallot(bData)) ? 0x2 : 0;\n"
300                         << "  tempResult |= uvec4(0) == subgroupBallot(false) ? 0x4 : 0;\n"
301                         << "  result[gl_PrimitiveIDIn] = tempResult;\n"
302                         << "}\n";
303
304                 programCollection.glslSources.add("geom")
305                                 << glu::GeometrySource(src.str()) << vk::ShaderBuildOptions(vk::SPIRV_VERSION_1_3, 0u);
306         }
307         else if (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT == caseDef.shaderStage)
308         {
309                 programCollection.glslSources.add("vert")
310                                 << glu::VertexSource(subgroups::getVertShaderForStage(caseDef.shaderStage)) << vk::ShaderBuildOptions(vk::SPIRV_VERSION_1_3, 0u);
311
312                 programCollection.glslSources.add("tese")
313                                 << glu::TessellationEvaluationSource("#version 450\nlayout(isolines) in;\nvoid main (void) {}\n");
314
315                 std::ostringstream src;
316
317                 src << "#version 450\n"
318                         << "#extension GL_KHR_shader_subgroup_ballot: enable\n"
319                         << "layout(vertices=1) out;\n"
320                         << "layout(set = 0, binding = 0, std430) buffer Buffer1\n"
321                         << "{\n"
322                         << "  uint result[];\n"
323                         << "};\n"
324                         << "layout(set = 0, binding = 1, std430) buffer Buffer2\n"
325                         << "{\n"
326                         << "  uint data[];\n"
327                         << "};\n"
328                         << "\n"
329                         << "void main (void)\n"
330                         << "{\n"
331                         << "  uint tempResult = 0;\n"
332                         << "  tempResult |= !bool(uvec4(0) == subgroupBallot(true)) ? 0x1 : 0;\n"
333                         << "  bool bData = data[gl_SubgroupInvocationID] != 0;\n"
334                         << "  tempResult |= !bool(uvec4(0) == subgroupBallot(bData)) ? 0x2 : 0;\n"
335                         << "  tempResult |= uvec4(0) == subgroupBallot(false) ? 0x4 : 0;\n"
336                         << "  result[gl_PrimitiveID] = tempResult;\n"
337                         << "}\n";
338
339                 programCollection.glslSources.add("tesc")
340                                 << glu::TessellationControlSource(src.str()) << vk::ShaderBuildOptions(vk::SPIRV_VERSION_1_3, 0u);
341         }
342         else if (VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT == caseDef.shaderStage)
343         {
344                 programCollection.glslSources.add("vert")
345                                 << glu::VertexSource(subgroups::getVertShaderForStage(caseDef.shaderStage)) << vk::ShaderBuildOptions(vk::SPIRV_VERSION_1_3, 0u);
346
347                 programCollection.glslSources.add("tesc")
348                                 << glu::TessellationControlSource("#version 450\nlayout(vertices=1) out;\nvoid main (void) { for(uint i = 0; i < 4; i++) { gl_TessLevelOuter[i] = 1.0f; } }\n");
349
350                 std::ostringstream src;
351
352                 src << "#version 450\n"
353                         << "#extension GL_KHR_shader_subgroup_ballot: enable\n"
354                         << "layout(isolines) in;\n"
355                         << "layout(set = 0, binding = 0, std430) buffer Buffer1\n"
356                         << "{\n"
357                         << "  uint result[];\n"
358                         << "};\n"
359                         << "layout(set = 0, binding = 1, std430) buffer Buffer2\n"
360                         << "{\n"
361                         << "  uint data[];\n"
362                         << "};\n"
363                         << "\n"
364                         << "void main (void)\n"
365                         << "{\n"
366                         << "  uint tempResult = 0;\n"
367                         << "  tempResult |= !bool(uvec4(0) == subgroupBallot(true)) ? 0x1 : 0;\n"
368                         << "  bool bData = data[gl_SubgroupInvocationID] != 0;\n"
369                         << "  tempResult |= !bool(uvec4(0) == subgroupBallot(bData)) ? 0x2 : 0;\n"
370                         << "  tempResult |= uvec4(0) == subgroupBallot(false) ? 0x4 : 0;\n"
371                         << "  result[gl_PrimitiveID * 2 + uint(gl_TessCoord.x + 0.5)] = tempResult;\n"
372                         << "}\n";
373
374                 programCollection.glslSources.add("tese")
375                                 << glu::TessellationEvaluationSource(src.str()) << vk::ShaderBuildOptions(vk::SPIRV_VERSION_1_3, 0u);
376         }
377         else
378         {
379                 DE_FATAL("Unsupported shader stage");
380         }
381 }
382
383 tcu::TestStatus test(Context& context, const CaseDefinition caseDef)
384 {
385         if (!subgroups::isSubgroupSupported(context))
386                 TCU_THROW(NotSupportedError, "Subgroup operations are not supported");
387
388         if (!subgroups::areSubgroupOperationsSupportedForStage(
389                                 context, caseDef.shaderStage))
390         {
391                 if (subgroups::areSubgroupOperationsRequiredForStage(
392                                         caseDef.shaderStage))
393                 {
394                         return tcu::TestStatus::fail(
395                                            "Shader stage " +
396                                            subgroups::getShaderStageName(caseDef.shaderStage) +
397                                            " is required to support subgroup operations!");
398                 }
399                 else
400                 {
401                         TCU_THROW(NotSupportedError, "Device does not support subgroup operations for this stage");
402                 }
403         }
404
405         if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_BALLOT_BIT))
406         {
407                 TCU_THROW(NotSupportedError, "Device does not support subgroup ballot operations");
408         }
409
410         //Tests which don't use the SSBO
411         if (caseDef.noSSBO && VK_SHADER_STAGE_VERTEX_BIT == caseDef.shaderStage)
412         {
413                 subgroups::SSBOData inputData[1];
414                 inputData[0].format = VK_FORMAT_R32_UINT;
415                 inputData[0].numElements = subgroups::maxSupportedSubgroupSize();
416                 inputData[0].initializeType = subgroups::SSBOData::InitializeNonZero;
417
418                 return subgroups::makeVertexFrameBufferTest(context, VK_FORMAT_R32_UINT, inputData, 1, checkVertexPipelineStages);
419         }
420
421         if ((VK_SHADER_STAGE_FRAGMENT_BIT != caseDef.shaderStage) &&
422                         (VK_SHADER_STAGE_COMPUTE_BIT != caseDef.shaderStage))
423         {
424                 if (!subgroups::isVertexSSBOSupportedForDevice(context))
425                 {
426                         TCU_THROW(NotSupportedError, "Device does not support vertex stage SSBO writes");
427                 }
428         }
429
430         if (VK_SHADER_STAGE_FRAGMENT_BIT == caseDef.shaderStage)
431         {
432                 subgroups::SSBOData inputData[1];
433                 inputData[0].format = VK_FORMAT_R32_UINT;
434                 inputData[0].numElements = subgroups::maxSupportedSubgroupSize();
435                 inputData[0].initializeType = subgroups::SSBOData::InitializeNonZero;
436
437                 return subgroups::makeFragmentTest(context, VK_FORMAT_R32_UINT,
438                                                                                    inputData, 1, checkFragment);
439         }
440         else if (VK_SHADER_STAGE_COMPUTE_BIT == caseDef.shaderStage)
441         {
442                 subgroups::SSBOData inputData[1];
443                 inputData[0].format = VK_FORMAT_R32_UINT;
444                 inputData[0].numElements = subgroups::maxSupportedSubgroupSize();
445                 inputData[0].initializeType = subgroups::SSBOData::InitializeNonZero;
446
447                 return subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT,
448                                                                                   inputData, 1, checkCompute);
449         }
450         else if (VK_SHADER_STAGE_VERTEX_BIT == caseDef.shaderStage)
451         {
452                 subgroups::SSBOData inputData[1];
453                 inputData[0].format = VK_FORMAT_R32_UINT;
454                 inputData[0].numElements = subgroups::maxSupportedSubgroupSize();
455                 inputData[0].initializeType = subgroups::SSBOData::InitializeNonZero;
456
457                 return subgroups::makeVertexTest(context, VK_FORMAT_R32_UINT,
458                                                                                  inputData, 1, checkVertexPipelineStages);
459         }
460         else if (VK_SHADER_STAGE_GEOMETRY_BIT == caseDef.shaderStage)
461         {
462                 subgroups::SSBOData inputData[1];
463                 inputData[0].format = VK_FORMAT_R32_UINT;
464                 inputData[0].numElements = subgroups::maxSupportedSubgroupSize();
465                 inputData[0].initializeType = subgroups::SSBOData::InitializeNonZero;
466
467                 return subgroups::makeGeometryTest(context, VK_FORMAT_R32_UINT,
468                                                                                    inputData, 1, checkVertexPipelineStages);
469         }
470         else if (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT == caseDef.shaderStage)
471         {
472                 subgroups::SSBOData inputData[1];
473                 inputData[0].format = VK_FORMAT_R32_UINT;
474                 inputData[0].numElements = subgroups::maxSupportedSubgroupSize();
475                 inputData[0].initializeType = subgroups::SSBOData::InitializeNonZero;
476
477                 return subgroups::makeTessellationControlTest(context, VK_FORMAT_R32_UINT,
478                                 inputData, 1, checkVertexPipelineStages);
479         }
480         else if (VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT == caseDef.shaderStage)
481         {
482                 subgroups::SSBOData inputData[1];
483                 inputData[0].format = VK_FORMAT_R32_UINT;
484                 inputData[0].numElements = subgroups::maxSupportedSubgroupSize();
485                 inputData[0].initializeType = subgroups::SSBOData::InitializeNonZero;
486
487                 return subgroups::makeTessellationEvaluationTest(context, VK_FORMAT_R32_UINT,
488                                 inputData, 1, checkVertexPipelineStages);
489         }
490         else
491         {
492                 TCU_THROW(InternalError, "Unhandled shader stage");
493         }
494 }
495 }
496
497 namespace vkt
498 {
499 namespace subgroups
500 {
501 tcu::TestCaseGroup* createSubgroupsBallotTests(tcu::TestContext& testCtx)
502 {
503         de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(
504                         testCtx, "ballot", "Subgroup ballot category tests"));
505
506         const VkShaderStageFlags stages[] =
507         {
508                 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
509                 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
510                 VK_SHADER_STAGE_GEOMETRY_BIT,
511                 VK_SHADER_STAGE_VERTEX_BIT,
512                 VK_SHADER_STAGE_FRAGMENT_BIT,
513                 VK_SHADER_STAGE_COMPUTE_BIT
514         };
515
516         for (int stageIndex = 0; stageIndex < DE_LENGTH_OF_ARRAY(stages); ++stageIndex)
517         {
518                 const VkShaderStageFlags stage = stages[stageIndex];
519
520                 CaseDefinition caseDef = {stage, false};
521
522                 std::ostringstream name;
523
524                 name << getShaderStageName(stage);
525
526                 addFunctionCaseWithPrograms(group.get(), name.str(),
527                                                                         "", initPrograms, test, caseDef);
528
529                 if (VK_SHADER_STAGE_VERTEX_BIT == stage )
530                 {
531                         caseDef.noSSBO = true;
532                         addFunctionCaseWithPrograms(group.get(), name.str()+"_framebuffer", "",
533                                                 initFrameBufferPrograms, test, caseDef);
534                 }
535         }
536
537         return group.release();
538 }
539
540 } // subgroups
541 } // vkt