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 #include "vktTestGroupUtil.hpp"
29 
30 #include "tcuTestLog.hpp"
31 #include "tcuRGBA.hpp"
32 #include "tcuMaybe.hpp"
33 
34 #include "vkDefs.hpp"
35 #include "vkBarrierUtil.hpp"
36 #include "vkQueryUtil.hpp"
37 #include "vkBuilderUtil.hpp"
38 #include "vkImageUtil.hpp"
39 #include "vkTypeUtil.hpp"
40 #include "vkStrUtil.hpp"
41 #include "vkCmdUtil.hpp"
42 #include "vkObjUtil.hpp"
43 #include "vkBufferWithMemory.hpp"
44 #include "vkImageWithMemory.hpp"
45 
46 #include "deUniquePtr.hpp"
47 
48 namespace vkt
49 {
50 namespace tessellation
51 {
52 
53 using namespace vk;
54 
55 namespace
56 {
57 
getCaseName(const TessPrimitiveType primitiveType,const ShaderLanguage shaderLanguage,const Winding winding,bool yFlip)58 std::string getCaseName(const TessPrimitiveType primitiveType, const ShaderLanguage shaderLanguage,
59                         const Winding winding, bool yFlip)
60 {
61     std::ostringstream str;
62     str << getShaderLanguageName(shaderLanguage) << "_" << getTessPrimitiveTypeShaderName(primitiveType) << "_"
63         << getWindingShaderName(winding);
64     if (yFlip)
65         str << "_yflip";
66     return str.str();
67 }
68 
mapFrontFace(const Winding winding)69 inline VkFrontFace mapFrontFace(const Winding winding)
70 {
71     switch (winding)
72     {
73     case WINDING_CCW:
74         return VK_FRONT_FACE_COUNTER_CLOCKWISE;
75     case WINDING_CW:
76         return VK_FRONT_FACE_CLOCKWISE;
77     default:
78         DE_ASSERT(false);
79         return VK_FRONT_FACE_LAST;
80     }
81 }
82 
83 //! Returns true when the image passes the verification.
verifyResultImage(tcu::TestLog & log,const tcu::ConstPixelBufferAccess image,const TessPrimitiveType primitiveType,const VkTessellationDomainOrigin domainOrigin,const Winding winding,bool yFlip,const Winding frontFaceWinding)84 bool verifyResultImage(tcu::TestLog &log, const tcu::ConstPixelBufferAccess image,
85                        const TessPrimitiveType primitiveType, const VkTessellationDomainOrigin domainOrigin,
86                        const Winding winding, bool yFlip, const Winding frontFaceWinding)
87 {
88     const bool expectVisiblePrimitive =
89         ((frontFaceWinding == winding) == (domainOrigin == VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT)) != yFlip;
90 
91     const int totalNumPixels = image.getWidth() * image.getHeight();
92 
93     const tcu::Vec4 white = tcu::RGBA::white().toVec();
94     const tcu::Vec4 red   = tcu::RGBA::red().toVec();
95 
96     int numWhitePixels = 0;
97     int numRedPixels   = 0;
98 
99     // Count red and white pixels
100     for (int y = 0; y < image.getHeight(); y++)
101         for (int x = 0; x < image.getWidth(); x++)
102         {
103             numWhitePixels += image.getPixel(x, y) == white ? 1 : 0;
104             numRedPixels += image.getPixel(x, y) == red ? 1 : 0;
105         }
106 
107     DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels);
108 
109     log << tcu::TestLog::Message << "Note: got " << numWhitePixels << " white and " << numRedPixels << " red pixels"
110         << tcu::TestLog::EndMessage;
111 
112     {
113         const int otherPixels = totalNumPixels - numWhitePixels - numRedPixels;
114         if (otherPixels > 0)
115         {
116             log << tcu::TestLog::Message << "Failure: Got " << otherPixels << " other than white or red pixels"
117                 << tcu::TestLog::EndMessage;
118             return false;
119         }
120     }
121 
122     if (expectVisiblePrimitive)
123     {
124         if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
125         {
126             const int badPixelTolerance =
127                 (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 5 * de::max(image.getWidth(), image.getHeight()) : 0);
128 
129             if (de::abs(numWhitePixels - totalNumPixels / 2) > badPixelTolerance)
130             {
131                 log << tcu::TestLog::Message << "Failure: wrong number of white pixels; expected approximately "
132                     << totalNumPixels / 2 << tcu::TestLog::EndMessage;
133                 return false;
134             }
135 
136             // Check number of filled pixels (from left) in top and bottom rows to
137             // determine if triangle is in right orientation.
138             {
139                 const tcu::IVec2 expectedStart(0, 1);
140                 const tcu::IVec2 expectedEnd(image.getWidth() - 1, image.getWidth());
141                 const tcu::IVec2 expectedTop    = yFlip ? expectedStart : expectedEnd;
142                 const tcu::IVec2 expectedBottom = yFlip ? expectedEnd : expectedStart;
143                 int numTopFilled                = 0;
144                 int numBottomFilled             = 0;
145 
146                 for (int x = 0; x < image.getWidth(); ++x)
147                 {
148                     if (image.getPixel(x, 0) == white)
149                         numTopFilled += 1;
150                     else
151                         break;
152                 }
153 
154                 for (int x = 0; x < image.getWidth(); ++x)
155                 {
156                     if (image.getPixel(x, image.getHeight() - 1) == white)
157                         numBottomFilled += 1;
158                     else
159                         break;
160                 }
161 
162                 if (!de::inBounds(numTopFilled, expectedTop[0], expectedTop[1]) ||
163                     !de::inBounds(numBottomFilled, expectedBottom[0], expectedBottom[1]))
164                 {
165                     log << tcu::TestLog::Message << "Failure: triangle orientation is incorrect"
166                         << tcu::TestLog::EndMessage;
167                     return false;
168                 }
169             }
170         }
171         else if (primitiveType == TESSPRIMITIVETYPE_QUADS)
172         {
173             if (numWhitePixels != totalNumPixels)
174             {
175                 log << tcu::TestLog::Message << "Failure: expected only white pixels (full-viewport quad)"
176                     << tcu::TestLog::EndMessage;
177                 return false;
178             }
179         }
180         else
181             DE_ASSERT(false);
182     }
183     else
184     {
185         if (numWhitePixels != 0)
186         {
187             log << tcu::TestLog::Message << "Failure: expected only red pixels (everything culled)"
188                 << tcu::TestLog::EndMessage;
189             return false;
190         }
191     }
192 
193     return true;
194 }
195 
196 typedef tcu::Maybe<VkTessellationDomainOrigin> MaybeDomainOrigin;
197 
198 class WindingTest : public TestCase
199 {
200 public:
201     WindingTest(tcu::TestContext &testCtx, const TessPrimitiveType primitiveType, const MaybeDomainOrigin &domainOrigin,
202                 const ShaderLanguage shaderLanguage, const Winding winding, bool yFlip);
203 
204     void initPrograms(SourceCollections &programCollection) const;
205     TestInstance *createInstance(Context &context) const;
206 
207 private:
208     const TessPrimitiveType m_primitiveType;
209     const MaybeDomainOrigin m_domainOrigin;
210     const ShaderLanguage m_shaderLanguage;
211     const Winding m_winding;
212     const bool m_yFlip;
213 };
214 
WindingTest(tcu::TestContext & testCtx,const TessPrimitiveType primitiveType,const MaybeDomainOrigin & domainOrigin,const ShaderLanguage shaderLanguage,const Winding winding,bool yFlip)215 WindingTest::WindingTest(tcu::TestContext &testCtx, const TessPrimitiveType primitiveType,
216                          const MaybeDomainOrigin &domainOrigin, const ShaderLanguage shaderLanguage,
217                          const Winding winding, bool yFlip)
218     : TestCase(testCtx, getCaseName(primitiveType, shaderLanguage, winding, yFlip))
219     , m_primitiveType(primitiveType)
220     , m_domainOrigin(domainOrigin)
221     , m_shaderLanguage(shaderLanguage)
222     , m_winding(winding)
223     , m_yFlip(yFlip)
224 {
225 }
226 
initPrograms(SourceCollections & programCollection) const227 void WindingTest::initPrograms(SourceCollections &programCollection) const
228 {
229     if (m_shaderLanguage == SHADER_LANGUAGE_GLSL)
230     {
231         // Vertex shader - no inputs
232         {
233             std::ostringstream src;
234             src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
235                 << "\n"
236                 << "void main (void)\n"
237                 << "{\n"
238                 << "}\n";
239 
240             programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
241         }
242 
243         // Tessellation control shader
244         {
245             std::ostringstream src;
246             src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
247                 << "#extension GL_EXT_tessellation_shader : require\n"
248                 << "\n"
249                 << "layout(vertices = 1) out;\n"
250                 << "\n"
251                 << "void main (void)\n"
252                 << "{\n"
253                 << "    gl_TessLevelInner[0] = 5.0;\n"
254                 << "    gl_TessLevelInner[1] = 5.0;\n"
255                 << "\n"
256                 << "    gl_TessLevelOuter[0] = 5.0;\n"
257                 << "    gl_TessLevelOuter[1] = 5.0;\n"
258                 << "    gl_TessLevelOuter[2] = 5.0;\n"
259                 << "    gl_TessLevelOuter[3] = 5.0;\n"
260                 << "}\n";
261 
262             programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
263         }
264 
265         // Tessellation evaluation shader
266         {
267             std::ostringstream src;
268             src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
269                 << "#extension GL_EXT_tessellation_shader : require\n"
270                 << "\n"
271                 << "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ", "
272                 << getWindingShaderName(m_winding) << ") in;\n"
273                 << "\n"
274                 << "void main (void)\n"
275                 << "{\n"
276                 << "    gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n"
277                 << "}\n";
278 
279             programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
280         }
281 
282         // Fragment shader
283         {
284             std::ostringstream src;
285             src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
286                 << "\n"
287                 << "layout(location = 0) out mediump vec4 o_color;\n"
288                 << "\n"
289                 << "void main (void)\n"
290                 << "{\n"
291                 << "    o_color = vec4(1.0);\n"
292                 << "}\n";
293 
294             programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
295         }
296     }
297     else
298     {
299         // Vertex shader - no inputs
300         {
301             std::ostringstream src;
302             src << "void main (void)\n"
303                 << "{\n"
304                 << "}\n";
305 
306             programCollection.hlslSources.add("vert") << glu::VertexSource(src.str());
307         }
308 
309         // Tessellation control shader
310         {
311             std::ostringstream src;
312             src << "struct HS_CONSTANT_OUT\n"
313                 << "{\n"
314                 << "    float tessLevelsOuter[4] : SV_TessFactor;\n"
315                 << "    float tessLevelsInner[2] : SV_InsideTessFactor;\n"
316                 << "};\n"
317                 << "\n"
318                 << "[domain(\"" << getDomainName(m_primitiveType) << "\")]\n"
319                 << "[partitioning(\"integer\")]\n"
320                 << "[outputtopology(\"" << getOutputTopologyName(m_primitiveType, m_winding, false) << "\")]\n"
321                 << "[outputcontrolpoints(1)]\n"
322                 << "[patchconstantfunc(\"PCF\")]\n"
323                 << "void main()\n"
324                 << "{\n"
325                 << "}\n"
326                 << "\n"
327                 << "HS_CONSTANT_OUT PCF()\n"
328                 << "{\n"
329                 << "    HS_CONSTANT_OUT output;\n"
330                 << "    output.tessLevelsInner[0] = 5.0;\n"
331                 << "    output.tessLevelsInner[1] = 5.0;\n"
332                 << "    output.tessLevelsOuter[0] = 5.0;\n"
333                 << "    output.tessLevelsOuter[1] = 5.0;\n"
334                 << "    output.tessLevelsOuter[2] = 5.0;\n"
335                 << "    output.tessLevelsOuter[3] = 5.0;\n"
336                 << "    return output;\n"
337                 << "}\n";
338 
339             programCollection.hlslSources.add("tesc") << glu::TessellationControlSource(src.str());
340         }
341 
342         // Tessellation evaluation shader
343         {
344             std::ostringstream src;
345 
346             src << "float4 main(" << (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "float3" : "float2")
347                 << " tessCoords : SV_DOMAINLOCATION) : SV_POSITION\n"
348                 << "{\n"
349                 << "    return float4(tessCoords.xy*2.0 - 1, 0.0, 1.0);\n"
350                 << "}\n";
351 
352             programCollection.hlslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
353         }
354 
355         // Fragment shader
356         {
357             std::ostringstream src;
358             src << "float4 main (void) : COLOR0\n"
359                 << "{\n"
360                 << "    return float4(1.0);\n"
361                 << "}\n";
362 
363             programCollection.hlslSources.add("frag") << glu::FragmentSource(src.str());
364         }
365     }
366 }
367 
368 class WindingTestInstance : public TestInstance
369 {
370 public:
371     WindingTestInstance(Context &context, const TessPrimitiveType primitiveType, const MaybeDomainOrigin &domainOrigin,
372                         const Winding winding, bool yFlip);
373 
374     tcu::TestStatus iterate(void);
375 
376 private:
377     void requireExtension(const char *name) const;
378 
379     const TessPrimitiveType m_primitiveType;
380     const MaybeDomainOrigin m_domainOrigin;
381     const Winding m_winding;
382     const bool m_yFlip;
383 };
384 
WindingTestInstance(Context & context,const TessPrimitiveType primitiveType,const MaybeDomainOrigin & domainOrigin,const Winding winding,bool yFlip)385 WindingTestInstance::WindingTestInstance(Context &context, const TessPrimitiveType primitiveType,
386                                          const MaybeDomainOrigin &domainOrigin, const Winding winding, bool yFlip)
387     : TestInstance(context)
388     , m_primitiveType(primitiveType)
389     , m_domainOrigin(domainOrigin)
390     , m_winding(winding)
391     , m_yFlip(yFlip)
392 {
393     if (m_yFlip)
394         requireExtension("VK_KHR_maintenance1");
395 
396     if ((bool)m_domainOrigin)
397         requireExtension("VK_KHR_maintenance2");
398 }
399 
requireExtension(const char * name) const400 void WindingTestInstance::requireExtension(const char *name) const
401 {
402     if (!m_context.isDeviceFunctionalitySupported(name))
403         TCU_THROW(NotSupportedError, (std::string(name) + " is not supported").c_str());
404 }
405 
iterate(void)406 tcu::TestStatus WindingTestInstance::iterate(void)
407 {
408     const DeviceInterface &vk       = m_context.getDeviceInterface();
409     const VkDevice device           = m_context.getDevice();
410     const VkQueue queue             = m_context.getUniversalQueue();
411     const uint32_t queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
412     Allocator &allocator            = m_context.getDefaultAllocator();
413 
414     // Color attachment
415 
416     const tcu::IVec2 renderSize = tcu::IVec2(64, 64);
417     const VkFormat colorFormat  = VK_FORMAT_R8G8B8A8_UNORM;
418     const VkImageSubresourceRange colorImageSubresourceRange =
419         makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
420     const ImageWithMemory colorAttachmentImage(
421         vk, device, allocator,
422         makeImageCreateInfo(renderSize, colorFormat,
423                             VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
424         MemoryRequirement::Any);
425 
426     // Color output buffer: image will be copied here for verification
427 
428     const VkDeviceSize colorBufferSizeBytes =
429         renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
430     const BufferWithMemory colorBuffer(vk, device, allocator,
431                                        makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT),
432                                        MemoryRequirement::HostVisible);
433 
434     // Pipeline
435 
436     const Unique<VkImageView> colorAttachmentView(makeImageView(
437         vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
438     const Unique<VkRenderPass> renderPass(makeRenderPass(vk, device, colorFormat));
439     const Unique<VkFramebuffer> framebuffer(
440         makeFramebuffer(vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y()));
441     const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device));
442 
443     const VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT;
444 
445     // Front face is static state, so we have to create two pipelines.
446 
447     const Unique<VkPipeline> pipelineCounterClockwise(
448         GraphicsPipelineBuilder()
449             .setCullModeFlags(cullMode)
450             .setFrontFace(VK_FRONT_FACE_COUNTER_CLOCKWISE)
451             .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
452             .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
453                        m_context.getBinaryCollection().get("tesc"), DE_NULL)
454             .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
455                        m_context.getBinaryCollection().get("tese"), DE_NULL)
456             .setShader(vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL)
457             .setTessellationDomainOrigin(m_domainOrigin)
458             .build(vk, device, *pipelineLayout, *renderPass));
459 
460     const Unique<VkPipeline> pipelineClockwise(
461         GraphicsPipelineBuilder()
462             .setCullModeFlags(cullMode)
463             .setFrontFace(VK_FRONT_FACE_CLOCKWISE)
464             .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
465             .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
466                        m_context.getBinaryCollection().get("tesc"), DE_NULL)
467             .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
468                        m_context.getBinaryCollection().get("tese"), DE_NULL)
469             .setShader(vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL)
470             .setTessellationDomainOrigin(m_domainOrigin)
471             .build(vk, device, *pipelineLayout, *renderPass));
472 
473     const struct // not static
474     {
475         Winding frontFaceWinding;
476         VkPipeline pipeline;
477     } testCases[] = {
478         {WINDING_CCW, *pipelineCounterClockwise},
479         {WINDING_CW, *pipelineClockwise},
480     };
481 
482     tcu::TestLog &log = m_context.getTestContext().getLog();
483     log << tcu::TestLog::Message << "Pipeline uses " << getCullModeFlagsStr(cullMode) << tcu::TestLog::EndMessage;
484 
485     bool success = true;
486 
487     // Draw commands
488 
489     const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
490     const Unique<VkCommandBuffer> cmdBuffer(
491         allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
492 
493     for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(testCases); ++caseNdx)
494     {
495         const Winding frontFaceWinding = testCases[caseNdx].frontFaceWinding;
496 
497         log << tcu::TestLog::Message << "Setting " << getFrontFaceName(mapFrontFace(frontFaceWinding))
498             << tcu::TestLog::EndMessage;
499 
500         // Reset the command buffer and begin.
501         beginCommandBuffer(vk, *cmdBuffer);
502 
503         // Change color attachment image layout
504         {
505             // State is slightly different on the first iteration.
506             const VkImageLayout currentLayout =
507                 (caseNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
508             const VkAccessFlags srcFlags =
509                 (caseNdx == 0 ? (VkAccessFlags)0 : (VkAccessFlags)VK_ACCESS_TRANSFER_READ_BIT);
510 
511             const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
512                 srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
513                 *colorAttachmentImage, colorImageSubresourceRange);
514 
515             vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
516                                   VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u,
517                                   &colorAttachmentLayoutBarrier);
518         }
519 
520         // Begin render pass
521         {
522             const VkRect2D renderArea  = makeRect2D(renderSize);
523             const tcu::Vec4 clearColor = tcu::RGBA::red().toVec();
524 
525             beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
526         }
527 
528         const VkViewport viewport = {
529             0.0f,                                                           // float x;
530             m_yFlip ? static_cast<float>(renderSize.y()) : 0.0f,            // float y;
531             static_cast<float>(renderSize.x()),                             // float width;
532             static_cast<float>(m_yFlip ? -renderSize.y() : renderSize.y()), // float height;
533             0.0f,                                                           // float minDepth;
534             1.0f,                                                           // float maxDepth;
535         };
536         vk.cmdSetViewport(*cmdBuffer, 0, 1, &viewport);
537 
538         const VkRect2D scissor = makeRect2D(renderSize);
539         vk.cmdSetScissor(*cmdBuffer, 0, 1, &scissor);
540 
541         vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, testCases[caseNdx].pipeline);
542 
543         // Process a single abstract vertex.
544         vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
545         endRenderPass(vk, *cmdBuffer);
546 
547         // Copy render result to a host-visible buffer
548         copyImageToBuffer(vk, *cmdBuffer, *colorAttachmentImage, *colorBuffer, renderSize);
549 
550         endCommandBuffer(vk, *cmdBuffer);
551         submitCommandsAndWait(vk, device, queue, *cmdBuffer);
552 
553         {
554             // Log rendered image
555             const Allocation &colorBufferAlloc = colorBuffer.getAllocation();
556 
557             invalidateAlloc(vk, device, colorBufferAlloc);
558 
559             const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(),
560                                                                1, colorBufferAlloc.getHostPtr());
561 
562             log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess);
563 
564             // Verify case result
565             success = verifyResultImage(log, imagePixelAccess, m_primitiveType,
566                                         !m_domainOrigin ? VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT : *m_domainOrigin,
567                                         m_winding, m_yFlip, frontFaceWinding) &&
568                       success;
569         }
570     } // for windingNdx
571 
572     return (success ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
573 }
574 
createInstance(Context & context) const575 TestInstance *WindingTest::createInstance(Context &context) const
576 {
577     requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
578 
579     return new WindingTestInstance(context, m_primitiveType, m_domainOrigin, m_winding, m_yFlip);
580 }
581 
populateWindingGroup(tcu::TestCaseGroup * group,tcu::Maybe<VkTessellationDomainOrigin> domainOrigin)582 void populateWindingGroup(tcu::TestCaseGroup *group, tcu::Maybe<VkTessellationDomainOrigin> domainOrigin)
583 {
584     static const TessPrimitiveType primitivesNoIsolines[] = {
585         TESSPRIMITIVETYPE_TRIANGLES,
586         TESSPRIMITIVETYPE_QUADS,
587     };
588 
589     static const ShaderLanguage shaderLanguage[] = {
590         SHADER_LANGUAGE_GLSL,
591         SHADER_LANGUAGE_HLSL,
592     };
593 
594     for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx)
595         for (int shaderLanguageNdx = 0; shaderLanguageNdx < DE_LENGTH_OF_ARRAY(shaderLanguage); ++shaderLanguageNdx)
596             for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx)
597             {
598                 group->addChild(new WindingTest(group->getTestContext(), primitivesNoIsolines[primitiveTypeNdx],
599                                                 domainOrigin, shaderLanguage[shaderLanguageNdx], (Winding)windingNdx,
600                                                 false));
601                 group->addChild(new WindingTest(group->getTestContext(), primitivesNoIsolines[primitiveTypeNdx],
602                                                 domainOrigin, shaderLanguage[shaderLanguageNdx], (Winding)windingNdx,
603                                                 true));
604             }
605 }
606 
607 } // namespace
608 
609 //! These tests correspond to dEQP-GLES31.functional.tessellation.winding.*
createWindingTests(tcu::TestContext & testCtx)610 tcu::TestCaseGroup *createWindingTests(tcu::TestContext &testCtx)
611 {
612     // Test the cw and ccw input layout qualifiers
613     de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "winding"));
614 
615     // No tessellation domain specified
616     addTestGroup(group.get(), "default_domain", populateWindingGroup, tcu::nothing<VkTessellationDomainOrigin>());
617     // Lower left tessellation domain
618     addTestGroup(group.get(), "lower_left_domain", populateWindingGroup,
619                  tcu::just(VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT));
620     // Upper left tessellation domain
621     addTestGroup(group.get(), "upper_left_domain", populateWindingGroup,
622                  tcu::just(VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT));
623 
624     return group.release();
625 }
626 
627 } // namespace tessellation
628 } // namespace vkt
629