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