Add tessellation shader tests (ported from ES 3.1)
[platform/upstream/VK-GL-CTS.git] / external / vulkancts / modules / vulkan / tessellation / vktTessellationPrimitiveDiscardTests.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 Primitive Discard Tests
23  *//*--------------------------------------------------------------------*/
24
25 #include "vktTessellationPrimitiveDiscardTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktTessellationUtil.hpp"
28
29 #include "tcuTestLog.hpp"
30
31 #include "vkDefs.hpp"
32 #include "vkQueryUtil.hpp"
33 #include "vkBuilderUtil.hpp"
34 #include "vkImageUtil.hpp"
35 #include "vkTypeUtil.hpp"
36
37 #include "deUniquePtr.hpp"
38 #include "deStringUtil.hpp"
39
40 #include <string>
41 #include <vector>
42
43 namespace vkt
44 {
45 namespace tessellation
46 {
47
48 using namespace vk;
49
50 namespace
51 {
52
53 struct CaseDefinition
54 {
55         TessPrimitiveType       primitiveType;
56         SpacingMode                     spacingMode;
57         Winding                         winding;
58         bool                            usePointMode;
59 };
60
61 int intPow (int base, int exp)
62 {
63         DE_ASSERT(exp >= 0);
64         if (exp == 0)
65                 return 1;
66         else
67         {
68                 const int sub = intPow(base, exp/2);
69                 if (exp % 2 == 0)
70                         return sub*sub;
71                 else
72                         return sub*sub*base;
73         }
74 }
75
76 std::vector<float> genAttributes (void)
77 {
78         // Generate input attributes (tessellation levels, and position scale and
79         // offset) for a number of primitives. Each primitive has a different
80         // combination of tessellatio levels; each level is either a valid
81         // value or an "invalid" value (negative or zero, chosen from
82         // invalidTessLevelChoices).
83
84         // \note The attributes are generated in such an order that all of the
85         //               valid attribute tuples come before the first invalid one both
86         //               in the result vector, and when scanning the resulting 2d grid
87         //               of primitives is scanned in y-major order. This makes
88         //               verification somewhat simpler.
89
90         static const float      baseTessLevels[6]                       = { 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
91         static const float      invalidTessLevelChoices[]       = { -0.42f, 0.0f };
92         const int                       numChoices                                      = 1 + DE_LENGTH_OF_ARRAY(invalidTessLevelChoices);
93         float                           choices[6][numChoices];
94         std::vector<float>      result;
95
96         for (int levelNdx = 0; levelNdx < 6; levelNdx++)
97                 for (int choiceNdx = 0; choiceNdx < numChoices; choiceNdx++)
98                         choices[levelNdx][choiceNdx] = choiceNdx == 0 ? baseTessLevels[levelNdx] : invalidTessLevelChoices[choiceNdx-1];
99
100         {
101                 const int       numCols = intPow(numChoices, 6/2); // sqrt(numChoices**6) == sqrt(number of primitives)
102                 const int       numRows = numCols;
103                 int                     index   = 0;
104                 int                     i[6];
105                 // We could do this with some generic combination-generation function, but meh, it's not that bad.
106                 for (i[2] = 0; i[2] < numChoices; i[2]++) // First  outer
107                 for (i[3] = 0; i[3] < numChoices; i[3]++) // Second outer
108                 for (i[4] = 0; i[4] < numChoices; i[4]++) // Third  outer
109                 for (i[5] = 0; i[5] < numChoices; i[5]++) // Fourth outer
110                 for (i[0] = 0; i[0] < numChoices; i[0]++) // First  inner
111                 for (i[1] = 0; i[1] < numChoices; i[1]++) // Second inner
112                 {
113                         for (int j = 0; j < 6; j++)
114                                 result.push_back(choices[j][i[j]]);
115
116                         {
117                                 const int col = index % numCols;
118                                 const int row = index / numCols;
119                                 // Position scale.
120                                 result.push_back((float)2.0f / (float)numCols);
121                                 result.push_back((float)2.0f / (float)numRows);
122                                 // Position offset.
123                                 result.push_back((float)col / (float)numCols * 2.0f - 1.0f);
124                                 result.push_back((float)row / (float)numRows * 2.0f - 1.0f);
125                         }
126
127                         index++;
128                 }
129         }
130
131         return result;
132 }
133
134 //! Check that white pixels are found around every non-discarded patch,
135 //! and that only black pixels are found after the last non-discarded patch.
136 //! Returns true on successful comparison.
137 bool verifyResultImage (tcu::TestLog&                                           log,
138                                                 const int                                                       numPrimitives,
139                                                 const int                                                       numAttribsPerPrimitive,
140                                                 const TessPrimitiveType                         primitiveType,
141                                                 const std::vector<float>&                       attributes,
142                                                 const tcu::ConstPixelBufferAccess       pixels)
143 {
144         const tcu::Vec4 black(0.0f, 0.0f, 0.0f, 1.0f);
145         const tcu::Vec4 white(1.0f, 1.0f, 1.0f, 1.0f);
146
147         int lastWhitePixelRow                                                           = 0;
148         int secondToLastWhitePixelRow                                           = 0;
149         int     lastWhitePixelColumnOnSecondToLastWhitePixelRow = 0;
150
151         for (int patchNdx = 0; patchNdx < numPrimitives; ++patchNdx)
152         {
153                 const float* const      attr                    = &attributes[numAttribsPerPrimitive*patchNdx];
154                 const bool                      validLevels             = !isPatchDiscarded(primitiveType, &attr[2]);
155
156                 if (validLevels)
157                 {
158                         // Not a discarded patch; check that at least one white pixel is found in its area.
159
160                         const float* const      scale           = &attr[6];
161                         const float* const      offset          = &attr[8];
162                         const int                       x0                      = (int)((                       offset[0] + 1.0f) * 0.5f * (float)pixels.getWidth()) - 1;
163                         const int                       x1                      = (int)((scale[0] + offset[0] + 1.0f) * 0.5f * (float)pixels.getWidth()) + 1;
164                         const int                       y0                      = (int)((                       offset[1] + 1.0f) * 0.5f * (float)pixels.getHeight()) - 1;
165                         const int                       y1                      = (int)((scale[1] + offset[1] + 1.0f) * 0.5f * (float)pixels.getHeight()) + 1;
166                         bool                            pixelOk         = false;
167
168                         if (y1 > lastWhitePixelRow)
169                         {
170                                 secondToLastWhitePixelRow       = lastWhitePixelRow;
171                                 lastWhitePixelRow                       = y1;
172                         }
173                         lastWhitePixelColumnOnSecondToLastWhitePixelRow = x1;
174
175                         for (int y = y0; y <= y1 && !pixelOk; y++)
176                         for (int x = x0; x <= x1 && !pixelOk; x++)
177                         {
178                                 if (!de::inBounds(x, 0, pixels.getWidth()) || !de::inBounds(y, 0, pixels.getHeight()))
179                                         continue;
180
181                                 if (pixels.getPixel(x, y) == white)
182                                         pixelOk = true;
183                         }
184
185                         if (!pixelOk)
186                         {
187                                 log << tcu::TestLog::Message
188                                         << "Failure: expected at least one white pixel in the rectangle "
189                                         << "[x0=" << x0 << ", y0=" << y0 << ", x1=" << x1 << ", y1=" << y1 << "]"
190                                         << tcu::TestLog::EndMessage
191                                         << tcu::TestLog::Message
192                                         << "Note: the rectangle approximately corresponds to the patch with these tessellation levels: "
193                                         << getTessellationLevelsString(&attr[0], &attr[1])
194                                         << tcu::TestLog::EndMessage;
195
196                                 return false;
197                         }
198                 }
199                 else
200                 {
201                         // First discarded primitive patch; the remaining are guaranteed to be discarded ones as well.
202
203                         for (int y = 0; y < pixels.getHeight(); y++)
204                         for (int x = 0; x < pixels.getWidth(); x++)
205                         {
206                                 if (y > lastWhitePixelRow || (y > secondToLastWhitePixelRow && x > lastWhitePixelColumnOnSecondToLastWhitePixelRow))
207                                 {
208                                         if (pixels.getPixel(x, y) != black)
209                                         {
210                                                 log << tcu::TestLog::Message
211                                                         << "Failure: expected all pixels to be black in the area "
212                                                         << (lastWhitePixelColumnOnSecondToLastWhitePixelRow < pixels.getWidth()-1
213                                                                 ? std::string() + "y > " + de::toString(lastWhitePixelRow) + " || (y > " + de::toString(secondToLastWhitePixelRow)
214                                                                                                 + " && x > " + de::toString(lastWhitePixelColumnOnSecondToLastWhitePixelRow) + ")"
215                                                                 : std::string() + "y > " + de::toString(lastWhitePixelRow))
216                                                         << " (they all correspond to patches that should be discarded)"
217                                                         << tcu::TestLog::EndMessage
218                                                         << tcu::TestLog::Message << "Note: pixel " << tcu::IVec2(x, y) << " isn't black" << tcu::TestLog::EndMessage;
219
220                                                 return false;
221                                         }
222                                 }
223                         }
224                         break;
225                 }
226         }
227         return true;
228 }
229
230 int expectedVertexCount (const int                                      numPrimitives,
231                                                  const int                                      numAttribsPerPrimitive,
232                                                  const TessPrimitiveType        primitiveType,
233                                                  const SpacingMode                      spacingMode,
234                                                  const std::vector<float>&      attributes)
235 {
236         int count = 0;
237         for (int patchNdx = 0; patchNdx < numPrimitives; ++patchNdx)
238                 count += referenceVertexCount(primitiveType, spacingMode, true, &attributes[numAttribsPerPrimitive*patchNdx+0], &attributes[numAttribsPerPrimitive*patchNdx+2]);
239         return count;
240 }
241
242 void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
243 {
244         // Vertex shader
245         {
246                 std::ostringstream src;
247                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
248                         << "\n"
249                         << "layout(location = 0) in  highp float in_v_attr;\n"
250                         << "layout(location = 0) out highp float in_tc_attr;\n"
251                         << "\n"
252                         << "void main (void)\n"
253                         << "{\n"
254                         << "    in_tc_attr = in_v_attr;\n"
255                         << "}\n";
256
257                 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
258         }
259
260         // Tessellation control shader
261         {
262                 std::ostringstream src;
263                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
264                         << "#extension GL_EXT_tessellation_shader : require\n"
265                         << "\n"
266                         << "layout(vertices = 1) out;\n"
267                         << "\n"
268                         << "layout(location = 0) in highp float in_tc_attr[];\n"
269                         << "\n"
270                         << "layout(location = 0) patch out highp vec2 in_te_positionScale;\n"
271                         << "layout(location = 1) patch out highp vec2 in_te_positionOffset;\n"
272                         << "\n"
273                         << "void main (void)\n"
274                         << "{\n"
275                         << "    in_te_positionScale  = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
276                         << "    in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
277                         << "\n"
278                         << "    gl_TessLevelInner[0] = in_tc_attr[0];\n"
279                         << "    gl_TessLevelInner[1] = in_tc_attr[1];\n"
280                         << "\n"
281                         << "    gl_TessLevelOuter[0] = in_tc_attr[2];\n"
282                         << "    gl_TessLevelOuter[1] = in_tc_attr[3];\n"
283                         << "    gl_TessLevelOuter[2] = in_tc_attr[4];\n"
284                         << "    gl_TessLevelOuter[3] = in_tc_attr[5];\n"
285                         << "}\n";
286
287                 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
288         }
289
290         // Tessellation evaluation shader
291         {
292                 std::ostringstream src;
293                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
294                         << "#extension GL_EXT_tessellation_shader : require\n"
295                         << "\n"
296                         << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
297                                                  << getSpacingModeShaderName(caseDef.spacingMode) << ", "
298                                                  << getWindingShaderName(caseDef.winding)
299                                                  << (caseDef.usePointMode ? ", point_mode" : "") << ") in;\n"
300                         << "\n"
301                         << "layout(location = 0) patch in highp vec2 in_te_positionScale;\n"
302                         << "layout(location = 1) patch in highp vec2 in_te_positionOffset;\n"
303                         << "\n"
304                         << "layout(set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
305                         << "    int  numInvocations;\n"
306                         << "} sb_out;\n"
307                         << "\n"
308                         << "void main (void)\n"
309                         << "{\n"
310                         << "    atomicAdd(sb_out.numInvocations, 1);\n"
311                         << "\n"
312                         << "    gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
313                         << "}\n";
314
315                 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
316         }
317
318         // Fragment shader
319         {
320                 std::ostringstream src;
321                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
322                         << "\n"
323                         << "layout(location = 0) out mediump vec4 o_color;\n"
324                         << "\n"
325                         << "void main (void)\n"
326                         << "{\n"
327                         << "    o_color = vec4(1.0);\n"
328                         << "}\n";
329
330                 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
331         }
332 }
333
334 /*--------------------------------------------------------------------*//*!
335  * \brief Test that patch is discarded if relevant outer level <= 0.0
336  *
337  * Draws patches with different combinations of tessellation levels,
338  * varying which levels are negative. Verifies by checking that white
339  * pixels exist inside the area of valid primitives, and only black pixels
340  * exist inside the area of discarded primitives. An additional sanity
341  * test is done, checking that the number of primitives written by shader is
342  * correct.
343  *//*--------------------------------------------------------------------*/
344 tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
345 {
346         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
347
348         const DeviceInterface&  vk                                      = context.getDeviceInterface();
349         const VkDevice                  device                          = context.getDevice();
350         const VkQueue                   queue                           = context.getUniversalQueue();
351         const deUint32                  queueFamilyIndex        = context.getUniversalQueueFamilyIndex();
352         Allocator&                              allocator                       = context.getDefaultAllocator();
353
354         const std::vector<float>        attributes                              = genAttributes();
355         const int                                       numAttribsPerPrimitive  = 6 + 2 + 2; // Tess levels, scale, offset.
356         const int                                       numPrimitives                   = static_cast<int>(attributes.size() / numAttribsPerPrimitive);
357         const int                                       numExpectedVertices             = expectedVertexCount(numPrimitives, numAttribsPerPrimitive, caseDef.primitiveType, caseDef.spacingMode, attributes);
358
359         // Check the convenience assertion that all discarded patches come after the last non-discarded patch.
360         {
361                 bool discardedPatchEncountered = false;
362                 for (int patchNdx = 0; patchNdx < numPrimitives; ++patchNdx)
363                 {
364                         const bool discard = isPatchDiscarded(caseDef.primitiveType, &attributes[numAttribsPerPrimitive*patchNdx + 2]);
365                         DE_ASSERT(discard || !discardedPatchEncountered);
366                         discardedPatchEncountered = discard;
367                 }
368                 DE_UNREF(discardedPatchEncountered);
369         }
370
371         // Vertex input attributes buffer
372
373         const VkFormat     vertexFormat            = VK_FORMAT_R32_SFLOAT;
374         const deUint32     vertexStride            = tcu::getPixelSize(mapVkFormat(vertexFormat));
375         const VkDeviceSize vertexDataSizeBytes = sizeInBytes(attributes);
376         const Buffer       vertexBuffer            (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
377
378         DE_ASSERT(static_cast<int>(attributes.size()) == numPrimitives * numAttribsPerPrimitive);
379         DE_ASSERT(sizeof(attributes[0]) == vertexStride);
380
381         {
382                 const Allocation& alloc = vertexBuffer.getAllocation();
383                 deMemcpy(alloc.getHostPtr(), &attributes[0], static_cast<std::size_t>(vertexDataSizeBytes));
384                 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
385                 // No barrier needed, flushed memory is automatically visible
386         }
387
388         // Output buffer: number of invocations
389
390         const VkDeviceSize resultBufferSizeBytes = sizeof(deInt32);
391         const Buffer       resultBuffer                  (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
392
393         {
394                 const Allocation& alloc = resultBuffer.getAllocation();
395                 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
396                 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
397         }
398
399         // Color attachment
400
401         const tcu::IVec2                          renderSize                             = tcu::IVec2(256, 256);
402         const VkFormat                            colorFormat                            = VK_FORMAT_R8G8B8A8_UNORM;
403         const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
404         const Image                                       colorAttachmentImage           (vk, device, allocator,
405                                                                                                                          makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
406                                                                                                                          MemoryRequirement::Any);
407
408         // Color output buffer: image will be copied here for verification
409
410         const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
411         const Buffer colorBuffer(vk, device, allocator,
412                 makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
413
414         // Descriptors
415
416         const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
417                 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
418                 .build(vk, device));
419
420         const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
421                 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
422                 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
423
424         const Unique<VkDescriptorSet> descriptorSet    (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
425         const VkDescriptorBufferInfo  resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
426
427         DescriptorSetUpdateBuilder()
428                 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
429                 .update(vk, device);
430
431         // Pipeline
432
433         const Unique<VkImageView>          colorAttachmentView(makeImageView     (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
434         const Unique<VkRenderPass>         renderPass             (makeRenderPass        (vk, device, colorFormat));
435         const Unique<VkFramebuffer>        framebuffer            (makeFramebuffer       (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
436         const Unique<VkPipelineLayout> pipelineLayout     (makePipelineLayout(vk, device, *descriptorSetLayout));
437         const Unique<VkCommandPool>        cmdPool                        (makeCommandPool       (vk, device, queueFamilyIndex));
438         const Unique<VkCommandBuffer>  cmdBuffer                  (makeCommandBuffer (vk, device, *cmdPool));
439
440         const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
441                 .setRenderSize                            (renderSize)
442                 .setPatchControlPoints            (numAttribsPerPrimitive)
443                 .setVertexInputSingleAttribute(vertexFormat, vertexStride)
444                 .setShader                                        (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                      context.getBinaryCollection().get("vert"), DE_NULL)
445                 .setShader                                        (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,        context.getBinaryCollection().get("tesc"), DE_NULL)
446                 .setShader                                        (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL)
447                 .setShader                                        (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,                            context.getBinaryCollection().get("frag"), DE_NULL)
448                 .build                                            (vk, device, *pipelineLayout, *renderPass));
449
450         context.getTestContext().getLog()
451                 << tcu::TestLog::Message
452                 << "Note: rendering " << numPrimitives << " patches; first patches have valid relevant outer levels, "
453                 << "but later patches have one or more invalid (i.e. less than or equal to 0.0) relevant outer levels"
454                 << tcu::TestLog::EndMessage;
455
456         // Draw commands
457
458         beginCommandBuffer(vk, *cmdBuffer);
459
460         // Change color attachment image layout
461         {
462                 const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
463                         (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
464                         VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
465                         *colorAttachmentImage, colorImageSubresourceRange);
466
467                 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
468                         0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
469         }
470
471         // Begin render pass
472         {
473                 const VkRect2D renderArea = {
474                         makeOffset2D(0, 0),
475                         makeExtent2D(renderSize.x(), renderSize.y()),
476                 };
477                 const tcu::Vec4 clearColor = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
478
479                 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
480         }
481
482         vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
483         vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
484         {
485                 const VkDeviceSize vertexBufferOffset = 0ull;
486                 vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
487         }
488
489         vk.cmdDraw(*cmdBuffer, static_cast<deUint32>(attributes.size()), 1u, 0u, 0u);
490         endRenderPass(vk, *cmdBuffer);
491
492         // Copy render result to a host-visible buffer
493         {
494                 const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
495                         VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
496                         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
497                         *colorAttachmentImage, colorImageSubresourceRange);
498
499                 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
500                         0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
501         }
502         {
503                 const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
504                 vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
505         }
506         {
507                 const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
508                         VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
509
510                 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
511                         0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
512         }
513         {
514                 const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
515                         VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
516
517                 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
518                         0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
519         }
520
521         endCommandBuffer(vk, *cmdBuffer);
522         submitCommandsAndWait(vk, device, queue, *cmdBuffer);
523
524         {
525                 // Log rendered image
526                 const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
527                 invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
528
529                 const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
530
531                 tcu::TestLog& log = context.getTestContext().getLog();
532                 log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess);
533
534                 // Verify case result
535                 const Allocation& resultAlloc = resultBuffer.getAllocation();
536                 invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
537
538                 const deInt32 numResultVertices = *static_cast<deInt32*>(resultAlloc.getHostPtr());
539
540                 if (numResultVertices < numExpectedVertices)
541                 {
542                         log << tcu::TestLog::Message
543                                 << "Failure: expected " << numExpectedVertices << " vertices from shader invocations, but got only " << numResultVertices
544                                 << tcu::TestLog::EndMessage;
545                         return tcu::TestStatus::fail("Wrong number of tessellation coordinates");
546                 }
547                 else if (numResultVertices == numExpectedVertices)
548                 {
549                         log << tcu::TestLog::Message
550                                 << "Note: shader invocations generated " << numResultVertices << " vertices"
551                                 << tcu::TestLog::EndMessage;
552                 }
553                 else
554                 {
555                         log << tcu::TestLog::Message
556                                 << "Note: shader invocations generated " << numResultVertices << " vertices (expected " << numExpectedVertices << ", got "
557                                 << (numResultVertices - numExpectedVertices) << " extra)"
558                                 << tcu::TestLog::EndMessage;
559                 }
560
561                 return (verifyResultImage(log, numPrimitives, numAttribsPerPrimitive, caseDef.primitiveType, attributes, imagePixelAccess)
562                                 ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image verification failed"));
563         }
564 }
565
566 } // anonymous
567
568 //! These tests correspond to dEQP-GLES31.functional.tessellation.primitive_discard.*
569 //! \note Original test used transform feedback (TF) to capture the number of output vertices. The behavior of TF differs significantly from SSBO approach,
570 //!       especially for non-point_mode rendering. TF returned all coordinates, while SSBO computes the count based on the number of shader invocations
571 //!       which yields a much smaller number because invocations for duplicate coordinates are often eliminated.
572 //!       Because of this, the test was changed to:
573 //!       - always compute the number of expected coordinates as if point_mode was enabled
574 //!       - not fail if implementation returned more coordinates than expected
575 tcu::TestCaseGroup* createPrimitiveDiscardTests (tcu::TestContext& testCtx)
576 {
577         de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "primitive_discard", "Test primitive discard with relevant outer tessellation level <= 0.0"));
578
579         for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; primitiveTypeNdx++)
580         for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; spacingModeNdx++)
581         for (int windingNdx = 0; windingNdx < WINDING_LAST; windingNdx++)
582         for (int usePointModeNdx = 0; usePointModeNdx <= 1; usePointModeNdx++)
583         {
584                 const CaseDefinition caseDef =
585                 {
586                         (TessPrimitiveType)primitiveTypeNdx,
587                         (SpacingMode)spacingModeNdx,
588                         (Winding)windingNdx,
589                         (usePointModeNdx != 0),
590                 };
591
592                 const std::string caseName = std::string() + getTessPrimitiveTypeShaderName(caseDef.primitiveType)
593                                                                          + "_" + getSpacingModeShaderName(caseDef.spacingMode)
594                                                                          + "_" + getWindingShaderName(caseDef.winding)
595                                                                          + (caseDef.usePointMode ? "_point_mode" : "");
596
597                 addFunctionCaseWithPrograms(group.get(), caseName, "", initPrograms, test, caseDef);
598         }
599
600         return group.release();
601 }
602
603 } // tessellation
604 } // vkt