Add yflip versions of tessellation winding order tests
[platform/upstream/VK-GL-CTS.git] / external / vulkancts / modules / vulkan / tessellation / vktTessellationWindingTests.cpp
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2014 The Android Open Source Project
6  * Copyright (c) 2016 The Khronos Group 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 Tessellation Winding Tests
23  *//*--------------------------------------------------------------------*/
24
25 #include "vktTessellationWindingTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktTessellationUtil.hpp"
28
29 #include "tcuTestLog.hpp"
30 #include "tcuRGBA.hpp"
31
32 #include "vkDefs.hpp"
33 #include "vkQueryUtil.hpp"
34 #include "vkBuilderUtil.hpp"
35 #include "vkImageUtil.hpp"
36 #include "vkTypeUtil.hpp"
37 #include "vkStrUtil.hpp"
38
39 #include "deUniquePtr.hpp"
40
41 namespace vkt
42 {
43 namespace tessellation
44 {
45
46 using namespace vk;
47
48 namespace
49 {
50
51 std::string getCaseName (const TessPrimitiveType primitiveType, const Winding winding, bool yFlip)
52 {
53         std::ostringstream str;
54         str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getWindingShaderName(winding);
55         if (yFlip)
56                 str << "_yflip";
57         return str.str();
58 }
59
60 inline VkFrontFace mapFrontFace (const Winding winding)
61 {
62         switch (winding)
63         {
64                 case WINDING_CCW:       return VK_FRONT_FACE_COUNTER_CLOCKWISE;
65                 case WINDING_CW:        return VK_FRONT_FACE_CLOCKWISE;
66                 default:
67                         DE_ASSERT(false);
68                         return VK_FRONT_FACE_LAST;
69         }
70 }
71
72 //! Returns true when the image passes the verification.
73 bool verifyResultImage (tcu::TestLog&                                           log,
74                                                 const tcu::ConstPixelBufferAccess       image,
75                                                 const TessPrimitiveType                         primitiveType,
76                                                 const Winding                                           winding,
77                                                 bool                                                            yFlip,
78                                                 const Winding                                           frontFaceWinding)
79 {
80         const int totalNumPixels        = image.getWidth()*image.getHeight();
81         const int badPixelTolerance = (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 5*de::max(image.getWidth(), image.getHeight()) : 0);
82
83         const tcu::Vec4 white = tcu::RGBA::white().toVec();
84         const tcu::Vec4 red   = tcu::RGBA::red().toVec();
85
86         int numWhitePixels = 0;
87         int numRedPixels   = 0;
88         for (int y = 0; y < image.getHeight();  y++)
89         for (int x = 0; x < image.getWidth();   x++)
90         {
91                 numWhitePixels += image.getPixel(x, y) == white ? 1 : 0;
92                 numRedPixels   += image.getPixel(x, y) == red   ? 1 : 0;
93         }
94
95         DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels);
96
97         log << tcu::TestLog::Message << "Note: got " << numWhitePixels << " white and " << numRedPixels << " red pixels" << tcu::TestLog::EndMessage;
98
99         const int otherPixels = totalNumPixels - numWhitePixels - numRedPixels;
100         if (otherPixels > badPixelTolerance)
101         {
102                 log << tcu::TestLog::Message
103                         << "Failure: Got " << otherPixels << " other than white or red pixels (maximum tolerance " << badPixelTolerance << ")"
104                         << tcu::TestLog::EndMessage;
105                 return false;
106         }
107
108         if ((frontFaceWinding == winding) != yFlip)
109         {
110                 if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
111                 {
112                         if (de::abs(numWhitePixels - totalNumPixels/2) > badPixelTolerance)
113                         {
114                                 log << tcu::TestLog::Message << "Failure: wrong number of white pixels; expected approximately " << totalNumPixels/2 << tcu::TestLog::EndMessage;
115                                 return false;
116                         }
117
118                         // Check number of filled pixels (from left) in top and bottom rows to
119                         // determine if triangle is in right orientation.
120                         {
121                                 const tcu::IVec2        expectedStart   (0, 1);
122                                 const tcu::IVec2        expectedEnd             (image.getWidth()-1, image.getWidth());
123                                 const tcu::IVec2        expectedTop             = yFlip ? expectedStart : expectedEnd;
124                                 const tcu::IVec2        expectedBottom  = yFlip ? expectedEnd : expectedStart;
125                                 int                                     numTopFilled    = 0;
126                                 int                                     numBottomFilled = 0;
127
128                                 for (int x = 0; x < image.getWidth(); ++x)
129                                 {
130                                         if (image.getPixel(x, 0) == white)
131                                                 numTopFilled += 1;
132                                         else
133                                                 break;
134                                 }
135
136                                 for (int x = 0; x < image.getWidth(); ++x)
137                                 {
138                                         if (image.getPixel(x, image.getHeight()-1) == white)
139                                                 numBottomFilled += 1;
140                                         else
141                                                 break;
142                                 }
143
144                                 if (!de::inBounds(numTopFilled, expectedTop[0], expectedTop[1]) ||
145                                         !de::inBounds(numBottomFilled, expectedBottom[0], expectedBottom[1]))
146                                 {
147                                         log << tcu::TestLog::Message << "Failure: triangle orientation is incorrect" << tcu::TestLog::EndMessage;
148                                         return false;
149                                 }
150                         }
151
152                 }
153                 else if (primitiveType == TESSPRIMITIVETYPE_QUADS)
154                 {
155                         if (numWhitePixels != totalNumPixels)
156                         {
157                                 log << tcu::TestLog::Message << "Failure: expected only white pixels (full-viewport quad)" << tcu::TestLog::EndMessage;
158                                 return false;
159                         }
160                 }
161                 else
162                         DE_ASSERT(false);
163         }
164         else
165         {
166                 if (numWhitePixels != 0)
167                 {
168                         log << tcu::TestLog::Message << "Failure: expected only red pixels (everything culled)" << tcu::TestLog::EndMessage;
169                         return false;
170                 }
171         }
172
173         return true;
174 }
175
176 class WindingTest : public TestCase
177 {
178 public:
179                                                                 WindingTest             (tcu::TestContext&                      testCtx,
180                                                                                                  const TessPrimitiveType        primitiveType,
181                                                                                                  const Winding                          winding,
182                                                                                                  bool                                           yFlip);
183
184         void                                            initPrograms    (SourceCollections&                     programCollection) const;
185         TestInstance*                           createInstance  (Context&                                       context) const;
186
187 private:
188         const TessPrimitiveType         m_primitiveType;
189         const Winding                           m_winding;
190         const bool                                      m_yFlip;
191 };
192
193 WindingTest::WindingTest (tcu::TestContext&                     testCtx,
194                                                   const TessPrimitiveType       primitiveType,
195                                                   const Winding                         winding,
196                                                   bool                                          yFlip)
197         : TestCase                      (testCtx, getCaseName(primitiveType, winding, yFlip), "")
198         , m_primitiveType       (primitiveType)
199         , m_winding                     (winding)
200         , m_yFlip                       (yFlip)
201 {
202 }
203
204 void WindingTest::initPrograms (SourceCollections& programCollection) const
205 {
206         // Vertex shader - no inputs
207         {
208                 std::ostringstream src;
209                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
210                         << "\n"
211                         << "void main (void)\n"
212                         << "{\n"
213                         << "}\n";
214
215                 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
216         }
217
218         // Tessellation control shader
219         {
220                 std::ostringstream src;
221                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
222                         << "#extension GL_EXT_tessellation_shader : require\n"
223                         << "\n"
224                         << "layout(vertices = 1) out;\n"
225                         << "\n"
226                         << "void main (void)\n"
227                         << "{\n"
228                         << "    gl_TessLevelInner[0] = 5.0;\n"
229                         << "    gl_TessLevelInner[1] = 5.0;\n"
230                         << "\n"
231                         << "    gl_TessLevelOuter[0] = 5.0;\n"
232                         << "    gl_TessLevelOuter[1] = 5.0;\n"
233                         << "    gl_TessLevelOuter[2] = 5.0;\n"
234                         << "    gl_TessLevelOuter[3] = 5.0;\n"
235                         << "}\n";
236
237                 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
238         }
239
240         // Tessellation evaluation shader
241         {
242                 std::ostringstream src;
243                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
244                         << "#extension GL_EXT_tessellation_shader : require\n"
245                         << "\n"
246                         << "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ", "
247                                                  << getWindingShaderName(m_winding) << ") in;\n"
248                         << "\n"
249                         << "void main (void)\n"
250                         << "{\n"
251                         << "    gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n"
252                         << "}\n";
253
254                 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
255         }
256
257         // Fragment shader
258         {
259                 std::ostringstream src;
260                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
261                         << "\n"
262                         << "layout(location = 0) out mediump vec4 o_color;\n"
263                         << "\n"
264                         << "void main (void)\n"
265                         << "{\n"
266                         << "    o_color = vec4(1.0);\n"
267                         << "}\n";
268
269                 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
270         }
271 }
272
273 class WindingTestInstance : public TestInstance
274 {
275 public:
276                                                                 WindingTestInstance (Context&                                   context,
277                                                                                                          const TessPrimitiveType        primitiveType,
278                                                                                                          const Winding                          winding,
279                                                                                                          bool                                           yFlip);
280
281         tcu::TestStatus                         iterate                         (void);
282
283 private:
284         const TessPrimitiveType         m_primitiveType;
285         const Winding                           m_winding;
286         const bool                                      m_yFlip;
287 };
288
289 WindingTestInstance::WindingTestInstance (Context&                                      context,
290                                                                                   const TessPrimitiveType       primitiveType,
291                                                                                   const Winding                         winding,
292                                                                                   bool                                          yFlip)
293         : TestInstance          (context)
294         , m_primitiveType       (primitiveType)
295         , m_winding                     (winding)
296         , m_yFlip                       (yFlip)
297 {
298 }
299
300 tcu::TestStatus WindingTestInstance::iterate (void)
301 {
302         if (m_yFlip && !de::contains(m_context.getDeviceExtensions().begin(), m_context.getDeviceExtensions().end(), "VK_KHR_maintenance1"))
303                 TCU_THROW(NotSupportedError, "Extension VK_KHR_maintenance1 not supported");
304
305         const DeviceInterface&  vk                                      = m_context.getDeviceInterface();
306         const VkDevice                  device                          = m_context.getDevice();
307         const VkQueue                   queue                           = m_context.getUniversalQueue();
308         const deUint32                  queueFamilyIndex        = m_context.getUniversalQueueFamilyIndex();
309         Allocator&                              allocator                       = m_context.getDefaultAllocator();
310
311         // Color attachment
312
313         const tcu::IVec2                          renderSize                             = tcu::IVec2(64, 64);
314         const VkFormat                            colorFormat                            = VK_FORMAT_R8G8B8A8_UNORM;
315         const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
316         const Image                                       colorAttachmentImage           (vk, device, allocator,
317                                                                                                                          makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
318                                                                                                                          MemoryRequirement::Any);
319
320         // Color output buffer: image will be copied here for verification
321
322         const VkDeviceSize      colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
323         const Buffer            colorBuffer                      (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
324
325         // Pipeline
326
327         const Unique<VkImageView>               colorAttachmentView(makeImageView                       (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
328         const Unique<VkRenderPass>              renderPass         (makeRenderPass                      (vk, device, colorFormat));
329         const Unique<VkFramebuffer>             framebuffer        (makeFramebuffer                     (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
330         const Unique<VkPipelineLayout>  pipelineLayout     (makePipelineLayoutWithoutDescriptors(vk, device));
331
332         const VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT;
333
334         // Front face is static state, so we have to create two pipelines.
335
336         const Unique<VkPipeline> pipelineCounterClockwise(GraphicsPipelineBuilder()
337                 .setCullModeFlags(cullMode)
338                 .setFrontFace    (VK_FRONT_FACE_COUNTER_CLOCKWISE)
339                 .setShader               (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                  m_context.getBinaryCollection().get("vert"), DE_NULL)
340                 .setShader               (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,    m_context.getBinaryCollection().get("tesc"), DE_NULL)
341                 .setShader               (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
342                 .setShader               (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,                                m_context.getBinaryCollection().get("frag"), DE_NULL)
343                 .build                   (vk, device, *pipelineLayout, *renderPass));
344
345         const Unique<VkPipeline> pipelineClockwise(GraphicsPipelineBuilder()
346                 .setCullModeFlags(cullMode)
347                 .setFrontFace    (VK_FRONT_FACE_CLOCKWISE)
348                 .setShader               (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                  m_context.getBinaryCollection().get("vert"), DE_NULL)
349                 .setShader               (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,    m_context.getBinaryCollection().get("tesc"), DE_NULL)
350                 .setShader               (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
351                 .setShader               (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,                                m_context.getBinaryCollection().get("frag"), DE_NULL)
352                 .build                   (vk, device, *pipelineLayout, *renderPass));
353
354         const struct // not static
355         {
356                 Winding         frontFaceWinding;
357                 VkPipeline      pipeline;
358         } testCases[] =
359         {
360                 { WINDING_CCW,  *pipelineCounterClockwise },
361                 { WINDING_CW,   *pipelineClockwise                },
362         };
363
364         tcu::TestLog& log = m_context.getTestContext().getLog();
365         log << tcu::TestLog::Message << "Pipeline uses " << getCullModeFlagsStr(cullMode) << tcu::TestLog::EndMessage;
366
367         bool success = true;
368
369         // Draw commands
370
371         const Unique<VkCommandPool>   cmdPool  (makeCommandPool  (vk, device, queueFamilyIndex));
372         const Unique<VkCommandBuffer> cmdBuffer(allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
373
374         for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(testCases); ++caseNdx)
375         {
376                 const Winding frontFaceWinding = testCases[caseNdx].frontFaceWinding;
377
378                 log << tcu::TestLog::Message << "Setting " << getFrontFaceName(mapFrontFace(frontFaceWinding)) << tcu::TestLog::EndMessage;
379
380                 // Reset the command buffer and begin.
381                 beginCommandBuffer(vk, *cmdBuffer);
382
383                 // Change color attachment image layout
384                 {
385                         // State is slightly different on the first iteration.
386                         const VkImageLayout currentLayout = (caseNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
387                         const VkAccessFlags srcFlags      = (caseNdx == 0 ? (VkAccessFlags)0          : (VkAccessFlags)VK_ACCESS_TRANSFER_READ_BIT);
388
389                         const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
390                                 srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
391                                 currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
392                                 *colorAttachmentImage, colorImageSubresourceRange);
393
394                         vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
395                                 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
396                 }
397
398                 // Begin render pass
399                 {
400                         const VkRect2D renderArea = {
401                                 makeOffset2D(0, 0),
402                                 makeExtent2D(renderSize.x(), renderSize.y()),
403                         };
404                         const tcu::Vec4 clearColor = tcu::RGBA::red().toVec();
405
406                         beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
407                 }
408
409                 const VkViewport viewport =
410                 {
411                         0.0f,                                                                                                                   // float        x;
412                         m_yFlip ? static_cast<float>(renderSize.y()) : 0.0f,                    // float        y;
413                         static_cast<float>(renderSize.x()),                                                             // float        width;
414                         static_cast<float>(m_yFlip ? -renderSize.y() : renderSize.y()), // float        height;
415                         0.0f,                                                                                                                   // float        minDepth;
416                         1.0f,                                                                                                                   // float        maxDepth;
417                 };
418                 vk.cmdSetViewport(*cmdBuffer, 0, 1, &viewport);
419
420                 const VkRect2D scissor =
421                 {
422                         makeOffset2D(0, 0),
423                         makeExtent2D(renderSize.x(), renderSize.y()),
424                 };
425                 vk.cmdSetScissor(*cmdBuffer, 0, 1, &scissor);
426
427                 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, testCases[caseNdx].pipeline);
428
429                 // Process a single abstract vertex.
430                 vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
431                 endRenderPass(vk, *cmdBuffer);
432
433                 // Copy render result to a host-visible buffer
434                 {
435                         const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
436                                 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
437                                 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
438                                 *colorAttachmentImage, colorImageSubresourceRange);
439
440                         vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
441                                 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
442                 }
443                 {
444                         const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 1), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
445                         vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
446                 }
447                 {
448                         const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
449                                 VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
450
451                         vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
452                                 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
453                 }
454
455                 endCommandBuffer(vk, *cmdBuffer);
456                 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
457
458                 {
459                         // Log rendered image
460                         const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
461                         invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
462
463                         const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
464                         log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess);
465
466                         // Verify case result
467                         success = success && verifyResultImage(log, imagePixelAccess, m_primitiveType, m_winding, m_yFlip, frontFaceWinding);
468                 }
469         }  // for windingNdx
470
471         return (success ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
472 }
473
474 TestInstance* WindingTest::createInstance (Context& context) const
475 {
476         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
477
478         return new WindingTestInstance(context, m_primitiveType, m_winding, m_yFlip);
479 }
480
481 } // anonymous
482
483 //! These tests correspond to dEQP-GLES31.functional.tessellation.winding.*
484 tcu::TestCaseGroup* createWindingTests (tcu::TestContext& testCtx)
485 {
486         de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "winding", "Test the cw and ccw input layout qualifiers"));
487
488         static const TessPrimitiveType primitivesNoIsolines[] =
489         {
490                 TESSPRIMITIVETYPE_TRIANGLES,
491                 TESSPRIMITIVETYPE_QUADS,
492         };
493
494         for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx)
495         for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx)
496         {
497                 group->addChild(new WindingTest(testCtx, primitivesNoIsolines[primitiveTypeNdx], (Winding)windingNdx, false));
498                 group->addChild(new WindingTest(testCtx, primitivesNoIsolines[primitiveTypeNdx], (Winding)windingNdx, true));
499         }
500
501         return group.release();
502 }
503
504 } // tessellation
505 } // vkt