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 Common Edge Tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktTessellationCommonEdgeTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktTessellationUtil.hpp"
28 
29 #include "tcuTestLog.hpp"
30 #include "tcuTexture.hpp"
31 
32 #include "vkDefs.hpp"
33 #include "vkQueryUtil.hpp"
34 #include "vkBuilderUtil.hpp"
35 #include "vkImageUtil.hpp"
36 #include "vkBarrierUtil.hpp"
37 #include "vkTypeUtil.hpp"
38 #include "vkStrUtil.hpp"
39 #include "vkCmdUtil.hpp"
40 #include "vkObjUtil.hpp"
41 #include "vkBufferWithMemory.hpp"
42 #include "vkImageWithMemory.hpp"
43 
44 #include "deUniquePtr.hpp"
45 #include "deStringUtil.hpp"
46 
47 #include <string>
48 #include <vector>
49 
50 namespace vkt
51 {
52 namespace tessellation
53 {
54 
55 using namespace vk;
56 
57 namespace
58 {
59 
60 enum CaseType
61 {
62     CASETYPE_BASIC =
63         0, //!< Order patch vertices such that when two patches share a vertex, it's at the same index for both.
64     CASETYPE_PRECISE, //!< Vertex indices don't match like for CASETYPE_BASIC, but other measures are taken, using the 'precise' qualifier.
65 
66     CASETYPE_LAST
67 };
68 
69 struct CaseDefinition
70 {
71     TessPrimitiveType primitiveType;
72     SpacingMode spacingMode;
73     CaseType caseType;
74 };
75 
76 //! Check that a certain rectangle in the image contains no black pixels.
77 //! Returns true if an image successfully passess the verification.
verifyResult(tcu::TestLog & log,const tcu::ConstPixelBufferAccess image)78 bool verifyResult(tcu::TestLog &log, const tcu::ConstPixelBufferAccess image)
79 {
80     const int startX = static_cast<int>(0.15f * (float)image.getWidth());
81     const int endX   = static_cast<int>(0.85f * (float)image.getWidth());
82     const int startY = static_cast<int>(0.15f * (float)image.getHeight());
83     const int endY   = static_cast<int>(0.85f * (float)image.getHeight());
84 
85     for (int y = startY; y < endY; ++y)
86         for (int x = startX; x < endX; ++x)
87         {
88             const tcu::Vec4 pixel = image.getPixel(x, y);
89 
90             if (pixel.x() == 0 && pixel.y() == 0 && pixel.z() == 0)
91             {
92                 log << tcu::TestLog::Message << "Failure: there seem to be cracks in the rendered result"
93                     << tcu::TestLog::EndMessage << tcu::TestLog::Message
94                     << "Note: pixel with zero r, g and b channels found at " << tcu::IVec2(x, y)
95                     << tcu::TestLog::EndMessage;
96 
97                 return false;
98             }
99         }
100 
101     log << tcu::TestLog::Message << "Success: there seem to be no cracks in the rendered result"
102         << tcu::TestLog::EndMessage;
103 
104     return true;
105 }
106 
initPrograms(vk::SourceCollections & programCollection,const CaseDefinition caseDef)107 void initPrograms(vk::SourceCollections &programCollection, const CaseDefinition caseDef)
108 {
109     DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
110 
111     // Vertex shader
112     {
113         std::ostringstream src;
114         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
115             << "\n"
116             << "layout(location = 0) in highp vec2  in_v_position;\n"
117             << "layout(location = 1) in highp float in_v_tessParam;\n"
118             << "\n"
119             << "layout(location = 0) out highp vec2  in_tc_position;\n"
120             << "layout(location = 1) out highp float in_tc_tessParam;\n"
121             << "\n"
122             << "void main (void)\n"
123             << "{\n"
124             << "    in_tc_position = in_v_position;\n"
125             << "    in_tc_tessParam = in_v_tessParam;\n"
126             << "}\n";
127 
128         programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
129     }
130 
131     // Tessellation control shader
132     {
133         const int numVertices = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
134 
135         std::ostringstream src;
136         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
137             << "#extension GL_EXT_tessellation_shader : require\n"
138             << (caseDef.caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "") << "\n"
139             << "layout(vertices = " << numVertices << ") out;\n"
140             << "\n"
141             << "layout(location = 0) in highp vec2  in_tc_position[];\n"
142             << "layout(location = 1) in highp float in_tc_tessParam[];\n"
143             << "\n"
144             << "layout(location = 0) out highp vec2 in_te_position[];\n"
145             << "\n"
146             << (caseDef.caseType == CASETYPE_PRECISE ? "precise gl_TessLevelOuter;\n\n" : "") << "void main (void)\n"
147             << "{\n"
148             << "    in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
149             << "\n"
150             << "    gl_TessLevelInner[0] = 5.0;\n"
151             << "    gl_TessLevelInner[1] = 5.0;\n"
152             << "\n"
153             << (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
154                     "    gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[2]);\n"
155                     "    gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[0]);\n"
156                     "    gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[1]);\n" :
157                 caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ?
158                     "    gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[2]);\n"
159                     "    gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[0]);\n"
160                     "    gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[3] + in_tc_tessParam[1]);\n"
161                     "    gl_TessLevelOuter[3] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[3]);\n" :
162                     "")
163             << "}\n";
164 
165         programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
166     }
167 
168     // Tessellation evaluation shader
169     {
170         std::ostringstream primitiveSpecificCode;
171         if (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
172             primitiveSpecificCode << "    highp vec2 pos = gl_TessCoord.x*in_te_position[0] + "
173                                      "gl_TessCoord.y*in_te_position[1] + gl_TessCoord.z*in_te_position[2];\n"
174                                   << "\n"
175                                   << "    highp float f = sqrt(3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, "
176                                      "gl_TessCoord.z))) * 0.5 + 0.5;\n"
177                                   << "    in_f_color = vec4(gl_TessCoord*f, 1.0);\n";
178         else if (caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
179             primitiveSpecificCode
180                 << (caseDef.caseType == CASETYPE_BASIC ?
181                         "    highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0]\n"
182                         "                   + (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1]\n"
183                         "                   + (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[2]\n"
184                         "                   + (    gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[3];\n" :
185                     caseDef.caseType == CASETYPE_PRECISE ?
186                         "    highp vec2 a = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0];\n"
187                         "    highp vec2 b = (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1];\n"
188                         "    highp vec2 c = (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[2];\n"
189                         "    highp vec2 d = (    gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[3];\n"
190                         "    highp vec2 pos = a+b+c+d;\n" :
191                         "")
192                 << "\n"
193                 << "    highp float f = sqrt(1.0 - 2.0 * max(abs(gl_TessCoord.x - 0.5), abs(gl_TessCoord.y - "
194                    "0.5)))*0.5 + 0.5;\n"
195                 << "    in_f_color = vec4(0.1, gl_TessCoord.xy*f, 1.0);\n";
196 
197         std::ostringstream src;
198         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
199             << "#extension GL_EXT_tessellation_shader : require\n"
200             << (caseDef.caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "") << "\n"
201             << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
202             << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
203             << "\n"
204             << "layout(location = 0) in highp vec2 in_te_position[];\n"
205             << "\n"
206             << "layout(location = 0) out mediump vec4 in_f_color;\n"
207             << "\n"
208             << (caseDef.caseType == CASETYPE_PRECISE ? "precise gl_Position;\n\n" : "") << "void main (void)\n"
209             << "{\n"
210             << primitiveSpecificCode.str() << "\n"
211             << "    // Offset the position slightly, based on the parity of the bits in the float representation.\n"
212             << "    // This is done to detect possible small differences in edge vertex positions between patches.\n"
213             << "    uvec2 bits = floatBitsToUint(pos);\n"
214             << "    uint numBits = 0u;\n"
215             << "    for (uint i = 0u; i < 32u; i++)\n"
216             << "        numBits += ((bits[0] >> i) & 1u) + ((bits[1] >> i) & 1u);\n"
217             << "    pos += float(numBits&1u)*0.04;\n"
218             << "\n"
219             << "    gl_Position = vec4(pos, 0.0, 1.0);\n"
220             << "}\n";
221 
222         programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
223     }
224 
225     // Fragment shader
226     {
227         std::ostringstream src;
228         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
229             << "\n"
230             << "layout(location = 0) in mediump vec4 in_f_color;\n"
231             << "\n"
232             << "layout(location = 0) out mediump vec4 o_color;\n"
233             << "\n"
234             << "void main (void)\n"
235             << "{\n"
236             << "    o_color = in_f_color;\n"
237             << "}\n";
238 
239         programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
240     }
241 }
242 
243 //! Generic test code used by all test cases.
test(Context & context,const CaseDefinition caseDef)244 tcu::TestStatus test(Context &context, const CaseDefinition caseDef)
245 {
246     DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
247     DE_ASSERT(caseDef.caseType == CASETYPE_BASIC || caseDef.caseType == CASETYPE_PRECISE);
248 
249     requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
250 
251     const DeviceInterface &vk       = context.getDeviceInterface();
252     const VkDevice device           = context.getDevice();
253     const VkQueue queue             = context.getUniversalQueue();
254     const uint32_t queueFamilyIndex = context.getUniversalQueueFamilyIndex();
255     Allocator &allocator            = context.getDefaultAllocator();
256 
257     // Prepare test data
258 
259     std::vector<float> gridPosComps;
260     std::vector<float> gridTessParams;
261     std::vector<uint16_t> gridIndices;
262 
263     {
264         const int gridWidth   = 4;
265         const int gridHeight  = 4;
266         const int numVertices = (gridWidth + 1) * (gridHeight + 1);
267         const int numIndices =
268             gridWidth * gridHeight * (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 * 2 : 4);
269         const int numPosCompsPerVertex = 2;
270         const int totalNumPosComps     = numPosCompsPerVertex * numVertices;
271 
272         gridPosComps.reserve(totalNumPosComps);
273         gridTessParams.reserve(numVertices);
274         gridIndices.reserve(numIndices);
275 
276         {
277             for (int i = 0; i < gridHeight + 1; ++i)
278                 for (int j = 0; j < gridWidth + 1; ++j)
279                 {
280                     gridPosComps.push_back(-1.0f + 2.0f * ((float)j + 0.5f) / (float)(gridWidth + 1));
281                     gridPosComps.push_back(-1.0f + 2.0f * ((float)i + 0.5f) / (float)(gridHeight + 1));
282                     gridTessParams.push_back((float)(i * (gridWidth + 1) + j) / (float)(numVertices - 1));
283                 }
284         }
285 
286         // Generate patch vertex indices.
287         // \note If CASETYPE_BASIC, the vertices are ordered such that when multiple
288         //         triangles/quads share a vertex, it's at the same index for everyone.
289 
290         if (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
291         {
292             for (int i = 0; i < gridHeight; i++)
293                 for (int j = 0; j < gridWidth; j++)
294                 {
295                     const uint16_t corners[4] = {
296                         (uint16_t)((i + 0) * (gridWidth + 1) + j + 0), (uint16_t)((i + 0) * (gridWidth + 1) + j + 1),
297                         (uint16_t)((i + 1) * (gridWidth + 1) + j + 0), (uint16_t)((i + 1) * (gridWidth + 1) + j + 1)};
298 
299                     const int secondTriangleVertexIndexOffset = caseDef.caseType == CASETYPE_BASIC ? 0 : 1;
300 
301                     for (int k = 0; k < 3; k++)
302                         gridIndices.push_back(corners[(k + 0 + i + (2 - j % 3)) % 3]);
303                     for (int k = 0; k < 3; k++)
304                         gridIndices.push_back(
305                             corners[(k + 2 + i + (2 - j % 3) + secondTriangleVertexIndexOffset) % 3 + 1]);
306                 }
307         }
308         else if (caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
309         {
310             for (int i = 0; i < gridHeight; ++i)
311                 for (int j = 0; j < gridWidth; ++j)
312                 {
313                     for (int m = 0; m < 2; m++)
314                         for (int n = 0; n < 2; n++)
315                             gridIndices.push_back((uint16_t)((i + (i + m) % 2) * (gridWidth + 1) + j + (j + n) % 2));
316 
317                     if (caseDef.caseType == CASETYPE_PRECISE && (i + j) % 2 == 0)
318                         std::reverse(gridIndices.begin() + (gridIndices.size() - 4),
319                                      gridIndices.begin() + gridIndices.size());
320                 }
321         }
322         else
323             DE_ASSERT(false);
324 
325         DE_ASSERT(static_cast<int>(gridPosComps.size()) == totalNumPosComps);
326         DE_ASSERT(static_cast<int>(gridTessParams.size()) == numVertices);
327         DE_ASSERT(static_cast<int>(gridIndices.size()) == numIndices);
328     }
329 
330     // Vertex input buffer: we put both attributes and indices in here.
331 
332     const VkDeviceSize vertexDataSizeBytes =
333         sizeInBytes(gridPosComps) + sizeInBytes(gridTessParams) + sizeInBytes(gridIndices);
334     const std::size_t vertexPositionsOffset  = 0;
335     const std::size_t vertexTessParamsOffset = sizeInBytes(gridPosComps);
336     const std::size_t vertexIndicesOffset    = vertexTessParamsOffset + sizeInBytes(gridTessParams);
337 
338     const BufferWithMemory vertexBuffer(
339         vk, device, allocator,
340         makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT),
341         MemoryRequirement::HostVisible);
342 
343     {
344         const Allocation &alloc = vertexBuffer.getAllocation();
345         uint8_t *const pData    = static_cast<uint8_t *>(alloc.getHostPtr());
346 
347         deMemcpy(pData + vertexPositionsOffset, &gridPosComps[0], static_cast<std::size_t>(sizeInBytes(gridPosComps)));
348         deMemcpy(pData + vertexTessParamsOffset, &gridTessParams[0],
349                  static_cast<std::size_t>(sizeInBytes(gridTessParams)));
350         deMemcpy(pData + vertexIndicesOffset, &gridIndices[0], static_cast<std::size_t>(sizeInBytes(gridIndices)));
351 
352         flushAlloc(vk, device, alloc);
353         // No barrier needed, flushed memory is automatically visible
354     }
355 
356     // Color attachment
357 
358     const tcu::IVec2 renderSize = tcu::IVec2(256, 256);
359     const VkFormat colorFormat  = VK_FORMAT_R8G8B8A8_UNORM;
360     const VkImageSubresourceRange colorImageSubresourceRange =
361         makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
362     const ImageWithMemory colorAttachmentImage(
363         vk, device, allocator,
364         makeImageCreateInfo(renderSize, colorFormat,
365                             VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
366         MemoryRequirement::Any);
367 
368     // Color output buffer: image will be copied here for verification
369 
370     const VkDeviceSize colorBufferSizeBytes =
371         renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
372     const BufferWithMemory colorBuffer(vk, device, allocator,
373                                        makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT),
374                                        MemoryRequirement::HostVisible);
375 
376     // Pipeline
377 
378     const Unique<VkImageView> colorAttachmentView(makeImageView(
379         vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
380     const Unique<VkRenderPass> renderPass(makeRenderPass(vk, device, colorFormat));
381     const Unique<VkFramebuffer> framebuffer(
382         makeFramebuffer(vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y()));
383     const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
384     const Unique<VkCommandBuffer> cmdBuffer(
385         allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
386     const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device));
387 
388     const int inPatchSize = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
389     const Unique<VkPipeline> pipeline(
390         GraphicsPipelineBuilder()
391             .setRenderSize(renderSize)
392             .setPatchControlPoints(inPatchSize)
393             .addVertexBinding(makeVertexInputBindingDescription(0u, sizeof(tcu::Vec2), VK_VERTEX_INPUT_RATE_VERTEX))
394             .addVertexBinding(makeVertexInputBindingDescription(1u, sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX))
395             .addVertexAttribute(makeVertexInputAttributeDescription(0u, 0u, VK_FORMAT_R32G32_SFLOAT, 0u))
396             .addVertexAttribute(makeVertexInputAttributeDescription(1u, 1u, VK_FORMAT_R32_SFLOAT, 0u))
397             .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"), DE_NULL)
398             .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc"),
399                        DE_NULL)
400             .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
401                        context.getBinaryCollection().get("tese"), DE_NULL)
402             .setShader(vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"), DE_NULL)
403             .build(vk, device, *pipelineLayout, *renderPass));
404 
405     // Draw commands
406 
407     beginCommandBuffer(vk, *cmdBuffer);
408 
409     // Change color attachment image layout
410     {
411         const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
412             (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
413             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, *colorAttachmentImage, colorImageSubresourceRange);
414 
415         vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
416                               VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u,
417                               &colorAttachmentLayoutBarrier);
418     }
419 
420     // Begin render pass
421     {
422         const VkRect2D renderArea = makeRect2D(renderSize);
423         const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
424 
425         beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
426     }
427 
428     vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
429     {
430         const VkBuffer buffers[]     = {*vertexBuffer, *vertexBuffer};
431         const VkDeviceSize offsets[] = {
432             vertexPositionsOffset,
433             vertexTessParamsOffset,
434         };
435         vk.cmdBindVertexBuffers(*cmdBuffer, 0u, DE_LENGTH_OF_ARRAY(buffers), buffers, offsets);
436 
437         vk.cmdBindIndexBuffer(*cmdBuffer, *vertexBuffer, vertexIndicesOffset, VK_INDEX_TYPE_UINT16);
438     }
439 
440     vk.cmdDrawIndexed(*cmdBuffer, static_cast<uint32_t>(gridIndices.size()), 1u, 0u, 0, 0u);
441     endRenderPass(vk, *cmdBuffer);
442 
443     // Copy render result to a host-visible buffer
444     copyImageToBuffer(vk, *cmdBuffer, *colorAttachmentImage, *colorBuffer, renderSize);
445 
446     endCommandBuffer(vk, *cmdBuffer);
447     submitCommandsAndWait(vk, device, queue, *cmdBuffer);
448 
449     {
450         // Log the result image.
451 
452         const Allocation &colorBufferAlloc = colorBuffer.getAllocation();
453 
454         invalidateAlloc(vk, device, colorBufferAlloc);
455 
456         const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1,
457                                                            colorBufferAlloc.getHostPtr());
458         tcu::TestLog &log = context.getTestContext().getLog();
459 
460         log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess) << tcu::TestLog::Message
461             << "Note: coloring is done to clarify the positioning and orientation of the "
462             << (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "triangles" :
463                 caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS     ? "quads" :
464                                                                        "")
465             << "; the color of a vertex corresponds to the index of that vertex in the patch"
466             << tcu::TestLog::EndMessage;
467 
468         if (caseDef.caseType == CASETYPE_BASIC)
469             log << tcu::TestLog::Message
470                 << "Note: each shared vertex has the same index among the primitives it belongs to"
471                 << tcu::TestLog::EndMessage;
472         else if (caseDef.caseType == CASETYPE_PRECISE)
473             log << tcu::TestLog::Message << "Note: the 'precise' qualifier is used to avoid cracks between primitives"
474                 << tcu::TestLog::EndMessage;
475         else
476             DE_ASSERT(false);
477 
478         // Verify the result.
479         const bool ok = verifyResult(log, imagePixelAccess);
480         return (ok ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
481     }
482 }
483 
getCaseName(const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const CaseType caseType)484 std::string getCaseName(const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const CaseType caseType)
485 {
486     std::ostringstream str;
487     str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getSpacingModeShaderName(spacingMode)
488         << (caseType == CASETYPE_PRECISE ? "_precise" : "");
489     return str.str();
490 }
491 
492 } // namespace
493 
494 //! These tests correspond to dEQP-GLES31.functional.tessellation.common_edge.*
createCommonEdgeTests(tcu::TestContext & testCtx)495 tcu::TestCaseGroup *createCommonEdgeTests(tcu::TestContext &testCtx)
496 {
497     // Draw multiple adjacent shapes and check that no cracks appear between them
498     de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "common_edge"));
499 
500     static const TessPrimitiveType primitiveTypes[] = {
501         TESSPRIMITIVETYPE_TRIANGLES,
502         TESSPRIMITIVETYPE_QUADS,
503     };
504 
505     for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx)
506         for (int caseTypeNdx = 0; caseTypeNdx < CASETYPE_LAST; ++caseTypeNdx)
507             for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
508             {
509                 const TessPrimitiveType primitiveType = primitiveTypes[primitiveTypeNdx];
510                 const CaseType caseType               = static_cast<CaseType>(caseTypeNdx);
511                 const SpacingMode spacingMode         = static_cast<SpacingMode>(spacingModeNdx);
512                 const CaseDefinition caseDef          = {primitiveType, spacingMode, caseType};
513 
514                 addFunctionCaseWithPrograms(group.get(), getCaseName(primitiveType, spacingMode, caseType),
515                                             initPrograms, test, caseDef);
516             }
517 
518     return group.release();
519 }
520 
521 } // namespace tessellation
522 } // namespace vkt
523