1 /*-------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2023 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Test multiple entry points.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktSpvAsmMultipleShadersTests.hpp"
25 #include "vktTestCaseUtil.hpp"
26 #include "vkPrograms.hpp"
27 #include "vkObjUtil.hpp"
28 #include "vkRefUtil.hpp"
29 #include "vkTypeUtil.hpp"
30 #include "vkQueryUtil.hpp"
31 #include "vkBuilderUtil.hpp"
32 #include "vkBarrierUtil.hpp"
33 #include "vkBufferWithMemory.hpp"
34 #include "vkCmdUtil.hpp"
35 
36 namespace vkt
37 {
38 namespace SpirVAssembly
39 {
40 
41 namespace
42 {
43 
44 using namespace vk;
45 
46 using BufferWithMemorySp = de::SharedPtr<BufferWithMemory>;
47 
48 enum class TestType
49 {
50     // two entry points where each OpEntryPoint has associated OpExectionModeId
51     TWO_ENTRY_POINTS_EXECUTION_MODE_ID = 0,
52 
53     // two entry points where each has different interfaces
54     TWO_ENTRY_POINTS_DIFFERENT_INTERFACES,
55 };
56 
57 struct TestConfig
58 {
59     TestType type;
60 };
61 
62 class EntryPointsTest : public TestInstance
63 {
64 public:
65     EntryPointsTest(Context &context, TestConfig config);
66     virtual ~EntryPointsTest(void) = default;
67 
68     tcu::TestStatus iterate(void);
69 
70 private:
71     TestConfig m_config;
72 };
73 
EntryPointsTest(Context & context,TestConfig config)74 EntryPointsTest::EntryPointsTest(Context &context, TestConfig config) : TestInstance(context), m_config(config)
75 {
76 }
77 
iterate(void)78 tcu::TestStatus EntryPointsTest::iterate(void)
79 {
80     const DeviceInterface &vk       = m_context.getDeviceInterface();
81     const VkDevice device           = m_context.getDevice();
82     const VkQueue queue             = m_context.getUniversalQueue();
83     const uint32_t queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
84     Allocator &memAlloc             = m_context.getDefaultAllocator();
85 
86     // Create test buffers
87     const uint32_t bufferItems    = 24u;
88     const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(bufferItems * sizeof(uint32_t));
89     const VkBufferUsageFlags bufferusage =
90         VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
91     const bool useTwoBuffers            = (m_config.type == TestType::TWO_ENTRY_POINTS_DIFFERENT_INTERFACES);
92     VkBufferCreateInfo bufferCreateInfo = makeBufferCreateInfo(bufferSize, bufferusage);
93     BufferWithMemorySp bufferA          = BufferWithMemorySp(
94         new BufferWithMemory(vk, device, memAlloc, bufferCreateInfo, MemoryRequirement::HostVisible));
95     ;
96     BufferWithMemorySp bufferB = BufferWithMemorySp(
97         new BufferWithMemory(vk, device, memAlloc, bufferCreateInfo, MemoryRequirement::HostVisible));
98     ;
99 
100     // Write data to test buffers
101     int dataASrc[bufferItems];
102     int dataBSrc[bufferItems];
103     for (int i = 0; i < DE_LENGTH_OF_ARRAY(dataASrc); ++i)
104     {
105         dataASrc[i] = deAbs32(9 * deAbs32(int(i / 6) - 1) - (i % 6)) + (i == 6);
106         dataBSrc[i] = 1 + i * 2;
107     }
108     auto fillBuffer = [&](BufferWithMemorySp buffer, int *dataSrc)
109     {
110         Allocation &allocation = buffer->getAllocation();
111         int *bufferPtr         = static_cast<int *>(allocation.getHostPtr());
112         deMemcpy(bufferPtr, dataSrc, bufferSize);
113         flushAlloc(vk, device, allocation);
114     };
115     fillBuffer(bufferA, dataASrc);
116     fillBuffer(bufferB, dataBSrc);
117 
118     // Create descriptor set
119     const VkDescriptorType descType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
120     DescriptorSetLayoutBuilder dsLayoutBuilder;
121     dsLayoutBuilder.addSingleBinding(descType, VK_SHADER_STAGE_COMPUTE_BIT);
122     if (useTwoBuffers)
123         dsLayoutBuilder.addSingleBinding(descType, VK_SHADER_STAGE_COMPUTE_BIT);
124     const Unique<VkDescriptorSetLayout> descriptorSetLayout(dsLayoutBuilder.build(vk, device));
125 
126     const Unique<VkDescriptorPool> descriptorPool(
127         DescriptorPoolBuilder()
128             .addType(descType, 1 + useTwoBuffers)
129             .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
130 
131     const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
132 
133     const VkDescriptorBufferInfo bufferADescriptorInfo = makeDescriptorBufferInfo(**bufferA, 0ull, bufferSize);
134     const VkDescriptorBufferInfo bufferBDescriptorInfo = makeDescriptorBufferInfo(**bufferB, 0ull, bufferSize);
135     DescriptorSetUpdateBuilder descriptorSetUpdateBuilder;
136     descriptorSetUpdateBuilder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), descType,
137                                            &bufferADescriptorInfo);
138     if (useTwoBuffers)
139         descriptorSetUpdateBuilder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u),
140                                                descType, &bufferBDescriptorInfo);
141     descriptorSetUpdateBuilder.update(vk, device);
142 
143     // Perform the computation
144     const Unique<VkShaderModule> shaderModule(
145         createShaderModule(vk, device, m_context.getBinaryCollection().get("comp"), 0u));
146     const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
147 
148     const VkPipelineShaderStageCreateInfo pipelineAShaderStageParams{
149         VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
150         DE_NULL,
151         static_cast<VkPipelineShaderStageCreateFlags>(0u),
152         VK_SHADER_STAGE_COMPUTE_BIT,
153         *shaderModule,
154         "mainA",
155         DE_NULL,
156     };
157     const VkPipelineShaderStageCreateInfo pipelineBShaderStageParams{
158         VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
159         DE_NULL,
160         static_cast<VkPipelineShaderStageCreateFlags>(0u),
161         VK_SHADER_STAGE_COMPUTE_BIT,
162         *shaderModule,
163         "mainB",
164         DE_NULL,
165     };
166     VkComputePipelineCreateInfo pipelineCreateInfo{
167         VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
168         DE_NULL,
169         static_cast<VkPipelineCreateFlags>(0u),
170         pipelineBShaderStageParams,
171         *pipelineLayout,
172         DE_NULL,
173         0,
174     };
175 
176     Unique<VkPipeline> pipelineB(createComputePipeline(vk, device, DE_NULL, &pipelineCreateInfo));
177     pipelineCreateInfo.stage = pipelineAShaderStageParams;
178     Unique<VkPipeline> pipelineA(createComputePipeline(vk, device, DE_NULL, &pipelineCreateInfo));
179 
180     const VkMemoryBarrier hostWriteBarrier(makeMemoryBarrier(VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT));
181     const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
182     const Unique<VkCommandBuffer> cmdBuffer(
183         allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
184 
185     // Start recording commands
186     beginCommandBuffer(vk, *cmdBuffer);
187 
188     vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1,
189                           &hostWriteBarrier, 0, 0, 0, 0);
190 
191     vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineB);
192     vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u,
193                              0);
194     vk.cmdDispatch(*cmdBuffer, 1, 1, 1);
195 
196     vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineA);
197     vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u,
198                              0);
199     vk.cmdDispatch(*cmdBuffer, 1, 1, 1);
200 
201     endCommandBuffer(vk, *cmdBuffer);
202 
203     // Wait for completion
204     submitCommandsAndWait(vk, device, queue, *cmdBuffer);
205 
206     invalidateAlloc(vk, device, bufferA->getAllocation());
207 
208     // Validate the results
209     if (m_config.type == TestType::TWO_ENTRY_POINTS_EXECUTION_MODE_ID)
210     {
211         int *bufferPtr = static_cast<int *>(bufferA->getAllocation().getHostPtr());
212         for (int i = 0; i < 6; i++)
213         {
214             if ((bufferPtr[12 + i] != (dataASrc[i] - dataASrc[6 + i])) ||
215                 (bufferPtr[18 + i] != (dataASrc[i] * dataASrc[6 + i])))
216                 return tcu::TestStatus::fail("Fail");
217         }
218     }
219     else if (m_config.type == TestType::TWO_ENTRY_POINTS_DIFFERENT_INTERFACES)
220     {
221         invalidateAlloc(vk, device, bufferB->getAllocation());
222         int *bufferAPtr = static_cast<int *>(bufferA->getAllocation().getHostPtr());
223         int *bufferBPtr = static_cast<int *>(bufferB->getAllocation().getHostPtr());
224         for (int i = 0; i < 6; i++)
225         {
226             if ((bufferAPtr[12 + i] != (dataASrc[i] + dataASrc[6 + i])) ||
227                 (bufferBPtr[12 + i] != (dataBSrc[5 - i] * dataBSrc[11 - i])))
228                 return tcu::TestStatus::fail("Fail");
229         }
230     }
231 
232     return tcu::TestStatus::pass("Pass");
233 }
234 
235 struct Programs
236 {
initvkt::SpirVAssembly::__anon0346374f0111::Programs237     void init(vk::SourceCollections &dst, TestConfig config) const
238     {
239         const SpirVAsmBuildOptions buildOptionsSpr(dst.usedVulkanVersion, SPIRV_VERSION_1_5, false, true);
240         std::string compSrc = "OpCapability Shader\n"
241                               "%1 = OpExtInstImport \"GLSL.std.450\"\n"
242                               "OpMemoryModel Logical GLSL450\n";
243 
244         if (config.type == TestType::TWO_ENTRY_POINTS_EXECUTION_MODE_ID)
245         {
246             // #version 450
247             // layout(local_size_x = 2, local_size_y = 3) in;
248             // layout(binding = 0, std430) buffer InOut { int v[]; } inOut;
249             // void mainA()
250             // {
251             //   uint id = gl_LocalInvocationIndex;
252             //   inOut.v[12+id] = inOut.v[id] - inOut.v[6+id];
253             // }
254             // void mainB()
255             // {
256             //   uint id = gl_LocalInvocationIndex;
257             //   inOut.v[18+id] = inOut.v[id] * inOut.v[6+id];
258             // }
259 
260             compSrc += "OpEntryPoint GLCompute %mainA \"mainA\" %inOutVar %gl_LocalInvocationIndex\n"
261                        "OpEntryPoint GLCompute %mainB \"mainB\" %inOutVar %gl_LocalInvocationIndex\n"
262                        "OpExecutionModeId %mainA LocalSizeId %uint_2 %uint_3 %uint_1\n"
263                        "OpExecutionModeId %mainB LocalSizeId %uint_2 %uint_3 %uint_1\n"
264 
265                        "OpDecorate %runtimearr_int ArrayStride 4\n"
266                        "OpMemberDecorate %InOut 0 Offset 0\n"
267                        "OpDecorate %InOut Block\n"
268                        "OpDecorate %inOutVar DescriptorSet 0\n"
269                        "OpDecorate %inOutVar Binding 0\n"
270                        "OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex\n"
271                        "OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize\n"
272 
273                        "%void = OpTypeVoid\n"
274                        "%int = OpTypeInt 32 1\n"
275                        "%uint = OpTypeInt 32 0\n"
276                        "%v3uint = OpTypeVector %uint 3\n"
277                        "%void_fun = OpTypeFunction %void\n"
278                        "%uint_fun = OpTypeFunction %uint\n"
279                        "%runtimearr_int = OpTypeRuntimeArray %int\n"
280                        "%InOut = OpTypeStruct %runtimearr_int\n"
281                        "%ptr_Uniform_InOut = OpTypePointer StorageBuffer %InOut\n"
282                        "%ptr_Uniform_int = OpTypePointer StorageBuffer %int\n"
283                        "%ptr_uint_fun = OpTypePointer Function %uint\n"
284                        "%ptr_v3uint_input = OpTypePointer Input %v3uint\n"
285                        "%ptr_uint_input = OpTypePointer Input %uint\n"
286 
287                        "%int_0 = OpConstant %int 0\n"
288                        "%uint_1 = OpConstant %uint 1\n"
289                        "%uint_2 = OpConstant %uint 2\n"
290                        "%uint_3 = OpConstant %uint 3\n"
291                        "%uint_6 = OpConstant %uint 6\n"
292                        "%uint_12 = OpConstant %uint 12\n"
293                        "%uint_18 = OpConstant %uint 18\n"
294 
295                        "%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_2 %uint_3 %uint_1\n"
296                        "%gl_LocalInvocationIndex = OpVariable %ptr_uint_input Input\n"
297                        "%inOutVar = OpVariable %ptr_Uniform_InOut StorageBuffer\n"
298 
299                        "%mainA = OpFunction %void None %void_fun\n"
300                        "%labelA = OpLabel\n"
301                        "%idxA = OpLoad %uint %gl_LocalInvocationIndex\n"
302                        "%30 = OpIAdd %uint %uint_12 %idxA\n"
303                        "%33 = OpAccessChain %ptr_Uniform_int %inOutVar %int_0 %idxA\n"
304                        "%34 = OpLoad %int %33\n"
305                        "%37 = OpIAdd %uint %uint_6 %idxA\n"
306                        "%38 = OpAccessChain %ptr_Uniform_int %inOutVar %int_0 %37\n"
307                        "%39 = OpLoad %int %38\n"
308                        "%40 = OpISub %int %34 %39\n"
309                        "%41 = OpAccessChain %ptr_Uniform_int %inOutVar %int_0 %30\n"
310                        "OpStore %41 %40\n"
311                        "OpReturn\n"
312                        "OpFunctionEnd\n"
313 
314                        "%mainB = OpFunction %void None %void_fun\n"
315                        "%labelB = OpLabel\n"
316                        "%idxB = OpLoad %uint %gl_LocalInvocationIndex\n"
317                        "%60 = OpIAdd %uint %uint_18 %idxB\n"
318                        "%63 = OpAccessChain %ptr_Uniform_int %inOutVar %int_0 %idxB\n"
319                        "%64 = OpLoad %int %63\n"
320                        "%67 = OpIAdd %uint %uint_6 %idxB\n"
321                        "%68 = OpAccessChain %ptr_Uniform_int %inOutVar %int_0 %67\n"
322                        "%69 = OpLoad %int %68\n"
323                        "%70 = OpIMul %int %64 %69\n"
324                        "%71 = OpAccessChain %ptr_Uniform_int %inOutVar %int_0 %60\n"
325                        "OpStore %71 %70\n"
326                        "OpReturn\n"
327                        "OpFunctionEnd\n";
328 
329             dst.spirvAsmSources.add("comp") << compSrc << buildOptionsSpr;
330         }
331         else if (config.type == TestType::TWO_ENTRY_POINTS_DIFFERENT_INTERFACES)
332         {
333             // #version 450
334             // layout(local_size_x = 3, local_size_y = 2) in;
335             // layout(binding = 0, std430) buffer BufferA { int v[]; } bufferA;
336             // layout(binding = 1, std430) buffer BufferB { int v[]; } bufferB;
337             // void mainA()
338             // {
339             //   uint idx = gl_LocalInvocationIndex;
340             //   bufferA.v[12+idx] = bufferA.v[idx] - bufferA.v[6+idx];
341             // }
342             // void mainB()
343             // {
344             //   uint idxOut = 2 * gl_LocalInvocationID.x + gl_LocalInvocationID.y;
345             //   uint idxIn  = 6 - gl_NumWorkGroups.x - idxOut;
346             //   bufferB.v[12+idxOut] = bufferB.v[idxIn] * bufferB.v[6+idxIn];
347             // }
348 
349             compSrc += "OpEntryPoint GLCompute %mainA \"mainA\" %gl_LocalInvocationIndex\n"
350                        "OpEntryPoint GLCompute %mainB \"mainB\" %gl_NumWorkGroups %gl_LocalInvocationId\n"
351                        "OpExecutionMode %mainA LocalSize 3 2 1\n"
352                        "OpExecutionMode %mainB LocalSize 3 2 1\n"
353                        "OpDecorate %gl_NumWorkGroups BuiltIn NumWorkgroups\n"
354                        "OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex\n"
355                        "OpDecorate %gl_LocalInvocationId BuiltIn LocalInvocationId\n"
356                        "OpDecorate %int_runtime_array ArrayStride 4\n"
357                        "OpMemberDecorate %struct_type 0 Offset 0\n"
358                        "OpDecorate %struct_type BufferBlock\n"
359                        "OpDecorate %var_BufferA DescriptorSet 0\n"
360                        "OpDecorate %var_BufferA Binding 0\n"
361                        "OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize\n"
362                        "OpDecorate %var_BufferB DescriptorSet 0\n"
363                        "OpDecorate %var_BufferB Binding 1\n"
364 
365                        "%void = OpTypeVoid\n"
366                        "%void_fun = OpTypeFunction %void\n"
367                        "%uint = OpTypeInt 32 0\n"
368                        "%int = OpTypeInt 32 1\n"
369                        "%ptr_uint_fun = OpTypePointer Function %uint\n"
370                        "%v3uint = OpTypeVector %uint 3\n"
371                        "%ptr_uint_input = OpTypePointer Input %uint\n"
372                        "%ptr_v3uint_input = OpTypePointer Input %v3uint\n"
373                        "%int_runtime_array = OpTypeRuntimeArray %int\n"
374                        "%struct_type = OpTypeStruct %int_runtime_array\n"
375                        "%25 = OpTypePointer Uniform %struct_type\n"
376                        "%ptr_uniform_int = OpTypePointer Uniform %int\n"
377 
378                        "%int_0 = OpConstant %int 0\n"
379                        "%uint_0 = OpConstant %uint 0\n"
380                        "%uint_1 = OpConstant %uint 1\n"
381                        "%uint_2 = OpConstant %uint 2\n"
382                        "%uint_3 = OpConstant %uint 3\n"
383                        "%uint_6 = OpConstant %uint 6\n"
384                        "%uint_12 = OpConstant %uint 12\n"
385                        "%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_3 %uint_2 %uint_1\n"
386 
387                        "%gl_LocalInvocationIndex = OpVariable %ptr_uint_input Input\n"
388                        "%gl_NumWorkGroups = OpVariable %ptr_v3uint_input Input\n"
389                        "%gl_LocalInvocationId = OpVariable %ptr_v3uint_input Input\n"
390                        "%var_BufferA = OpVariable %25 Uniform\n"
391                        "%var_BufferB = OpVariable %25 Uniform\n"
392 
393                        "%mainA = OpFunction %void None %void_fun\n"
394                        "%labelA = OpLabel\n"
395                        "%idxA = OpLoad %uint %gl_LocalInvocationIndex\n"
396                        "%inA1_location = OpAccessChain %ptr_uniform_int %var_BufferA %int_0 %idxA\n"
397                        "%inA1 = OpLoad %int %inA1_location\n"
398                        "%inA2_index = OpIAdd %uint %uint_6 %idxA\n"
399                        "%inA2_location = OpAccessChain %ptr_uniform_int %var_BufferA %int_0 %inA2_index\n"
400                        "%inA2 = OpLoad %int %inA2_location\n"
401                        "%outA_index = OpIAdd %uint %uint_12 %idxA\n"
402                        "%add_result = OpIAdd %int %inA1 %inA2\n"
403                        "%outA_location = OpAccessChain %ptr_uniform_int %var_BufferA %int_0 %outA_index\n"
404                        "OpStore %outA_location %add_result\n"
405                        "OpReturn\n"
406                        "OpFunctionEnd\n"
407 
408                        "%mainB = OpFunction %void None %void_fun\n"
409                        "%labelB = OpLabel\n"
410 
411                        "%local_x_location = OpAccessChain %ptr_uint_input %gl_LocalInvocationId %uint_0\n"
412                        "%local_x = OpLoad %uint %local_x_location\n"
413                        "%local_x_times_2 = OpIMul %uint %local_x %uint_2\n"
414                        "%local_y_location = OpAccessChain %ptr_uint_input %gl_LocalInvocationId %uint_1\n"
415                        "%local_y = OpLoad %uint %local_y_location\n"
416                        "%idxOut = OpIAdd %int %local_x_times_2 %local_y\n"
417 
418                        "%group_count_location = OpAccessChain %ptr_uint_input %gl_NumWorkGroups %uint_0\n"
419                        "%group_count = OpLoad %uint %group_count_location\n"
420                        "%sub_result = OpISub %int %uint_6 %group_count\n"
421                        "%idxIn = OpISub %int %sub_result %idxOut\n"
422 
423                        "%inB1_location = OpAccessChain %ptr_uniform_int %var_BufferB %int_0 %idxIn\n"
424                        "%inB1 = OpLoad %int %inB1_location\n"
425                        "%inB2_index = OpIAdd %uint %uint_6 %idxIn\n"
426                        "%inB2_location = OpAccessChain %ptr_uniform_int %var_BufferB %int_0 %inB2_index\n"
427                        "%inB2 = OpLoad %int %inB2_location\n"
428                        "%outB_index = OpIAdd %uint %uint_12 %idxOut\n"
429                        "%mul_result = OpIMul %int %inB1 %inB2\n"
430                        "%outB_location = OpAccessChain %ptr_uniform_int %var_BufferB %int_0 %outB_index\n"
431                        "OpStore %outB_location %mul_result\n"
432 
433                        "OpReturn\n"
434                        "OpFunctionEnd\n";
435 
436             dst.spirvAsmSources.add("comp") << compSrc;
437         }
438     }
439 };
440 
checkSupport(vkt::Context & context,TestConfig testConfig)441 void checkSupport(vkt::Context &context, TestConfig testConfig)
442 {
443     if (testConfig.type == TestType::TWO_ENTRY_POINTS_EXECUTION_MODE_ID)
444         context.requireDeviceFunctionality("VK_KHR_maintenance4");
445 }
446 
447 } // namespace
448 
createMultipleShaderExtendedGroup(tcu::TestContext & testCtx)449 tcu::TestCaseGroup *createMultipleShaderExtendedGroup(tcu::TestContext &testCtx)
450 {
451     typedef InstanceFactory1WithSupport<EntryPointsTest, TestConfig, FunctionSupport1<TestConfig>, Programs>
452         EntryPointsTestCase;
453     de::MovePtr<tcu::TestCaseGroup> mainGroup(new tcu::TestCaseGroup(testCtx, "multiple_shaders_extended"));
454 
455     TestConfig testConfig = {TestType::TWO_ENTRY_POINTS_EXECUTION_MODE_ID};
456     mainGroup->addChild(new EntryPointsTestCase(testCtx, "two_entry_points_execution_mode_id", testConfig,
457                                                 typename FunctionSupport1<TestConfig>::Args(checkSupport, testConfig)));
458     testConfig = {TestType::TWO_ENTRY_POINTS_DIFFERENT_INTERFACES};
459     mainGroup->addChild(new EntryPointsTestCase(testCtx, "two_entry_points_different_interfaces", testConfig,
460                                                 typename FunctionSupport1<TestConfig>::Args(checkSupport, testConfig)));
461 
462     return mainGroup.release();
463 }
464 
465 } // namespace SpirVAssembly
466 } // namespace vkt
467