1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2019 The Khronos Group Inc.
6  * Copyright (c) 2019 Google Inc.
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 Tests for multiple interpolation decorations in a shader stage
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktDrawMultipleInterpolationTests.hpp"
26 
27 #include "tcuStringTemplate.hpp"
28 #include "vkCmdUtil.hpp"
29 #include "vkQueryUtil.hpp"
30 #include "vkTypeUtil.hpp"
31 #include "vkQueryUtil.hpp"
32 #include "vktDrawBaseClass.hpp"
33 #include "vktTestGroupUtil.hpp"
34 #include "tcuVectorUtil.hpp"
35 
36 namespace vkt
37 {
38 namespace Draw
39 {
40 namespace
41 {
42 
43 enum Interpolation
44 {
45     SMOOTH        = 0,
46     FLAT          = 1,
47     NOPERSPECTIVE = 2,
48     CENTROID      = 3,
49     SAMPLE        = 4,
50     COUNT         = 5,
51 };
52 
53 struct DrawParams
54 {
55     vk::VkFormat format;
56     tcu::UVec2 size;
57     vk::VkSampleCountFlagBits samples;
58     // From the SPIR-V point of view, structured test variants will allow us to test interpolation decorations on struct members
59     // instead of plain ids.
60     bool useStructure;
61     bool includeSampleDecoration;
62     const SharedGroupParams groupParams;
63 };
64 
65 template <typename T>
makeSharedPtr(vk::Move<T> move)66 inline de::SharedPtr<vk::Move<T>> makeSharedPtr(vk::Move<T> move)
67 {
68     return de::SharedPtr<vk::Move<T>>(new vk::Move<T>(move));
69 }
70 
interpolationToString(Interpolation interpolation)71 const char *interpolationToString(Interpolation interpolation)
72 {
73     switch (interpolation)
74     {
75     case SMOOTH:
76         return "smooth";
77     case FLAT:
78         return "flat";
79     case NOPERSPECTIVE:
80         return "noperspective";
81     case CENTROID:
82         return "centroid";
83     case SAMPLE:
84         return "sample";
85     default:
86         DE_FATAL("Invalid interpolation enum");
87     }
88 
89     return "";
90 }
91 
92 class DrawTestInstance : public TestInstance
93 {
94 public:
95     DrawTestInstance(Context &context, DrawParams params);
96     void render(de::SharedPtr<Image> &colorTargetImage, tcu::ConstPixelBufferAccess *frame, const char *vsName,
97                 const char *fsName, Interpolation interpolation, bool sampleRateShading);
98     bool compare(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference);
99     tcu::TestStatus iterate(void);
100 
101 protected:
102     void preRenderCommands(vk::VkCommandBuffer cmdBuffer, vk::VkImage colorTargetImage) const;
103     void drawCommands(vk::VkCommandBuffer cmdBuffer, vk::VkPipeline pipeline, vk::VkPipelineLayout pipelineLayout,
104                       vk::VkBuffer vertexBuffer, uint32_t pcData) const;
105 
106 #ifndef CTS_USES_VULKANSC
107     void beginSecondaryCmdBuffer(vk::VkCommandBuffer cmdBuffer, vk::VkRenderingFlagsKHR renderingFlags = 0u) const;
108     void beginDynamicRender(vk::VkCommandBuffer cmdBuffer, vk::VkRect2D renderArea, vk::VkClearValue clearValue,
109                             vk::VkRenderingFlagsKHR renderingFlags = 0u) const;
110 #endif // CTS_USES_VULKANSC
111 
112 private:
113     DrawParams m_params;
114     de::SharedPtr<Image> m_multisampleImage;
115     std::vector<de::SharedPtr<vk::Move<vk::VkImageView>>> m_colorTargetViews;
116     std::vector<de::SharedPtr<vk::Move<vk::VkImageView>>> m_multisampleViews;
117     de::SharedPtr<Buffer> m_vertexBuffer;
118     vk::Move<vk::VkRenderPass> m_renderPass;
119     vk::Move<vk::VkFramebuffer> m_framebuffer;
120     vk::Move<vk::VkPipelineLayout> m_pipelineLayout;
121     vk::Move<vk::VkPipeline> m_pipeline;
122 };
123 
DrawTestInstance(Context & context,DrawParams params)124 DrawTestInstance::DrawTestInstance(Context &context, DrawParams params) : TestInstance(context), m_params(params)
125 {
126 }
127 
128 class DrawTestCase : public TestCase
129 {
130 public:
131     DrawTestCase(tcu::TestContext &testCtx, const std::string &name, const DrawParams params);
132     ~DrawTestCase(void);
133     virtual void initPrograms(vk::SourceCollections &programCollection) const;
134     virtual void checkSupport(Context &context) const;
135     virtual TestInstance *createInstance(Context &context) const;
136 
137 private:
138     const DrawParams m_params;
139 };
140 
DrawTestCase(tcu::TestContext & testCtx,const std::string & name,const DrawParams params)141 DrawTestCase::DrawTestCase(tcu::TestContext &testCtx, const std::string &name, const DrawParams params)
142     : TestCase(testCtx, name)
143     , m_params(params)
144 {
145 }
146 
~DrawTestCase(void)147 DrawTestCase::~DrawTestCase(void)
148 {
149 }
150 
initPrograms(vk::SourceCollections & programCollection) const151 void DrawTestCase::initPrograms(vk::SourceCollections &programCollection) const
152 {
153     const std::string blockName                           = "ifb";
154     const std::map<std::string, std::string> replacements = {
155         std::pair<std::string, std::string>{
156             "blockOpeningOut", (m_params.useStructure ? "layout(location = 0) out InterfaceBlock {\n" : "")},
157         std::pair<std::string, std::string>{
158             "blockOpeningIn", (m_params.useStructure ? "layout(location = 0) in InterfaceBlock {\n" : "")},
159         std::pair<std::string, std::string>{"blockClosure", (m_params.useStructure ? "} " + blockName + ";\n" : "")},
160         std::pair<std::string, std::string>{
161             "extensions", (m_params.useStructure ? "#extension GL_ARB_enhanced_layouts : require\n" : "")},
162         std::pair<std::string, std::string>{"accessPrefix", (m_params.useStructure ? blockName + "." : "")},
163         std::pair<std::string, std::string>{"outQual", (m_params.useStructure ? "" : "out ")},
164         std::pair<std::string, std::string>{"inQual", (m_params.useStructure ? "" : "in ")},
165         std::pair<std::string, std::string>{"indent", (m_params.useStructure ? "    " : "")},
166     };
167 
168     std::ostringstream vertShaderMultiStream;
169     vertShaderMultiStream << "#version 430\n"
170                           << "${extensions}"
171                           << "\n"
172                           << "layout(location = 0) in vec4 in_position;\n"
173                           << "layout(location = 1) in vec4 in_color;\n"
174                           << "\n"
175                           << "${blockOpeningOut}"
176                           << "${indent}layout(location = 0) ${outQual}vec4 out_color_smooth;\n"
177                           << "${indent}layout(location = 1) ${outQual}flat vec4 out_color_flat;\n"
178                           << "${indent}layout(location = 2) ${outQual}noperspective vec4 out_color_noperspective;\n"
179                           << "${indent}layout(location = 3) ${outQual}centroid vec4 out_color_centroid;\n"
180                           << (m_params.includeSampleDecoration ?
181                                   "${indent}layout(location = 4) ${outQual}sample vec4 out_color_sample;\n" :
182                                   "")
183                           << "${blockClosure}"
184                           << "\n"
185                           << "void main()\n"
186                           << "{\n"
187                           << "    ${accessPrefix}out_color_smooth = in_color;\n"
188                           << "    ${accessPrefix}out_color_flat = in_color;\n"
189                           << "    ${accessPrefix}out_color_noperspective = in_color;\n"
190                           << "    ${accessPrefix}out_color_centroid = in_color;\n"
191                           << (m_params.includeSampleDecoration ? "    ${accessPrefix}out_color_sample = in_color;\n" :
192                                                                  "")
193                           << "    gl_Position = in_position;\n"
194                           << "}\n";
195     const tcu::StringTemplate vertShaderMulti(vertShaderMultiStream.str());
196 
197     const auto colorCount = (m_params.includeSampleDecoration ? COUNT : (COUNT - 1));
198 
199     std::ostringstream fragShaderMultiStream;
200     fragShaderMultiStream << "#version 430\n"
201                           << "${extensions}"
202                           << "\n"
203                           << "${blockOpeningIn}"
204                           << "${indent}layout(location = 0) ${inQual}vec4 in_color_smooth;\n"
205                           << "${indent}layout(location = 1) ${inQual}flat vec4 in_color_flat;\n"
206                           << "${indent}layout(location = 2) ${inQual}noperspective vec4 in_color_noperspective;\n"
207                           << "${indent}layout(location = 3) ${inQual}centroid vec4 in_color_centroid;\n"
208                           << (m_params.includeSampleDecoration ?
209                                   "${indent}layout(location = 4) ${inQual}sample vec4 in_color_sample;\n" :
210                                   "")
211                           << "${blockClosure}"
212                           << "\n"
213                           << "layout(push_constant, std430) uniform PushConstants {\n"
214                           << "    uint interpolationIndex;\n"
215                           << "} pc;\n"
216                           << "\n"
217                           << "layout(location=0) out vec4 out_color;\n"
218                           << "\n"
219                           << "void main()\n"
220                           << "{\n"
221                           << "    const vec4 in_colors[" + de::toString(colorCount) + "] = vec4[](\n"
222                           << "        ${accessPrefix}in_color_smooth,\n"
223                           << "        ${accessPrefix}in_color_flat,\n"
224                           << "        ${accessPrefix}in_color_noperspective,\n"
225                           << "        ${accessPrefix}in_color_centroid" << (m_params.includeSampleDecoration ? "," : "")
226                           << "\n"
227                           << (m_params.includeSampleDecoration ? "        ${accessPrefix}in_color_sample\n" : "")
228                           << "    );\n"
229                           << "    out_color = in_colors[pc.interpolationIndex];\n"
230                           << "}\n";
231     const tcu::StringTemplate fragShaderMulti(fragShaderMultiStream.str());
232 
233     const tcu::StringTemplate vertShaderSingle{
234         "#version 430\n"
235         "${extensions}"
236         "\n"
237         "layout(location = 0) in vec4 in_position;\n"
238         "layout(location = 1) in vec4 in_color;\n"
239         "\n"
240         "${blockOpeningOut}"
241         "${indent}layout(location = 0) ${outQual}${qualifier:opt}vec4 out_color;\n"
242         "${blockClosure}"
243         "\n"
244         "void main()\n"
245         "{\n"
246         "    ${accessPrefix}out_color = in_color;\n"
247         "    gl_Position = in_position;\n"
248         "}\n"};
249 
250     const tcu::StringTemplate fragShaderSingle{"#version 430\n"
251                                                "${extensions}"
252                                                "\n"
253                                                "${blockOpeningIn}"
254                                                "${indent}layout(location = 0) ${inQual}${qualifier:opt}vec4 in_color;\n"
255                                                "${blockClosure}"
256                                                "\n"
257                                                "layout(location = 0) out vec4 out_color;\n"
258                                                "\n"
259                                                "void main()\n"
260                                                "{\n"
261                                                "    out_color = ${accessPrefix}in_color;\n"
262                                                "}\n"};
263 
264     std::map<std::string, std::string> smooth        = replacements;
265     std::map<std::string, std::string> flat          = replacements;
266     std::map<std::string, std::string> noperspective = replacements;
267     std::map<std::string, std::string> centroid      = replacements;
268     std::map<std::string, std::string> sample        = replacements;
269 
270     flat["qualifier"]          = "flat ";
271     noperspective["qualifier"] = "noperspective ";
272     centroid["qualifier"]      = "centroid ";
273     sample["qualifier"]        = "sample ";
274 
275     programCollection.glslSources.add("vert_multi") << glu::VertexSource(vertShaderMulti.specialize(replacements));
276     programCollection.glslSources.add("frag_multi") << glu::FragmentSource(fragShaderMulti.specialize(replacements));
277     programCollection.glslSources.add("vert_smooth") << glu::VertexSource(vertShaderSingle.specialize(smooth));
278     programCollection.glslSources.add("frag_smooth") << glu::FragmentSource(fragShaderSingle.specialize(smooth));
279     programCollection.glslSources.add("vert_flat") << glu::VertexSource(vertShaderSingle.specialize(flat));
280     programCollection.glslSources.add("frag_flat") << glu::FragmentSource(fragShaderSingle.specialize(flat));
281     programCollection.glslSources.add("vert_noperspective")
282         << glu::VertexSource(vertShaderSingle.specialize(noperspective));
283     programCollection.glslSources.add("frag_noperspective")
284         << glu::FragmentSource(fragShaderSingle.specialize(noperspective));
285     programCollection.glslSources.add("vert_centroid") << glu::VertexSource(vertShaderSingle.specialize(centroid));
286     programCollection.glslSources.add("frag_centroid") << glu::FragmentSource(fragShaderSingle.specialize(centroid));
287 
288     if (m_params.includeSampleDecoration)
289     {
290         programCollection.glslSources.add("vert_sample") << glu::VertexSource(vertShaderSingle.specialize(sample));
291         programCollection.glslSources.add("frag_sample") << glu::FragmentSource(fragShaderSingle.specialize(sample));
292     }
293 }
294 
checkSupport(Context & context) const295 void DrawTestCase::checkSupport(Context &context) const
296 {
297     if (!(m_params.samples & context.getDeviceProperties().limits.framebufferColorSampleCounts))
298         TCU_THROW(NotSupportedError, "Multisampling with " + de::toString(m_params.samples) + " samples not supported");
299 
300     if (m_params.includeSampleDecoration && !context.getDeviceFeatures().sampleRateShading)
301         TCU_THROW(NotSupportedError, "Sample rate shading not supported");
302 
303     if (m_params.groupParams->useDynamicRendering)
304         context.requireDeviceFunctionality("VK_KHR_dynamic_rendering");
305 }
306 
createInstance(Context & context) const307 TestInstance *DrawTestCase::createInstance(Context &context) const
308 {
309     return new DrawTestInstance(context, m_params);
310 }
311 
render(de::SharedPtr<Image> & colorTargetImage,tcu::ConstPixelBufferAccess * frame,const char * vsName,const char * fsName,Interpolation interpolation,bool sampleRateShading)312 void DrawTestInstance::render(de::SharedPtr<Image> &colorTargetImage, tcu::ConstPixelBufferAccess *frame,
313                               const char *vsName, const char *fsName, Interpolation interpolation,
314                               bool sampleRateShading)
315 {
316     const uint32_t pcData                  = static_cast<uint32_t>(interpolation);
317     const uint32_t pcDataSize              = static_cast<uint32_t>(sizeof(pcData));
318     const bool useMultisampling            = (m_params.samples != vk::VK_SAMPLE_COUNT_1_BIT);
319     const vk::VkBool32 sampleShadingEnable = (sampleRateShading ? VK_TRUE : VK_FALSE);
320     const vk::DeviceInterface &vk          = m_context.getDeviceInterface();
321     const vk::VkDevice device              = m_context.getDevice();
322     const vk::Unique<vk::VkShaderModule> vs(
323         createShaderModule(vk, device, m_context.getBinaryCollection().get(vsName), 0));
324     const vk::Unique<vk::VkShaderModule> fs(
325         createShaderModule(vk, device, m_context.getBinaryCollection().get(fsName), 0));
326     const CmdPoolCreateInfo cmdPoolCreateInfo = m_context.getUniversalQueueFamilyIndex();
327     vk::Move<vk::VkCommandPool> cmdPool       = createCommandPool(vk, device, &cmdPoolCreateInfo);
328     vk::Move<vk::VkCommandBuffer> cmdBuffer =
329         vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
330     vk::Move<vk::VkCommandBuffer> secCmdBuffer;
331 
332     m_colorTargetViews.clear();
333     m_multisampleViews.clear();
334 
335     // Create color buffer images
336     {
337         const vk::VkExtent3D targetImageExtent = {m_params.size.x(), m_params.size.y(), 1};
338         const vk::VkImageUsageFlags usage      = vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
339                                             vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT | vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT;
340         const ImageCreateInfo targetImageCreateInfo(vk::VK_IMAGE_TYPE_2D, m_params.format, targetImageExtent, 1, 1,
341                                                     vk::VK_SAMPLE_COUNT_1_BIT, vk::VK_IMAGE_TILING_OPTIMAL, usage);
342 
343         colorTargetImage = Image::createAndAlloc(vk, device, targetImageCreateInfo, m_context.getDefaultAllocator(),
344                                                  m_context.getUniversalQueueFamilyIndex());
345 
346         if (useMultisampling)
347         {
348             const ImageCreateInfo multisampleImageCreateInfo(vk::VK_IMAGE_TYPE_2D, m_params.format, targetImageExtent,
349                                                              1, 1, m_params.samples, vk::VK_IMAGE_TILING_OPTIMAL,
350                                                              usage);
351 
352             m_multisampleImage =
353                 Image::createAndAlloc(vk, device, multisampleImageCreateInfo, m_context.getDefaultAllocator(),
354                                       m_context.getUniversalQueueFamilyIndex());
355         }
356     }
357 
358     {
359         const ImageViewCreateInfo colorTargetViewInfo(colorTargetImage->object(), vk::VK_IMAGE_VIEW_TYPE_2D,
360                                                       m_params.format);
361 
362         m_colorTargetViews.push_back(makeSharedPtr(createImageView(vk, device, &colorTargetViewInfo)));
363 
364         if (useMultisampling)
365         {
366             const ImageViewCreateInfo multisamplingTargetViewInfo(m_multisampleImage->object(),
367                                                                   vk::VK_IMAGE_VIEW_TYPE_2D, m_params.format);
368 
369             m_multisampleViews.push_back(makeSharedPtr(createImageView(vk, device, &multisamplingTargetViewInfo)));
370         }
371     }
372 
373     // Create render pass and framebuffer
374     if (!m_params.groupParams->useDynamicRendering)
375     {
376         RenderPassCreateInfo renderPassCreateInfo;
377         std::vector<vk::VkImageView> attachments;
378         std::vector<vk::VkAttachmentReference> colorAttachmentRefs;
379         std::vector<vk::VkAttachmentReference> multisampleAttachmentRefs;
380         uint32_t attachmentNdx = 0;
381 
382         {
383             const vk::VkAttachmentReference colorAttachmentReference = {attachmentNdx++, vk::VK_IMAGE_LAYOUT_GENERAL};
384 
385             colorAttachmentRefs.push_back(colorAttachmentReference);
386 
387             renderPassCreateInfo.addAttachment(AttachmentDescription(
388                 m_params.format, vk::VK_SAMPLE_COUNT_1_BIT, vk::VK_ATTACHMENT_LOAD_OP_CLEAR,
389                 vk::VK_ATTACHMENT_STORE_OP_STORE, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE,
390                 vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, vk::VK_IMAGE_LAYOUT_UNDEFINED, vk::VK_IMAGE_LAYOUT_GENERAL));
391 
392             if (useMultisampling)
393             {
394                 const vk::VkAttachmentReference multiSampleAttachmentReference = {attachmentNdx++,
395                                                                                   vk::VK_IMAGE_LAYOUT_GENERAL};
396 
397                 multisampleAttachmentRefs.push_back(multiSampleAttachmentReference);
398 
399                 renderPassCreateInfo.addAttachment(AttachmentDescription(
400                     m_params.format, m_params.samples, vk::VK_ATTACHMENT_LOAD_OP_CLEAR,
401                     vk::VK_ATTACHMENT_STORE_OP_STORE, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE,
402                     vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, vk::VK_IMAGE_LAYOUT_UNDEFINED, vk::VK_IMAGE_LAYOUT_GENERAL));
403             }
404         }
405 
406         renderPassCreateInfo.addSubpass(SubpassDescription(
407             vk::VK_PIPELINE_BIND_POINT_GRAPHICS, 0, 0, DE_NULL, (uint32_t)colorAttachmentRefs.size(),
408             useMultisampling ? &multisampleAttachmentRefs[0] : &colorAttachmentRefs[0],
409             useMultisampling ? &colorAttachmentRefs[0] : DE_NULL, AttachmentReference(), 0, DE_NULL));
410 
411         m_renderPass = createRenderPass(vk, device, &renderPassCreateInfo);
412 
413         for (uint32_t frameNdx = 0; frameNdx < m_colorTargetViews.size(); frameNdx++)
414         {
415             attachments.push_back(**m_colorTargetViews[frameNdx]);
416 
417             if (useMultisampling)
418                 attachments.push_back(**m_multisampleViews[frameNdx]);
419         }
420 
421         const vk::VkFramebufferCreateInfo framebufferCreateInfo = {vk::VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
422                                                                    DE_NULL,
423                                                                    0u,
424                                                                    *m_renderPass,
425                                                                    (uint32_t)attachments.size(),
426                                                                    &attachments[0],
427                                                                    m_params.size.x(),
428                                                                    m_params.size.y(),
429                                                                    1};
430 
431         m_framebuffer = createFramebuffer(vk, device, &framebufferCreateInfo);
432     }
433 
434     // Create vertex buffer.
435     {
436         const PositionColorVertex vertices[] = {
437             PositionColorVertex(tcu::Vec4(-1.5f, -0.4f, 1.0f, 2.0f), // Coord
438                                 tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f)),  // Color
439 
440             PositionColorVertex(tcu::Vec4(0.4f, -0.4f, 0.5f, 0.5f), // Coord
441                                 tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f)), // Color
442 
443             PositionColorVertex(tcu::Vec4(0.3f, 0.8f, 0.0f, 1.0f), // Coord
444                                 tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f)) // Color
445         };
446 
447         const vk::VkDeviceSize dataSize = DE_LENGTH_OF_ARRAY(vertices) * sizeof(PositionColorVertex);
448         m_vertexBuffer =
449             Buffer::createAndAlloc(vk, device, BufferCreateInfo(dataSize, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT),
450                                    m_context.getDefaultAllocator(), vk::MemoryRequirement::HostVisible);
451         uint8_t *ptr = reinterpret_cast<uint8_t *>(m_vertexBuffer->getBoundMemory().getHostPtr());
452 
453         deMemcpy(ptr, vertices, static_cast<size_t>(dataSize));
454         flushMappedMemoryRange(vk, device, m_vertexBuffer->getBoundMemory().getMemory(),
455                                m_vertexBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE);
456     }
457 
458     // Create pipeline
459     {
460         const vk::VkViewport viewport = vk::makeViewport(m_params.size.x(), m_params.size.y());
461         const vk::VkRect2D scissor    = vk::makeRect2D(m_params.size.x(), m_params.size.y());
462         const auto pcRange            = vk::makePushConstantRange(vk::VK_SHADER_STAGE_FRAGMENT_BIT, 0u, pcDataSize);
463         const std::vector<vk::VkPushConstantRange> pcRanges(1u, pcRange);
464         const PipelineLayoutCreateInfo pipelineLayoutCreateInfo(0u, nullptr, static_cast<uint32_t>(pcRanges.size()),
465                                                                 pcRanges.data());
466 
467         m_pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
468 
469         PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, 0);
470 
471         const vk::VkVertexInputBindingDescription vertexInputBindingDescription = {0, (uint32_t)sizeof(tcu::Vec4) * 2,
472                                                                                    vk::VK_VERTEX_INPUT_RATE_VERTEX};
473 
474         const vk::VkVertexInputAttributeDescription vertexInputAttributeDescriptions[2] = {
475             {0u, 0u, vk::VK_FORMAT_R32G32B32A32_SFLOAT, 0u},
476             {1u, 0u, vk::VK_FORMAT_R32G32B32A32_SFLOAT, (uint32_t)(sizeof(float) * 4)}};
477 
478         std::vector<PipelineCreateInfo::ColorBlendState::Attachment> vkCbAttachmentStates(1u);
479         PipelineCreateInfo::VertexInputState vertexInputState = PipelineCreateInfo::VertexInputState(
480             1, &vertexInputBindingDescription, 2, vertexInputAttributeDescriptions);
481 
482         pipelineCreateInfo.addShader(
483             PipelineCreateInfo::PipelineShaderStage(*vs, "main", vk::VK_SHADER_STAGE_VERTEX_BIT));
484         pipelineCreateInfo.addShader(
485             PipelineCreateInfo::PipelineShaderStage(*fs, "main", vk::VK_SHADER_STAGE_FRAGMENT_BIT));
486         pipelineCreateInfo.addState(PipelineCreateInfo::VertexInputState(vertexInputState));
487         pipelineCreateInfo.addState(PipelineCreateInfo::InputAssemblerState(vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST));
488         pipelineCreateInfo.addState(
489             PipelineCreateInfo::ColorBlendState((uint32_t)vkCbAttachmentStates.size(), &vkCbAttachmentStates[0]));
490         pipelineCreateInfo.addState(PipelineCreateInfo::ViewportState(1, std::vector<vk::VkViewport>(1, viewport),
491                                                                       std::vector<vk::VkRect2D>(1, scissor)));
492         pipelineCreateInfo.addState(PipelineCreateInfo::DepthStencilState());
493         pipelineCreateInfo.addState(PipelineCreateInfo::RasterizerState());
494         pipelineCreateInfo.addState(PipelineCreateInfo::MultiSampleState(m_params.samples, sampleShadingEnable, 1.0f));
495 
496 #ifndef CTS_USES_VULKANSC
497         std::vector<vk::VkFormat> colorAttachmentFormats(m_colorTargetViews.size(), m_params.format);
498         vk::VkPipelineRenderingCreateInfoKHR renderingCreateInfo{
499             vk::VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR,
500             DE_NULL,
501             0u,
502             static_cast<uint32_t>(colorAttachmentFormats.size()),
503             colorAttachmentFormats.data(),
504             vk::VK_FORMAT_UNDEFINED,
505             vk::VK_FORMAT_UNDEFINED};
506 
507         if (m_params.groupParams->useDynamicRendering)
508             pipelineCreateInfo.pNext = &renderingCreateInfo;
509 #endif // CTS_USES_VULKANSC
510 
511         m_pipeline = createGraphicsPipeline(vk, device, DE_NULL, &pipelineCreateInfo);
512     }
513 
514     // Queue draw and read results.
515     {
516         const vk::VkQueue queue         = m_context.getUniversalQueue();
517         const vk::VkRect2D renderArea   = vk::makeRect2D(m_params.size.x(), m_params.size.y());
518         const vk::VkBuffer buffer       = m_vertexBuffer->object();
519         const vk::VkOffset3D zeroOffset = {0, 0, 0};
520         const auto clearValueColor      = vk::makeClearValueColor(tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
521 
522 #ifndef CTS_USES_VULKANSC
523         if (m_params.groupParams->useSecondaryCmdBuffer)
524         {
525             secCmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_SECONDARY);
526 
527             // record secondary command buffer
528             if (m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
529             {
530                 beginSecondaryCmdBuffer(*secCmdBuffer, vk::VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT);
531                 beginDynamicRender(*secCmdBuffer, renderArea, clearValueColor);
532             }
533             else
534                 beginSecondaryCmdBuffer(*secCmdBuffer);
535 
536             drawCommands(*secCmdBuffer, *m_pipeline, *m_pipelineLayout, buffer, pcData);
537 
538             if (m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
539                 endRendering(vk, *secCmdBuffer);
540 
541             endCommandBuffer(vk, *secCmdBuffer);
542 
543             // record primary command buffer
544             beginCommandBuffer(vk, *cmdBuffer, 0u);
545             preRenderCommands(*cmdBuffer, colorTargetImage->object());
546 
547             if (!m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
548                 beginDynamicRender(*cmdBuffer, renderArea, clearValueColor,
549                                    vk::VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
550 
551             vk.cmdExecuteCommands(*cmdBuffer, 1u, &*secCmdBuffer);
552 
553             if (!m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
554                 endRendering(vk, *cmdBuffer);
555 
556             endCommandBuffer(vk, *cmdBuffer);
557         }
558         else if (m_params.groupParams->useDynamicRendering)
559         {
560             beginCommandBuffer(vk, *cmdBuffer);
561 
562             preRenderCommands(*cmdBuffer, colorTargetImage->object());
563             beginDynamicRender(*cmdBuffer, renderArea, clearValueColor);
564             drawCommands(*cmdBuffer, *m_pipeline, *m_pipelineLayout, buffer, pcData);
565             endRendering(vk, *cmdBuffer);
566 
567             endCommandBuffer(vk, *cmdBuffer);
568         }
569 #endif // CTS_USES_VULKANSC
570 
571         if (!m_params.groupParams->useDynamicRendering)
572         {
573             const uint32_t imagesCount = static_cast<uint32_t>(m_colorTargetViews.size() + m_multisampleViews.size());
574             std::vector<vk::VkClearValue> clearValues(2, clearValueColor);
575 
576             beginCommandBuffer(vk, *cmdBuffer);
577 
578             preRenderCommands(*cmdBuffer, colorTargetImage->object());
579             beginRenderPass(vk, *cmdBuffer, *m_renderPass, *m_framebuffer, renderArea, imagesCount, &clearValues[0]);
580             drawCommands(*cmdBuffer, *m_pipeline, *m_pipelineLayout, buffer, pcData);
581             endRenderPass(vk, *cmdBuffer);
582 
583             endCommandBuffer(vk, *cmdBuffer);
584         }
585 
586         submitCommandsAndWait(vk, device, queue, *cmdBuffer);
587 
588         *frame = colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(), vk::VK_IMAGE_LAYOUT_GENERAL,
589                                                zeroOffset, (int)m_params.size.x(), (int)m_params.size.y(),
590                                                vk::VK_IMAGE_ASPECT_COLOR_BIT);
591     }
592 }
593 
preRenderCommands(vk::VkCommandBuffer cmdBuffer,vk::VkImage colorTargetImage) const594 void DrawTestInstance::preRenderCommands(vk::VkCommandBuffer cmdBuffer, vk::VkImage colorTargetImage) const
595 {
596     if (!m_params.groupParams->useDynamicRendering)
597         return;
598 
599     const vk::DeviceInterface &vk = m_context.getDeviceInterface();
600     initialTransitionColor2DImage(vk, cmdBuffer, colorTargetImage, vk::VK_IMAGE_LAYOUT_GENERAL,
601                                   vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
602                                   vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
603 
604     if (m_params.samples != vk::VK_SAMPLE_COUNT_1_BIT)
605     {
606         initialTransitionColor2DImage(vk, cmdBuffer, m_multisampleImage->object(), vk::VK_IMAGE_LAYOUT_GENERAL,
607                                       vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
608                                       vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
609     }
610 }
611 
drawCommands(vk::VkCommandBuffer cmdBuffer,vk::VkPipeline pipeline,vk::VkPipelineLayout pipelineLayout,vk::VkBuffer vertexBuffer,uint32_t pcData) const612 void DrawTestInstance::drawCommands(vk::VkCommandBuffer cmdBuffer, vk::VkPipeline pipeline,
613                                     vk::VkPipelineLayout pipelineLayout, vk::VkBuffer vertexBuffer,
614                                     uint32_t pcData) const
615 {
616     const vk::DeviceInterface &vk             = m_context.getDeviceInterface();
617     const vk::VkDeviceSize vertexBufferOffset = 0;
618     const uint32_t pcDataSize                 = static_cast<uint32_t>(sizeof(pcData));
619 
620     vk.cmdBindVertexBuffers(cmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
621     vk.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
622     vk.cmdPushConstants(cmdBuffer, pipelineLayout, vk::VK_SHADER_STAGE_FRAGMENT_BIT, 0u, pcDataSize, &pcData);
623     vk.cmdDraw(cmdBuffer, 3u, 1u, 0u, 0u);
624 }
625 
compare(const tcu::ConstPixelBufferAccess & result,const tcu::ConstPixelBufferAccess & reference)626 bool DrawTestInstance::compare(const tcu::ConstPixelBufferAccess &result, const tcu::ConstPixelBufferAccess &reference)
627 {
628     DE_ASSERT(result.getSize() == reference.getSize());
629 
630     const tcu::IVec4 threshold(1u, 1u, 1u, 1u);
631 
632     for (int y = 0; y < result.getHeight(); y++)
633     {
634         for (int x = 0; x < result.getWidth(); x++)
635         {
636             tcu::IVec4 refPix = reference.getPixelInt(x, y);
637             tcu::IVec4 cmpPix = result.getPixelInt(x, y);
638             tcu::IVec4 diff   = tcu::abs(refPix - cmpPix);
639 
640             if (!tcu::boolAll(tcu::lessThanEqual(diff, threshold)))
641                 return false;
642         }
643     }
644 
645     return true;
646 }
647 
648 #ifndef CTS_USES_VULKANSC
beginSecondaryCmdBuffer(vk::VkCommandBuffer cmdBuffer,vk::VkRenderingFlagsKHR renderingFlags) const649 void DrawTestInstance::beginSecondaryCmdBuffer(vk::VkCommandBuffer cmdBuffer,
650                                                vk::VkRenderingFlagsKHR renderingFlags) const
651 {
652     std::vector<vk::VkFormat> colorAttachmentFormats(m_colorTargetViews.size(), m_params.format);
653     vk::VkCommandBufferInheritanceRenderingInfoKHR inheritanceRenderingInfo{
654         vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO_KHR, // VkStructureType sType;
655         DE_NULL,                                                             // const void* pNext;
656         renderingFlags,                                                      // VkRenderingFlagsKHR flags;
657         0u,                                                                  // uint32_t viewMask;
658         (uint32_t)colorAttachmentFormats.size(),                             // uint32_t colorAttachmentCount;
659         colorAttachmentFormats.data(),                                       // const VkFormat* pColorAttachmentFormats;
660         vk::VK_FORMAT_UNDEFINED,                                             // VkFormat depthAttachmentFormat;
661         vk::VK_FORMAT_UNDEFINED,                                             // VkFormat stencilAttachmentFormat;
662         m_params.samples, // VkSampleCountFlagBits rasterizationSamples;
663     };
664     const vk::VkCommandBufferInheritanceInfo bufferInheritanceInfo = vk::initVulkanStructure(&inheritanceRenderingInfo);
665 
666     vk::VkCommandBufferUsageFlags usageFlags = vk::VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
667     if (!m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
668         usageFlags |= vk::VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
669 
670     const vk::VkCommandBufferBeginInfo commandBufBeginParams{
671         vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType;
672         DE_NULL,                                         // const void* pNext;
673         usageFlags,                                      // VkCommandBufferUsageFlags flags;
674         &bufferInheritanceInfo};
675 
676     const vk::DeviceInterface &vk = m_context.getDeviceInterface();
677     VK_CHECK(vk.beginCommandBuffer(cmdBuffer, &commandBufBeginParams));
678 }
679 
beginDynamicRender(vk::VkCommandBuffer cmdBuffer,vk::VkRect2D renderArea,vk::VkClearValue clearValue,vk::VkRenderingFlagsKHR renderingFlags) const680 void DrawTestInstance::beginDynamicRender(vk::VkCommandBuffer cmdBuffer, vk::VkRect2D renderArea,
681                                           vk::VkClearValue clearValue, vk::VkRenderingFlagsKHR renderingFlags) const
682 {
683     const vk::DeviceInterface &vk = m_context.getDeviceInterface();
684     const uint32_t imagesCount    = static_cast<uint32_t>(m_colorTargetViews.size());
685 
686     std::vector<vk::VkRenderingAttachmentInfoKHR> colorAttachments(
687         imagesCount,
688         {
689             vk::VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR, // VkStructureType sType;
690             DE_NULL,                                             // const void* pNext;
691             DE_NULL,                                             // VkImageView imageView;
692             vk::VK_IMAGE_LAYOUT_GENERAL,                         // VkImageLayout imageLayout;
693             vk::VK_RESOLVE_MODE_NONE,                            // VkResolveModeFlagBits resolveMode;
694             DE_NULL,                                             // VkImageView resolveImageView;
695             vk::VK_IMAGE_LAYOUT_GENERAL,                         // VkImageLayout resolveImageLayout;
696             vk::VK_ATTACHMENT_LOAD_OP_CLEAR,                     // VkAttachmentLoadOp loadOp;
697             vk::VK_ATTACHMENT_STORE_OP_STORE,                    // VkAttachmentStoreOp storeOp;
698             clearValue                                           // VkClearValue clearValue;
699         });
700 
701     for (uint32_t i = 0; i < imagesCount; ++i)
702     {
703         if (m_params.samples != vk::VK_SAMPLE_COUNT_1_BIT)
704         {
705             colorAttachments[i].imageView        = **m_multisampleViews[i];
706             colorAttachments[i].resolveMode      = vk::VK_RESOLVE_MODE_AVERAGE_BIT;
707             colorAttachments[i].resolveImageView = **m_colorTargetViews[i];
708         }
709         else
710             colorAttachments[i].imageView = **m_colorTargetViews[i];
711     }
712 
713     vk::VkRenderingInfoKHR renderingInfo{
714         vk::VK_STRUCTURE_TYPE_RENDERING_INFO_KHR,
715         DE_NULL,
716         renderingFlags,          // VkRenderingFlagsKHR flags;
717         renderArea,              // VkRect2D renderArea;
718         1u,                      // uint32_t layerCount;
719         0u,                      // uint32_t viewMask;
720         imagesCount,             // uint32_t colorAttachmentCount;
721         colorAttachments.data(), // const VkRenderingAttachmentInfoKHR* pColorAttachments;
722         DE_NULL,                 // const VkRenderingAttachmentInfoKHR* pDepthAttachment;
723         DE_NULL,                 // const VkRenderingAttachmentInfoKHR* pStencilAttachment;
724     };
725 
726     vk.cmdBeginRendering(cmdBuffer, &renderingInfo);
727 }
728 #endif // CTS_USES_VULKANSC
729 
iterate(void)730 tcu::TestStatus DrawTestInstance::iterate(void)
731 {
732     tcu::TestLog &log           = m_context.getTestContext().getLog();
733     const bool useMultisampling = (m_params.samples != vk::VK_SAMPLE_COUNT_1_BIT);
734     const uint32_t frameCount   = static_cast<uint32_t>(COUNT);
735     std::vector<de::SharedPtr<Image>> resImages(frameCount);
736     de::SharedPtr<Image> smoothImage[2];
737     de::SharedPtr<Image> flatImage[2];
738     de::SharedPtr<Image> noperspectiveImage[2];
739     de::SharedPtr<Image> centroidImage[2];
740     de::SharedPtr<Image> sampleImage[2];
741     tcu::ConstPixelBufferAccess resFrames[frameCount];
742     tcu::ConstPixelBufferAccess refFrames[frameCount];
743     tcu::ConstPixelBufferAccess refSRSFrames[frameCount]; // Using sample rate shading.
744 
745     for (int interpolationType = 0; interpolationType < COUNT; ++interpolationType)
746     {
747         // Avoid generating a result image for the sample decoration if we're not using it.
748         if (!m_params.includeSampleDecoration && interpolationType == Interpolation::SAMPLE)
749             continue;
750 
751         render(resImages[interpolationType], &resFrames[interpolationType], "vert_multi", "frag_multi",
752                static_cast<Interpolation>(interpolationType), false);
753     }
754 
755     for (int i = 0; i < 2; ++i)
756     {
757         const bool useSampleRateShading = (i > 0);
758 
759         // Sample rate shading is an alternative good result for cases using the sample decoration.
760         if (useSampleRateShading && !m_params.includeSampleDecoration)
761             continue;
762 
763         tcu::ConstPixelBufferAccess *framesArray = (useSampleRateShading ? refSRSFrames : refFrames);
764 
765         render(smoothImage[i], &framesArray[SMOOTH], "vert_smooth", "frag_smooth", SMOOTH, useSampleRateShading);
766         render(flatImage[i], &framesArray[FLAT], "vert_flat", "frag_flat", FLAT, useSampleRateShading);
767         render(noperspectiveImage[i], &framesArray[NOPERSPECTIVE], "vert_noperspective", "frag_noperspective",
768                NOPERSPECTIVE, useSampleRateShading);
769         render(centroidImage[i], &framesArray[CENTROID], "vert_centroid", "frag_centroid", CENTROID,
770                useSampleRateShading);
771 
772         // Avoid generating a reference image for the sample interpolation if we're not using it.
773         if (m_params.includeSampleDecoration)
774             render(sampleImage[i], &framesArray[SAMPLE], "vert_sample", "frag_sample", SAMPLE, useSampleRateShading);
775     }
776 
777     for (uint32_t resNdx = 0; resNdx < frameCount; resNdx++)
778     {
779         if (!m_params.includeSampleDecoration && resNdx == SAMPLE)
780             continue;
781 
782         const std::string resName = interpolationToString((Interpolation)resNdx);
783 
784         log << tcu::TestLog::ImageSet(resName, resName) << tcu::TestLog::Image("Result", "Result", resFrames[resNdx])
785             << tcu::TestLog::Image("Reference", "Reference", refFrames[resNdx]);
786         if (m_params.includeSampleDecoration)
787             log << tcu::TestLog::Image("ReferenceSRS", "Reference with sample shading", refSRSFrames[resNdx]);
788         log << tcu::TestLog::EndImageSet;
789 
790         for (uint32_t refNdx = 0; refNdx < frameCount; refNdx++)
791         {
792             if (!m_params.includeSampleDecoration && refNdx == SAMPLE)
793                 continue;
794 
795             const std::string refName = interpolationToString((Interpolation)refNdx);
796 
797             if (resNdx == refNdx)
798             {
799                 if (!compare(resFrames[resNdx], refFrames[refNdx]) &&
800                     (!m_params.includeSampleDecoration || !compare(resFrames[resNdx], refSRSFrames[refNdx])))
801                     return tcu::TestStatus::fail(resName + " produced different results");
802             }
803             else if (!useMultisampling &&
804                      ((resNdx == SMOOTH && refNdx == CENTROID) || (resNdx == CENTROID && refNdx == SMOOTH) ||
805                       (resNdx == SMOOTH && refNdx == SAMPLE) || (resNdx == SAMPLE && refNdx == SMOOTH) ||
806                       (resNdx == CENTROID && refNdx == SAMPLE) || (resNdx == SAMPLE && refNdx == CENTROID)))
807             {
808                 if (!compare(resFrames[resNdx], refFrames[refNdx]))
809                     return tcu::TestStatus::fail(resName + " and " + refName +
810                                                  " produced different results without multisampling");
811             }
812             else
813             {
814                 // "smooth" means lack of centroid and sample.
815                 // Spec does not specify exactly what "smooth" should be, so it can match centroid or sample.
816                 // "centroid" and "sample" may also produce the same results.
817                 if (!((resNdx == SMOOTH && refNdx == CENTROID) || (resNdx == CENTROID && refNdx == SMOOTH) ||
818                       (resNdx == SMOOTH && refNdx == SAMPLE) || (resNdx == SAMPLE && refNdx == SMOOTH) ||
819                       (resNdx == CENTROID && refNdx == SAMPLE) || (resNdx == SAMPLE && refNdx == CENTROID)))
820                 {
821                     if (compare(resFrames[resNdx], refFrames[refNdx]))
822                         return tcu::TestStatus::fail(resName + " and " + refName + " produced same result");
823                 }
824             }
825         }
826     }
827 
828     return tcu::TestStatus::pass("Results differ and references match");
829 }
830 
createTests(tcu::TestCaseGroup * testGroup,const SharedGroupParams groupParams)831 void createTests(tcu::TestCaseGroup *testGroup, const SharedGroupParams groupParams)
832 {
833     tcu::TestContext &testCtx = testGroup->getTestContext();
834     const vk::VkFormat format = vk::VK_FORMAT_R8G8B8A8_UNORM;
835     const tcu::UVec2 size(128, 128);
836 
837     struct TestVariant
838     {
839         const std::string name;
840         const vk::VkSampleCountFlagBits samples;
841     };
842 
843     static const std::vector<TestVariant> testVariants{
844         {"1_sample", vk::VK_SAMPLE_COUNT_1_BIT},    {"2_samples", vk::VK_SAMPLE_COUNT_2_BIT},
845         {"4_samples", vk::VK_SAMPLE_COUNT_4_BIT},   {"8_samples", vk::VK_SAMPLE_COUNT_8_BIT},
846         {"16_samples", vk::VK_SAMPLE_COUNT_16_BIT}, {"32_samples", vk::VK_SAMPLE_COUNT_32_BIT},
847         {"64_samples", vk::VK_SAMPLE_COUNT_64_BIT},
848     };
849 
850     struct GroupVariant
851     {
852         const bool useStructure;
853         const std::string groupName;
854     };
855 
856     static const std::vector<GroupVariant> groupVariants{
857         {false, "separate"},
858         {true, "structured"},
859     };
860 
861     const struct
862     {
863         const bool includeSampleDecoration;
864         const std::string groupName;
865     } sampleVariants[]{
866         {false, "no_sample_decoration"},
867         {true, "with_sample_decoration"},
868     };
869 
870     for (const auto &grpVariant : groupVariants)
871     {
872         de::MovePtr<tcu::TestCaseGroup> group{new tcu::TestCaseGroup{testCtx, grpVariant.groupName.c_str()}};
873 
874         for (const auto &sampleVariant : sampleVariants)
875         {
876             de::MovePtr<tcu::TestCaseGroup> sampleGroup{
877                 new tcu::TestCaseGroup{testCtx, sampleVariant.groupName.c_str()}};
878 
879             for (const auto &testVariant : testVariants)
880             {
881                 const DrawParams params{format,
882                                         size,
883                                         testVariant.samples,
884                                         grpVariant.useStructure,
885                                         sampleVariant.includeSampleDecoration,
886                                         groupParams};
887                 sampleGroup->addChild(new DrawTestCase(testCtx, testVariant.name, params));
888             }
889 
890             group->addChild(sampleGroup.release());
891         }
892 
893         testGroup->addChild(group.release());
894     }
895 }
896 
897 } // namespace
898 
createMultipleInterpolationTests(tcu::TestContext & testCtx,const SharedGroupParams groupParams)899 tcu::TestCaseGroup *createMultipleInterpolationTests(tcu::TestContext &testCtx, const SharedGroupParams groupParams)
900 {
901     // Tests for multiple interpolation decorations in a shader stage.
902     return createTestGroup(testCtx, "multiple_interpolation", createTests, groupParams);
903 }
904 
905 } // namespace Draw
906 } // namespace vkt
907