xref: /aosp_15_r20/external/deqp/external/vulkancts/modules/vulkan/clipping/vktClippingTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2016 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Clipping tests
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktClippingTests.hpp"
25 #include "vktTestCase.hpp"
26 #include "vktTestGroupUtil.hpp"
27 #include "vktTestCaseUtil.hpp"
28 #include "vktDrawUtil.hpp"
29 #include "vkRefUtil.hpp"
30 #include "vkTypeUtil.hpp"
31 #include "vkImageUtil.hpp"
32 #include "tcuImageCompare.hpp"
33 #include "tcuTestLog.hpp"
34 #include "tcuVectorUtil.hpp"
35 #include "tcuCommandLine.hpp"
36 #include "deUniquePtr.hpp"
37 #include "deStringUtil.hpp"
38 #include "deRandom.hpp"
39 
40 namespace vkt
41 {
42 namespace clipping
43 {
44 namespace
45 {
46 using namespace vk;
47 using de::MovePtr;
48 using tcu::IVec2;
49 using tcu::UVec2;
50 using tcu::Vec4;
51 using namespace drawutil;
52 
53 enum TestConstants
54 {
55     RENDER_SIZE                          = 16,
56     RENDER_SIZE_LARGE                    = 128,
57     NUM_RENDER_PIXELS                    = RENDER_SIZE * RENDER_SIZE,
58     NUM_PATCH_CONTROL_POINTS             = 3,
59     MAX_CLIP_DISTANCES                   = 8,
60     MAX_CULL_DISTANCES                   = 8,
61     MAX_COMBINED_CLIP_AND_CULL_DISTANCES = 8,
62 };
63 
64 enum FeatureFlagBits
65 {
66     FEATURE_TESSELLATION_SHADER                         = 1u << 0,
67     FEATURE_GEOMETRY_SHADER                             = 1u << 1,
68     FEATURE_SHADER_FLOAT_64                             = 1u << 2,
69     FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS          = 1u << 3,
70     FEATURE_FRAGMENT_STORES_AND_ATOMICS                 = 1u << 4,
71     FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE = 1u << 5,
72     FEATURE_DEPTH_CLAMP                                 = 1u << 6,
73     FEATURE_LARGE_POINTS                                = 1u << 7,
74     FEATURE_WIDE_LINES                                  = 1u << 8,
75     FEATURE_SHADER_CLIP_DISTANCE                        = 1u << 9,
76     FEATURE_SHADER_CULL_DISTANCE                        = 1u << 10,
77 };
78 typedef uint32_t FeatureFlags;
79 
requireFeatures(const InstanceInterface & vki,const VkPhysicalDevice physDevice,const FeatureFlags flags)80 void requireFeatures(const InstanceInterface &vki, const VkPhysicalDevice physDevice, const FeatureFlags flags)
81 {
82     const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice);
83 
84     if (((flags & FEATURE_TESSELLATION_SHADER) != 0) && !features.tessellationShader)
85         throw tcu::NotSupportedError("Tessellation shader not supported");
86 
87     if (((flags & FEATURE_GEOMETRY_SHADER) != 0) && !features.geometryShader)
88         throw tcu::NotSupportedError("Geometry shader not supported");
89 
90     if (((flags & FEATURE_SHADER_FLOAT_64) != 0) && !features.shaderFloat64)
91         throw tcu::NotSupportedError("Double-precision floats not supported");
92 
93     if (((flags & FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS) != 0) && !features.vertexPipelineStoresAndAtomics)
94         throw tcu::NotSupportedError("SSBO and image writes not supported in vertex pipeline");
95 
96     if (((flags & FEATURE_FRAGMENT_STORES_AND_ATOMICS) != 0) && !features.fragmentStoresAndAtomics)
97         throw tcu::NotSupportedError("SSBO and image writes not supported in fragment shader");
98 
99     if (((flags & FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE) != 0) &&
100         !features.shaderTessellationAndGeometryPointSize)
101         throw tcu::NotSupportedError("Tessellation and geometry shaders don't support PointSize built-in");
102 
103     if (((flags & FEATURE_DEPTH_CLAMP) != 0) && !features.depthClamp)
104         throw tcu::NotSupportedError("Depth clamp not supported");
105 
106     if (((flags & FEATURE_LARGE_POINTS) != 0) && !features.largePoints)
107         throw tcu::NotSupportedError("Large points not supported");
108 
109     if (((flags & FEATURE_WIDE_LINES) != 0) && !features.wideLines)
110         throw tcu::NotSupportedError("Wide lines not supported");
111 
112     if (((flags & FEATURE_SHADER_CLIP_DISTANCE) != 0) && !features.shaderClipDistance)
113         throw tcu::NotSupportedError("Shader ClipDistance not supported");
114 
115     if (((flags & FEATURE_SHADER_CULL_DISTANCE) != 0) && !features.shaderCullDistance)
116         throw tcu::NotSupportedError("Shader CullDistance not supported");
117 }
118 
genVertices(const VkPrimitiveTopology topology,const Vec4 & offset,const float slope)119 std::vector<Vec4> genVertices(const VkPrimitiveTopology topology, const Vec4 &offset, const float slope)
120 {
121     const float p  = 1.0f;
122     const float hp = 0.5f;
123     const float z  = 0.0f;
124     const float w  = 1.0f;
125 
126     std::vector<Vec4> vertices;
127 
128     // We're setting adjacent vertices to zero where needed, as we don't use them in meaningful way.
129 
130     switch (topology)
131     {
132     case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
133         vertices.push_back(offset + Vec4(0.0f, 0.0f, slope / 2.0f + z, w));
134         vertices.push_back(offset + Vec4(-hp, -hp, z, w));
135         vertices.push_back(offset + Vec4(hp, -hp, slope + z, w));
136         vertices.push_back(offset + Vec4(-hp, hp, z, w));
137         vertices.push_back(offset + Vec4(hp, hp, slope + z, w));
138         break;
139 
140     case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
141         vertices.push_back(offset + Vec4(-p, -p, z, w));
142         vertices.push_back(offset + Vec4(p, p, slope + z, w)); // line 0
143         vertices.push_back(offset + Vec4(p, p, slope + z, w));
144         vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // line 1
145         vertices.push_back(offset + Vec4(p, -p, slope + z, w));
146         vertices.push_back(offset + Vec4(-p, p, z, w)); // line 2
147         break;
148 
149     case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
150         vertices.push_back(Vec4());
151         vertices.push_back(offset + Vec4(-p, -p, z, w));
152         vertices.push_back(offset + Vec4(p, p, slope + z, w)); // line 0
153         vertices.push_back(Vec4());
154         vertices.push_back(Vec4());
155         vertices.push_back(offset + Vec4(p, p, slope + z, w));
156         vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // line 1
157         vertices.push_back(Vec4());
158         vertices.push_back(Vec4());
159         vertices.push_back(offset + Vec4(p, -p, slope + z, w));
160         vertices.push_back(offset + Vec4(-p, p, z, w)); // line 2
161         vertices.push_back(Vec4());
162         break;
163 
164     case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
165         vertices.push_back(offset + Vec4(-p, -p, z, w));
166         vertices.push_back(offset + Vec4(p, p, slope + z, w));  // line 0
167         vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // line 1
168         vertices.push_back(offset + Vec4(-p, p, z, w));         // line 2
169         break;
170 
171     case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
172         vertices.push_back(Vec4());
173         vertices.push_back(offset + Vec4(-p, -p, z, w));
174         vertices.push_back(offset + Vec4(p, p, slope + z, w));  // line 0
175         vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // line 1
176         vertices.push_back(offset + Vec4(-p, p, z, w));         // line 2
177         vertices.push_back(Vec4());
178         break;
179 
180     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
181         vertices.push_back(offset + Vec4(p, -p, slope + z, w));
182         vertices.push_back(offset + Vec4(-p, -p, z, w));
183         vertices.push_back(offset + Vec4(-p, p, z, w)); // triangle 0
184         vertices.push_back(offset + Vec4(-p, p, z, w));
185         vertices.push_back(offset + Vec4(p, p, slope + z, w));
186         vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // triangle 1
187         break;
188 
189     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
190         vertices.push_back(offset + Vec4(p, -p, slope + z, w));
191         vertices.push_back(Vec4());
192         vertices.push_back(offset + Vec4(-p, -p, z, w));
193         vertices.push_back(Vec4());
194         vertices.push_back(offset + Vec4(-p, p, z, w)); // triangle 0
195         vertices.push_back(Vec4());
196         vertices.push_back(offset + Vec4(-p, p, z, w));
197         vertices.push_back(Vec4());
198         vertices.push_back(offset + Vec4(p, p, slope + z, w));
199         vertices.push_back(Vec4());
200         vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // triangle 1
201         vertices.push_back(Vec4());
202         break;
203 
204     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
205         vertices.push_back(offset + Vec4(-p, -p, z, w));
206         vertices.push_back(offset + Vec4(-p, p, z, w));
207         vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // triangle 0
208         vertices.push_back(offset + Vec4(p, p, slope + z, w));  // triangle 1
209         break;
210 
211     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
212         vertices.push_back(offset + Vec4(-p, -p, z, w));
213         vertices.push_back(Vec4());
214         vertices.push_back(offset + Vec4(-p, p, z, w));
215         vertices.push_back(Vec4());
216         vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // triangle 0
217         vertices.push_back(Vec4());
218         vertices.push_back(offset + Vec4(p, p, slope + z, w)); // triangle 1
219         vertices.push_back(Vec4());
220         break;
221 
222     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
223         vertices.push_back(offset + Vec4(p, -p, slope + z, w));
224         vertices.push_back(offset + Vec4(-p, -p, z, w));
225         vertices.push_back(offset + Vec4(-p, p, z, w));        // triangle 0
226         vertices.push_back(offset + Vec4(p, p, slope + z, w)); // triangle 1
227         break;
228 
229     case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST:
230         DE_ASSERT(0);
231         break;
232 
233     default:
234         DE_ASSERT(0);
235         break;
236     }
237     return vertices;
238 }
239 
isColorInRange(const Vec4 & color,const Vec4 & minColor,const Vec4 & maxColor)240 bool inline isColorInRange(const Vec4 &color, const Vec4 &minColor, const Vec4 &maxColor)
241 {
242     return (minColor.x() <= color.x() && color.x() <= maxColor.x()) &&
243            (minColor.y() <= color.y() && color.y() <= maxColor.y()) &&
244            (minColor.z() <= color.z() && color.z() <= maxColor.z()) &&
245            (minColor.w() <= color.w() && color.w() <= maxColor.w());
246 }
247 
248 //! Count pixels that match color within threshold, in the specified region.
countPixels(const tcu::ConstPixelBufferAccess pixels,const IVec2 & regionOffset,const IVec2 & regionSize,const Vec4 & color,const Vec4 & colorThreshold)249 int countPixels(const tcu::ConstPixelBufferAccess pixels, const IVec2 &regionOffset, const IVec2 &regionSize,
250                 const Vec4 &color, const Vec4 &colorThreshold)
251 {
252     const Vec4 minColor = color - colorThreshold;
253     const Vec4 maxColor = color + colorThreshold;
254     const int xEnd      = regionOffset.x() + regionSize.x();
255     const int yEnd      = regionOffset.y() + regionSize.y();
256     int numPixels       = 0;
257 
258     DE_ASSERT(xEnd <= pixels.getWidth());
259     DE_ASSERT(yEnd <= pixels.getHeight());
260 
261     for (int y = regionOffset.y(); y < yEnd; ++y)
262         for (int x = regionOffset.x(); x < xEnd; ++x)
263         {
264             if (isColorInRange(pixels.getPixel(x, y), minColor, maxColor))
265                 ++numPixels;
266         }
267 
268     return numPixels;
269 }
270 
countPixels(const tcu::ConstPixelBufferAccess pixels,const Vec4 & color,const Vec4 & colorThreshold)271 int countPixels(const tcu::ConstPixelBufferAccess pixels, const Vec4 &color, const Vec4 &colorThreshold)
272 {
273     return countPixels(pixels, IVec2(), IVec2(pixels.getWidth(), pixels.getHeight()), color, colorThreshold);
274 }
275 
276 //! Check for correct cull and clip distance values. Middle bar should contain clip distance with linear values between 0 and 1. Cull distance is always 0.5 when enabled.
checkFragColors(const tcu::ConstPixelBufferAccess pixels,IVec2 clipRegion,int barIdx,bool hasCullDistance)277 bool checkFragColors(const tcu::ConstPixelBufferAccess pixels, IVec2 clipRegion, int barIdx, bool hasCullDistance)
278 {
279     for (int y = 0; y < pixels.getHeight(); ++y)
280         for (int x = 0; x < pixels.getWidth(); ++x)
281         {
282             if (x < clipRegion.x() && y < clipRegion.y())
283                 continue;
284 
285             const tcu::Vec4 color = pixels.getPixel(x, y);
286             const int barWidth    = pixels.getWidth() / 8;
287             const bool insideBar  = x >= barWidth * barIdx && x < barWidth * (barIdx + 1);
288             const float expectedClipDistance =
289                 insideBar ? (((((float)y + 0.5f) / (float)pixels.getHeight()) - 0.5f) * 2.0f) : 0.0f;
290             float expectedCullDistance = 0.5f;
291             const float clipDistance   = color.y();
292             const float cullDistance   = color.z();
293             const float height         = (float)pixels.getHeight();
294 
295             if (hasCullDistance)
296             {
297                 /* Linear interpolation of the cull distance.
298                  * Remember there are precision errors due to 8-bit UNORM, but they should fall inside 0.01f threshold.
299                  *
300                  * Notes about the results:
301                  * - linear interpolation of gl_CullDistance[i] = [0.0f, 0.5f]. Correct.
302                  * - Constant value:
303                  *   + 0.1f: value written by vertex shader when there are other geometry-related shaders. It means the value was not overriden. Failure.
304                  *   + 0.2f: value written by tessc shader when cull distance value from vertex is not 0.1f. Failure.
305                  *   + 0.3f: value written by tessc shader when cull distance value from vertex is 0.1f and there is geometry shader. Failure.
306                  *   + 0.4f: value written by geometry shader when cull distance is not either 0.1f (if no tess is present) or 0.3f (tess present). Failure.
307                  */
308                 if (y >= (pixels.getHeight() / 2))
309                     expectedCullDistance = expectedCullDistance * (1.0f + (2.0f * (float)y) - height) / height;
310                 else
311                     expectedCullDistance = 0.0f;
312             }
313 
314             if (fabs(clipDistance - expectedClipDistance) > 0.01f)
315                 return false;
316             if (hasCullDistance && fabs(cullDistance - expectedCullDistance) > 0.01f)
317                 return false;
318         }
319 
320     return true;
321 }
322 
323 //! Clipping against the default clip volume.
324 namespace ClipVolume
325 {
326 
327 //! Used by wide lines test.
328 enum LineOrientation
329 {
330     LINE_ORIENTATION_AXIS_ALIGNED,
331     LINE_ORIENTATION_DIAGONAL,
332 };
333 
334 const VkPointClippingBehavior invalidClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_LAST;
335 
getClippingBehavior(const InstanceInterface & vk,VkPhysicalDevice physicalDevice)336 VkPointClippingBehavior getClippingBehavior(const InstanceInterface &vk, VkPhysicalDevice physicalDevice)
337 {
338     VkPhysicalDevicePointClippingProperties behaviorProperties = {
339         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES, // VkStructureType                sType
340         DE_NULL,                                                     // void*                        pNext
341         invalidClippingBehavior                                      // VkPointClippingBehavior    pointClippingBehavior
342     };
343     VkPhysicalDeviceProperties2 properties2;
344 
345     DE_ASSERT(getPointClippingBehaviorName(invalidClippingBehavior) == DE_NULL);
346 
347     deMemset(&properties2, 0, sizeof(properties2));
348 
349     properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
350     properties2.pNext = &behaviorProperties;
351 
352     vk.getPhysicalDeviceProperties2(physicalDevice, &properties2);
353 
354     return behaviorProperties.pointClippingBehavior;
355 }
356 
addSimplePrograms(SourceCollections & programCollection,const float pointSize=0.0f)357 void addSimplePrograms(SourceCollections &programCollection, const float pointSize = 0.0f)
358 {
359     // Vertex shader
360     {
361         const bool usePointSize = pointSize > 0.0f;
362 
363         std::ostringstream src;
364         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
365             << "\n"
366             << "layout(location = 0) in vec4 v_position;\n"
367             << "\n"
368             << "out gl_PerVertex {\n"
369             << "    vec4  gl_Position;\n"
370             << (usePointSize ? "    float gl_PointSize;\n" : "") << "};\n"
371             << "\n"
372             << "void main (void)\n"
373             << "{\n"
374             << "    gl_Position = v_position;\n"
375             << (usePointSize ? "    gl_PointSize = " + de::floatToString(pointSize, 1) + ";\n" : "") << "}\n";
376 
377         programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
378     }
379 
380     // Fragment shader
381     {
382         std::ostringstream src;
383         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
384             << "\n"
385             << "layout(location = 0) out vec4 o_color;\n"
386             << "\n"
387             << "void main (void)\n"
388             << "{\n"
389             << "    o_color = vec4(1.0, gl_FragCoord.z, 0.0, 1.0);\n"
390             << "}\n";
391 
392         programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
393     }
394 }
395 
initPrograms(SourceCollections & programCollection,const VkPrimitiveTopology topology)396 void initPrograms(SourceCollections &programCollection, const VkPrimitiveTopology topology)
397 {
398     const float pointSize = (topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST ? 1.0f : 0.0f);
399     addSimplePrograms(programCollection, pointSize);
400 }
401 
initPrograms(SourceCollections & programCollection,const LineOrientation lineOrientation)402 void initPrograms(SourceCollections &programCollection, const LineOrientation lineOrientation)
403 {
404     DE_UNREF(lineOrientation);
405     addSimplePrograms(programCollection);
406 }
407 
initProgramsPointSize(SourceCollections & programCollection)408 void initProgramsPointSize(SourceCollections &programCollection)
409 {
410     addSimplePrograms(programCollection, 0.75f * static_cast<float>(RENDER_SIZE));
411 }
412 
413 //! Primitives fully inside the clip volume.
testPrimitivesInside(Context & context,const VkPrimitiveTopology topology)414 tcu::TestStatus testPrimitivesInside(Context &context, const VkPrimitiveTopology topology)
415 {
416     int minExpectedBlackPixels = 0;
417 
418     switch (topology)
419     {
420     case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
421         // We draw only 5 points.
422         minExpectedBlackPixels = NUM_RENDER_PIXELS - 5;
423         break;
424 
425     case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
426     case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
427         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
428         // Fallthrough
429     case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
430     case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
431         // Allow for some error.
432         minExpectedBlackPixels = NUM_RENDER_PIXELS - 3 * RENDER_SIZE;
433         break;
434 
435     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
436     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
437         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
438         // Fallthrough
439     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
440     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
441     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
442         // All render area should be covered.
443         minExpectedBlackPixels = 0;
444         break;
445 
446     default:
447         DE_ASSERT(0);
448         break;
449     }
450 
451     std::vector<VulkanShader> shaders;
452     shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
453     shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
454 
455     tcu::TestLog &log = context.getTestContext().getLog();
456     int numPassed     = 0;
457 
458     static const struct
459     {
460         const char *const desc;
461         float zPos;
462     } cases[] = {
463         {
464             "Draw primitives at near clipping plane, z = 0.0",
465             0.0f,
466         },
467         {
468             "Draw primitives at z = 0.5",
469             0.5f,
470         },
471         {
472             "Draw primitives at far clipping plane, z = 1.0",
473             1.0f,
474         },
475     };
476 
477     for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
478     {
479         log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
480 
481         const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 0.0f);
482         FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
483         PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
484         DrawCallData drawCallData(topology, vertices);
485         VulkanProgram vulkanProgram(shaders);
486 
487         VulkanDrawContext drawContext(context, framebufferState);
488         drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
489         drawContext.draw();
490 
491         const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
492         if (numBlackPixels >= minExpectedBlackPixels)
493             ++numPassed;
494     }
495 
496     return (numPassed == DE_LENGTH_OF_ARRAY(cases) ? tcu::TestStatus::pass("OK") :
497                                                      tcu::TestStatus::fail("Rendered image(s) are incorrect"));
498 }
499 
500 //! Primitives fully outside the clip volume.
testPrimitivesOutside(Context & context,const VkPrimitiveTopology topology)501 tcu::TestStatus testPrimitivesOutside(Context &context, const VkPrimitiveTopology topology)
502 {
503     switch (topology)
504     {
505     case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
506     case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
507     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
508     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
509         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
510         break;
511     default:
512         break;
513     }
514 
515     std::vector<VulkanShader> shaders;
516     shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
517     shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
518 
519     tcu::TestLog &log = context.getTestContext().getLog();
520     int numPassed     = 0;
521 
522     static const struct
523     {
524         const char *const desc;
525         float zPos;
526     } cases[] = {
527         {
528             "Draw primitives in front of the near clipping plane, z < 0.0",
529             -0.5f,
530         },
531         {
532             "Draw primitives behind the far clipping plane, z > 1.0",
533             1.5f,
534         },
535     };
536 
537     log << tcu::TestLog::Message << "Drawing primitives outside the clip volume. Expecting an empty image."
538         << tcu::TestLog::EndMessage;
539 
540     for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
541     {
542         log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
543 
544         const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 0.0f);
545         FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
546         PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
547         DrawCallData drawCallData(topology, vertices);
548         VulkanProgram vulkanProgram(shaders);
549 
550         VulkanDrawContext drawContext(context, framebufferState);
551         drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
552         drawContext.draw();
553 
554         // All pixels must be black -- nothing is drawn.
555         const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
556         if (numBlackPixels == NUM_RENDER_PIXELS)
557             ++numPassed;
558     }
559 
560     return (numPassed == DE_LENGTH_OF_ARRAY(cases) ? tcu::TestStatus::pass("OK") :
561                                                      tcu::TestStatus::fail("Rendered image(s) are incorrect"));
562 }
563 
564 //! Primitives partially outside the clip volume, but depth clamped
testPrimitivesDepthClamp(Context & context,const VkPrimitiveTopology topology)565 tcu::TestStatus testPrimitivesDepthClamp(Context &context, const VkPrimitiveTopology topology)
566 {
567     requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_DEPTH_CLAMP);
568 
569     std::vector<VulkanShader> shaders;
570     shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
571     shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
572 
573     const int numCases     = 4;
574     const IVec2 regionSize = IVec2(RENDER_SIZE / 2, RENDER_SIZE); //! size of the clamped region
575     const int regionPixels = regionSize.x() * regionSize.y();
576     tcu::TestLog &log      = context.getTestContext().getLog();
577     int numPassed          = 0;
578 
579     static const struct
580     {
581         const char *const desc;
582         float zPos;
583         bool depthClampEnable;
584         IVec2 regionOffset;
585         Vec4 color;
586     } cases[numCases] = {
587         {"Draw primitives intersecting the near clipping plane, depth clamp disabled", -0.5f, false, IVec2(0, 0),
588          Vec4(0.0f, 0.0f, 0.0f, 1.0f)},
589         {"Draw primitives intersecting the near clipping plane, depth clamp enabled", -0.5f, true, IVec2(0, 0),
590          Vec4(1.0f, 0.0f, 0.0f, 1.0f)},
591         {"Draw primitives intersecting the far clipping plane, depth clamp disabled", 0.5f, false,
592          IVec2(RENDER_SIZE / 2, 0), Vec4(0.0f, 0.0f, 0.0f, 1.0f)},
593         {"Draw primitives intersecting the far clipping plane, depth clamp enabled", 0.5f, true,
594          IVec2(RENDER_SIZE / 2, 0), Vec4(1.0f, 1.0f, 0.0f, 1.0f)},
595     };
596 
597     // Per case minimum number of colored pixels.
598     int caseMinPixels[numCases] = {0, 0, 0, 0};
599 
600     switch (topology)
601     {
602     case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
603         caseMinPixels[0] = caseMinPixels[2] = regionPixels - 1;
604         caseMinPixels[1] = caseMinPixels[3] = 2;
605         break;
606 
607     case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
608     case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
609         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
610         // Fallthrough
611     case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
612     case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
613         caseMinPixels[0] = regionPixels;
614         caseMinPixels[1] = RENDER_SIZE - 2;
615         caseMinPixels[2] = regionPixels;
616         caseMinPixels[3] = 2 * (RENDER_SIZE - 2);
617         break;
618 
619     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
620     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
621         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
622         // Fallthrough
623     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
624     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
625     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
626         caseMinPixels[0] = caseMinPixels[1] = caseMinPixels[2] = caseMinPixels[3] = regionPixels;
627         break;
628 
629     default:
630         DE_ASSERT(0);
631         break;
632     }
633 
634     for (int caseNdx = 0; caseNdx < numCases; ++caseNdx)
635     {
636         log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
637 
638         const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 1.0f);
639         FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
640         PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
641         pipelineState.depthClampEnable = cases[caseNdx].depthClampEnable;
642         DrawCallData drawCallData(topology, vertices);
643         VulkanProgram vulkanProgram(shaders);
644 
645         VulkanDrawContext drawContext(context, framebufferState);
646         drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
647         drawContext.draw();
648 
649         const int numPixels = countPixels(drawContext.getColorPixels(), cases[caseNdx].regionOffset, regionSize,
650                                           cases[caseNdx].color, Vec4());
651 
652         if (numPixels >= caseMinPixels[caseNdx])
653             ++numPassed;
654     }
655 
656     return (numPassed == numCases ? tcu::TestStatus::pass("OK") :
657                                     tcu::TestStatus::fail("Rendered image(s) are incorrect"));
658 }
659 
660 //! Primitives partially outside the clip volume, but depth clipped with explicit depth clip control
testPrimitivesDepthClip(Context & context,const VkPrimitiveTopology topology)661 tcu::TestStatus testPrimitivesDepthClip(Context &context, const VkPrimitiveTopology topology)
662 {
663     if (!context.getDepthClipEnableFeaturesEXT().depthClipEnable)
664         throw tcu::NotSupportedError("VK_EXT_depth_clip_enable not supported");
665 
666     std::vector<VulkanShader> shaders;
667     shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
668     shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
669 
670     const int numCases     = 4;
671     const IVec2 regionSize = IVec2(RENDER_SIZE / 2, RENDER_SIZE); //! size of the clamped region
672     const int regionPixels = regionSize.x() * regionSize.y();
673     tcu::TestLog &log      = context.getTestContext().getLog();
674     int numPassed          = 0;
675 
676     static const struct
677     {
678         const char *const desc;
679         float zPos;
680         bool depthClipEnable;
681         IVec2 regionOffset;
682         Vec4 color;
683     } cases[numCases] = {
684         {"Draw primitives intersecting the near clipping plane, depth clip enabled", -0.5f, true, IVec2(0, 0),
685          Vec4(0.0f, 0.0f, 0.0f, 1.0f)},
686         {"Draw primitives intersecting the near clipping plane, depth clip disabled", -0.5f, false, IVec2(0, 0),
687          Vec4(1.0f, 0.0f, 0.0f, 1.0f)},
688         {"Draw primitives intersecting the far clipping plane, depth clip enabled", 0.5f, true,
689          IVec2(RENDER_SIZE / 2, 0), Vec4(0.0f, 0.0f, 0.0f, 1.0f)},
690         {"Draw primitives intersecting the far clipping plane, depth clip disabled", 0.5f, false,
691          IVec2(RENDER_SIZE / 2, 0), Vec4(1.0f, 1.0f, 0.0f, 1.0f)},
692     };
693 
694     // Per case minimum number of colored pixels.
695     int caseMinPixels[numCases] = {0, 0, 0, 0};
696 
697     switch (topology)
698     {
699     case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
700         caseMinPixels[0] = caseMinPixels[2] = regionPixels - 1;
701         caseMinPixels[1] = caseMinPixels[3] = 2;
702         break;
703 
704     case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
705     case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
706         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
707         // Fallthrough
708     case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
709     case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
710         caseMinPixels[0] = regionPixels;
711         caseMinPixels[1] = RENDER_SIZE - 2;
712         caseMinPixels[2] = regionPixels;
713         caseMinPixels[3] = 2 * (RENDER_SIZE - 2);
714         break;
715 
716     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
717     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
718         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
719         // Fallthrough
720     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
721     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
722     case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
723         caseMinPixels[0] = caseMinPixels[1] = caseMinPixels[2] = caseMinPixels[3] = regionPixels;
724         break;
725 
726     default:
727         DE_ASSERT(0);
728         break;
729     }
730 
731     // Test depth clip with depth clamp disabled.
732     numPassed = 0;
733     for (int caseNdx = 0; caseNdx < numCases; ++caseNdx)
734     {
735         log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
736 
737         const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 1.0f);
738         FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
739         PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
740         pipelineState.depthClampEnable        = false;
741         pipelineState.explicitDepthClipEnable = true;
742         pipelineState.depthClipEnable         = cases[caseNdx].depthClipEnable;
743         DrawCallData drawCallData(topology, vertices);
744         VulkanProgram vulkanProgram(shaders);
745 
746         VulkanDrawContext drawContext(context, framebufferState);
747         drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
748         drawContext.draw();
749 
750         const int numPixels = countPixels(drawContext.getColorPixels(), cases[caseNdx].regionOffset, regionSize,
751                                           cases[caseNdx].color, Vec4());
752 
753         if (numPixels >= caseMinPixels[caseNdx])
754             ++numPassed;
755     }
756 
757 #ifdef CTS_USES_VULKANSC
758     if (context.getTestContext().getCommandLine().isSubProcess())
759 #endif // CTS_USES_VULKANSC
760     {
761         if (numPassed < numCases)
762             return tcu::TestStatus::fail("Rendered image(s) are incorrect (depth clip with depth clamp disabled)");
763     }
764 
765     // Test depth clip with depth clamp enabled.
766     numPassed = 0;
767     if (getPhysicalDeviceFeatures(context.getInstanceInterface(), context.getPhysicalDevice()).depthClamp)
768     {
769         for (int caseNdx = 0; caseNdx < numCases; ++caseNdx)
770         {
771             log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
772 
773             const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 1.0f);
774             FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
775             PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
776             pipelineState.depthClampEnable        = true;
777             pipelineState.explicitDepthClipEnable = true;
778             pipelineState.depthClipEnable         = cases[caseNdx].depthClipEnable;
779             DrawCallData drawCallData(topology, vertices);
780             VulkanProgram vulkanProgram(shaders);
781 
782             VulkanDrawContext drawContext(context, framebufferState);
783             drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
784             drawContext.draw();
785 
786             const int numPixels = countPixels(drawContext.getColorPixels(), cases[caseNdx].regionOffset, regionSize,
787                                               cases[caseNdx].color, Vec4());
788 
789             if (numPixels >= caseMinPixels[caseNdx])
790                 ++numPassed;
791         }
792 
793         if (numPassed < numCases)
794             return tcu::TestStatus::fail("Rendered image(s) are incorrect (depth clip with depth clamp enabled)");
795     }
796 
797     return tcu::TestStatus::pass("OK");
798 }
799 
800 //! Large point clipping
801 //! Spec: If the primitive under consideration is a point, then clipping passes it unchanged if it lies within the clip volume;
802 //!       otherwise, it is discarded.
testLargePoints(Context & context)803 tcu::TestStatus testLargePoints(Context &context)
804 {
805     requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_LARGE_POINTS);
806 
807     bool pointClippingOutside = true;
808 
809     if (context.isDeviceFunctionalitySupported("VK_KHR_maintenance2"))
810     {
811         VkPointClippingBehavior clippingBehavior =
812             getClippingBehavior(context.getInstanceInterface(), context.getPhysicalDevice());
813 
814         switch (clippingBehavior)
815         {
816         case VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES:
817             pointClippingOutside = true;
818             break;
819         case VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY:
820             pointClippingOutside = false;
821             break;
822         case invalidClippingBehavior:
823             TCU_FAIL("Clipping behavior read failure"); // Does not fall through
824         default:
825         {
826             TCU_FAIL("Unexpected clipping behavior reported");
827         }
828         }
829     }
830 
831     std::vector<VulkanShader> shaders;
832     shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
833     shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
834 
835     std::vector<Vec4> vertices;
836     {
837         const float delta = 0.1f; // much smaller than the point size
838         const float p     = 1.0f + delta;
839 
840         vertices.push_back(Vec4(-p, -p, 0.1f, 1.0f));
841         vertices.push_back(Vec4(-p, p, 0.2f, 1.0f));
842         vertices.push_back(Vec4(p, p, 0.4f, 1.0f));
843         vertices.push_back(Vec4(p, -p, 0.6f, 1.0f));
844         vertices.push_back(Vec4(0.0f, -p, 0.8f, 1.0f));
845         vertices.push_back(Vec4(p, 0.0f, 0.7f, 1.0f));
846         vertices.push_back(Vec4(0.0f, p, 0.5f, 1.0f));
847         vertices.push_back(Vec4(-p, 0.0f, 0.3f, 1.0f));
848     }
849 
850     tcu::TestLog &log = context.getTestContext().getLog();
851 
852     log << tcu::TestLog::Message
853         << "Drawing several large points just outside the clip volume. Expecting an empty image or all points rendered."
854         << tcu::TestLog::EndMessage;
855 
856     FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
857     PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
858     DrawCallData drawCallData(VK_PRIMITIVE_TOPOLOGY_POINT_LIST, vertices);
859     VulkanProgram vulkanProgram(shaders);
860 
861     VulkanDrawContext drawContext(context, framebufferState);
862     drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
863     drawContext.draw();
864 
865     // Popful case: All pixels must be black -- nothing is drawn.
866     const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
867     bool result              = false;
868 
869     // Pop-free case: All points must be rendered.
870     bool allPointsRendered = true;
871     for (std::vector<Vec4>::iterator i = vertices.begin(); i != vertices.end(); ++i)
872     {
873         if (countPixels(drawContext.getColorPixels(), Vec4(1.0f, i->z(), 0.0f, 1.0f), Vec4(0.01f)) == 0)
874             allPointsRendered = false;
875     }
876 
877     if (pointClippingOutside)
878     {
879         result = (numBlackPixels == NUM_RENDER_PIXELS || allPointsRendered);
880     }
881     else
882     {
883         // Rendering pixels without clipping: all points should be drawn.
884         result = (allPointsRendered == true);
885     }
886 
887     return (result ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
888 }
889 
890 class WideLineVertexShader : public rr::VertexShader
891 {
892 public:
WideLineVertexShader(void)893     WideLineVertexShader(void) : rr::VertexShader(1, 1)
894     {
895         m_inputs[0].type  = rr::GENERICVECTYPE_FLOAT;
896         m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
897     }
898 
~WideLineVertexShader()899     virtual ~WideLineVertexShader()
900     {
901     }
902 
shadeVertices(const rr::VertexAttrib * inputs,rr::VertexPacket * const * packets,const int numPackets) const903     void shadeVertices(const rr::VertexAttrib *inputs, rr::VertexPacket *const *packets, const int numPackets) const
904     {
905         for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
906         {
907             const tcu::Vec4 position =
908                 rr::readVertexAttribFloat(inputs[0], packets[packetNdx]->instanceNdx, packets[packetNdx]->vertexNdx);
909 
910             packets[packetNdx]->position   = position;
911             packets[packetNdx]->outputs[0] = position;
912         }
913     }
914 };
915 
916 class WideLineFragmentShader : public rr::FragmentShader
917 {
918 public:
WideLineFragmentShader(void)919     WideLineFragmentShader(void) : rr::FragmentShader(1, 1)
920     {
921         m_inputs[0].type  = rr::GENERICVECTYPE_FLOAT;
922         m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
923     }
924 
~WideLineFragmentShader()925     virtual ~WideLineFragmentShader()
926     {
927     }
928 
shadeFragments(rr::FragmentPacket * packets,const int numPackets,const rr::FragmentShadingContext & context) const929     void shadeFragments(rr::FragmentPacket *packets, const int numPackets,
930                         const rr::FragmentShadingContext &context) const
931     {
932         for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
933         {
934             for (int fragNdx = 0; fragNdx < rr::NUM_FRAGMENTS_PER_PACKET; ++fragNdx)
935             {
936                 const float depth = rr::readVarying<float>(packets[packetNdx], context, 0, fragNdx).z();
937                 rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, tcu::Vec4(1.0f, depth, 0.0f, 1.0f));
938             }
939         }
940     }
941 };
942 //! Wide line clipping
testWideLines(Context & context,const LineOrientation lineOrientation)943 tcu::TestStatus testWideLines(Context &context, const LineOrientation lineOrientation)
944 {
945     requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_WIDE_LINES);
946 
947     std::vector<VulkanShader> shaders;
948     shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
949     shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
950 
951     const float delta = 0.1f; // much smaller than the line width
952 
953     std::vector<Vec4> vertices;
954     if (lineOrientation == LINE_ORIENTATION_AXIS_ALIGNED)
955     {
956         // Axis-aligned lines just outside the clip volume.
957         const float p = 1.0f + delta;
958         const float q = 0.9f;
959 
960         vertices.push_back(Vec4(-p, -q, 0.1f, 1.0f));
961         vertices.push_back(Vec4(-p, q, 0.9f, 1.0f)); // line 0
962         vertices.push_back(Vec4(-q, p, 0.1f, 1.0f));
963         vertices.push_back(Vec4(q, p, 0.9f, 1.0f)); // line 1
964         vertices.push_back(Vec4(p, q, 0.1f, 1.0f));
965         vertices.push_back(Vec4(p, -q, 0.9f, 1.0f)); // line 2
966         vertices.push_back(Vec4(q, -p, 0.1f, 1.0f));
967         vertices.push_back(Vec4(-q, -p, 0.9f, 1.0f)); // line 3
968     }
969     else if (lineOrientation == LINE_ORIENTATION_DIAGONAL)
970     {
971         // Diagonal lines just outside the clip volume.
972         const float p = 2.0f + delta;
973 
974         vertices.push_back(Vec4(-p, 0.0f, 0.1f, 1.0f));
975         vertices.push_back(Vec4(0.0f, -p, 0.9f, 1.0f)); // line 0
976         vertices.push_back(Vec4(0.0f, -p, 0.1f, 1.0f));
977         vertices.push_back(Vec4(p, 0.0f, 0.9f, 1.0f)); // line 1
978         vertices.push_back(Vec4(p, 0.0f, 0.1f, 1.0f));
979         vertices.push_back(Vec4(0.0f, p, 0.9f, 1.0f)); // line 2
980         vertices.push_back(Vec4(0.0f, p, 0.1f, 1.0f));
981         vertices.push_back(Vec4(-p, 0.0f, 0.9f, 1.0f)); // line 3
982     }
983     else
984         DE_ASSERT(0);
985 
986     const VkPhysicalDeviceLimits limits =
987         getPhysicalDeviceProperties(context.getInstanceInterface(), context.getPhysicalDevice()).limits;
988 
989     const float lineWidth  = std::min(static_cast<float>(RENDER_SIZE), limits.lineWidthRange[1]);
990     const bool strictLines = limits.strictLines;
991     tcu::TestLog &log      = context.getTestContext().getLog();
992 
993     log << tcu::TestLog::Message
994         << "Drawing several wide lines just outside the clip volume. Expecting an empty image or all lines rendered."
995         << tcu::TestLog::EndMessage << tcu::TestLog::Message << "Line width is " << lineWidth << "."
996         << tcu::TestLog::EndMessage << tcu::TestLog::Message << "strictLines is "
997         << (strictLines ? "VK_TRUE." : "VK_FALSE.") << tcu::TestLog::EndMessage;
998 
999     FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
1000     PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
1001     DrawCallData drawCallData(VK_PRIMITIVE_TOPOLOGY_LINE_LIST, vertices);
1002     VulkanProgram vulkanProgram(shaders);
1003 
1004     VulkanDrawContext drawContext(context, framebufferState);
1005     drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
1006     drawContext.draw();
1007 
1008     // Popful case: All pixels must be black -- nothing is drawn.
1009     if (countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4()) == NUM_RENDER_PIXELS)
1010     {
1011         return tcu::TestStatus::pass("OK");
1012     }
1013     // Pop-free case: All lines must be rendered.
1014     else
1015     {
1016         const float halfWidth = lineWidth / float(RENDER_SIZE);
1017         std::vector<Vec4> refVertices;
1018 
1019         // Create reference primitives
1020         for (uint32_t lineNdx = 0u; lineNdx < (uint32_t)vertices.size() / 2u; lineNdx++)
1021         {
1022             const uint32_t vertexNdx0 = 2 * lineNdx;
1023             const uint32_t vertexNdx1 = 2 * lineNdx + 1;
1024 
1025             const bool xMajorAxis = deFloatAbs(vertices[vertexNdx1].x() - vertices[vertexNdx0].x()) >=
1026                                     deFloatAbs(vertices[vertexNdx1].y() - vertices[vertexNdx0].y());
1027             const tcu::Vec2 lineDir = tcu::normalize(tcu::Vec2(vertices[vertexNdx1].x() - vertices[vertexNdx0].x(),
1028                                                                vertices[vertexNdx1].y() - vertices[vertexNdx0].y()));
1029             const tcu::Vec4 lineNormalDir =
1030                 (strictLines) ? tcu::Vec4(lineDir.y(), -lineDir.x(), 0.0f,
1031                                           0.0f) // Line caps are perpendicular to the direction of the line segment.
1032                 :
1033                 (xMajorAxis) ? tcu::Vec4(0.0f, 1.0f, 0.0f, 0.0f) :
1034                                tcu::Vec4(1.0f, 0.0f, 0.0f, 0.0f); // Line caps are aligned to the minor axis
1035 
1036             const tcu::Vec4 wideLineVertices[] = {tcu::Vec4(vertices[vertexNdx0] + lineNormalDir * halfWidth),
1037                                                   tcu::Vec4(vertices[vertexNdx0] - lineNormalDir * halfWidth),
1038                                                   tcu::Vec4(vertices[vertexNdx1] - lineNormalDir * halfWidth),
1039                                                   tcu::Vec4(vertices[vertexNdx1] + lineNormalDir * halfWidth)};
1040 
1041             // 1st triangle
1042             refVertices.push_back(wideLineVertices[0]);
1043             refVertices.push_back(wideLineVertices[1]);
1044             refVertices.push_back(wideLineVertices[2]);
1045 
1046             // 2nd triangle
1047             refVertices.push_back(wideLineVertices[0]);
1048             refVertices.push_back(wideLineVertices[2]);
1049             refVertices.push_back(wideLineVertices[3]);
1050         }
1051 
1052         std::shared_ptr<rr::VertexShader> vertexShader     = std::make_shared<WideLineVertexShader>();
1053         std::shared_ptr<rr::FragmentShader> fragmentShader = std::make_shared<WideLineFragmentShader>();
1054 
1055         // Draw wide line was two triangles
1056         DrawCallData refCallData(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, refVertices);
1057 
1058         ReferenceDrawContext refDrawContext(framebufferState);
1059         refDrawContext.registerDrawObject(pipelineState, vertexShader, fragmentShader, refCallData);
1060         refDrawContext.draw();
1061 
1062         if (tcu::intThresholdCompare(log, "Compare", "Result comparsion", refDrawContext.getColorPixels(),
1063                                      drawContext.getColorPixels(), tcu::UVec4(1), tcu::COMPARE_LOG_ON_ERROR))
1064             return tcu::TestStatus::pass("OK");
1065     }
1066 
1067     return tcu::TestStatus::fail("Rendered image(s) are incorrect");
1068 }
1069 
1070 } // namespace ClipVolume
1071 
1072 namespace ClipDistance
1073 {
1074 
1075 struct CaseDefinition
1076 {
1077     const VkPrimitiveTopology topology;
1078     const bool dynamicIndexing;
1079     const bool enableTessellation;
1080     const bool enableGeometry;
1081     const int numClipDistances;
1082     const int numCullDistances;
1083     const bool readInFragmentShader;
1084 
CaseDefinitionvkt::clipping::__anon0be3e7980111::ClipDistance::CaseDefinition1085     CaseDefinition(const VkPrimitiveTopology topology_, const int numClipDistances_, const int numCullDistances_,
1086                    const bool enableTessellation_, const bool enableGeometry_, const bool dynamicIndexing_,
1087                    const bool readInFragmentShader_)
1088         : topology(topology_)
1089         , dynamicIndexing(dynamicIndexing_)
1090         , enableTessellation(enableTessellation_)
1091         , enableGeometry(enableGeometry_)
1092         , numClipDistances(numClipDistances_)
1093         , numCullDistances(numCullDistances_)
1094         , readInFragmentShader(readInFragmentShader_)
1095     {
1096     }
1097 };
1098 
initPrograms(SourceCollections & programCollection,const CaseDefinition caseDef)1099 void initPrograms(SourceCollections &programCollection, const CaseDefinition caseDef)
1100 {
1101     DE_ASSERT(caseDef.numClipDistances + caseDef.numCullDistances <= MAX_COMBINED_CLIP_AND_CULL_DISTANCES);
1102 
1103     std::string perVertexBlock;
1104     {
1105         std::ostringstream str;
1106         str << "gl_PerVertex {\n"
1107             << "    vec4  gl_Position;\n";
1108         if (caseDef.numClipDistances > 0)
1109             str << "    float gl_ClipDistance[" << caseDef.numClipDistances << "];\n";
1110         if (caseDef.numCullDistances > 0)
1111             str << "    float gl_CullDistance[" << caseDef.numCullDistances << "];\n";
1112         str << "}";
1113         perVertexBlock = str.str();
1114     }
1115 
1116     // Vertex shader
1117     {
1118         std::ostringstream src;
1119         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1120             << "\n"
1121             << "layout(location = 0) in  vec4 v_position;\n"
1122             << "layout(location = 0) out vec4 out_color;\n"
1123             << "\n"
1124             << "out " << perVertexBlock << ";\n"
1125             << "\n"
1126             << "void main (void)\n"
1127             << "{\n"
1128             << "    gl_Position = v_position;\n"
1129             << "    out_color   = vec4(1.0, 0.5 * (v_position.x + 1.0), 0.0, 1.0);\n"
1130             << "\n"
1131             << "    const int barNdx = gl_VertexIndex / 6;\n";
1132         if (caseDef.dynamicIndexing)
1133         {
1134             if (caseDef.numClipDistances > 0)
1135                 src << "    for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
1136                     << "        gl_ClipDistance[i] = (barNdx == i ? v_position.y : 0.0);\n";
1137             if (caseDef.numCullDistances > 0)
1138             {
1139                 src << "    for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n";
1140                 if (!caseDef.readInFragmentShader)
1141                 {
1142                     src << "        gl_CullDistance[i] = (gl_Position.x >= 0.75f) ? -0.5f : 0.5f;\n";
1143                 }
1144                 else
1145                 {
1146                     if (caseDef.enableTessellation || caseDef.enableGeometry)
1147                         src << "        gl_CullDistance[i] = 0.1f;\n";
1148                     else
1149                         src << "        gl_CullDistance[i] = (gl_Position.y < 0) ? -0.5f : 0.5f;\n";
1150                 }
1151             }
1152         }
1153         else
1154         {
1155             for (int i = 0; i < caseDef.numClipDistances; ++i)
1156                 src << "    gl_ClipDistance[" << i << "] = (barNdx == " << i << " ? v_position.y : 0.0);\n";
1157 
1158             for (int i = 0; i < caseDef.numCullDistances; ++i)
1159             {
1160                 if (!caseDef.readInFragmentShader)
1161                 {
1162                     src << "    gl_CullDistance[" << i << "] = (gl_Position.x >= 0.75f) ? -0.5f : 0.5f;\n";
1163                 }
1164                 else
1165                 {
1166                     if (caseDef.enableTessellation || caseDef.enableGeometry)
1167                         src << "    gl_CullDistance[" << i << "] = 0.1f;\n";
1168                     else
1169                         src << "    gl_CullDistance[" << i << "] = (gl_Position.y < 0) ? -0.5f : 0.5f;\n";
1170                 }
1171             }
1172         }
1173         src << "}\n";
1174 
1175         programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
1176     }
1177 
1178     if (caseDef.enableTessellation)
1179     {
1180         std::ostringstream src;
1181         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1182             << "\n"
1183             << "layout(vertices = " << NUM_PATCH_CONTROL_POINTS << ") out;\n"
1184             << "\n"
1185             << "layout(location = 0) in  vec4 in_color[];\n"
1186             << "layout(location = 0) out vec4 out_color[];\n"
1187             << "\n"
1188             << "in " << perVertexBlock << " gl_in[gl_MaxPatchVertices];\n"
1189             << "\n"
1190             << "out " << perVertexBlock << " gl_out[];\n"
1191             << "\n"
1192             << "void main (void)\n"
1193             << "{\n"
1194             << "    gl_TessLevelInner[0] = 1.0;\n"
1195             << "    gl_TessLevelInner[1] = 1.0;\n"
1196             << "\n"
1197             << "    gl_TessLevelOuter[0] = 1.0;\n"
1198             << "    gl_TessLevelOuter[1] = 1.0;\n"
1199             << "    gl_TessLevelOuter[2] = 1.0;\n"
1200             << "    gl_TessLevelOuter[3] = 1.0;\n"
1201             << "\n"
1202             << "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
1203             << "    out_color[gl_InvocationID]          = in_color[gl_InvocationID];\n"
1204             << "\n";
1205         if (caseDef.dynamicIndexing)
1206         {
1207             if (caseDef.numClipDistances > 0)
1208                 src << "    for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
1209                     << "        gl_out[gl_InvocationID].gl_ClipDistance[i] = "
1210                        "gl_in[gl_InvocationID].gl_ClipDistance[i];\n";
1211             if (caseDef.numCullDistances > 0)
1212             {
1213                 src << "    for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n";
1214                 src << "    {\n";
1215                 if (!caseDef.readInFragmentShader)
1216                 {
1217                     src << "    gl_out[gl_InvocationID].gl_CullDistance[i] = (gl_in[gl_InvocationID].gl_Position.x >= "
1218                            "0.75f) ? -0.5f : 0.5f;\n";
1219                 }
1220                 else
1221                 {
1222                     src << "        gl_out[gl_InvocationID].gl_CullDistance[i] = "
1223                            "(gl_in[gl_InvocationID].gl_CullDistance[i] == 0.1f) ? ";
1224                     if (caseDef.enableGeometry)
1225                         src << "0.3f";
1226                     else
1227                         src << "((gl_in[gl_InvocationID].gl_Position.y < 0) ? -0.5f : 0.5f)";
1228                     src << " : 0.2f;\n";
1229                 }
1230                 src << "    }\n";
1231             }
1232         }
1233         else
1234         {
1235             for (int i = 0; i < caseDef.numClipDistances; ++i)
1236                 src << "    gl_out[gl_InvocationID].gl_ClipDistance[" << i
1237                     << "] = gl_in[gl_InvocationID].gl_ClipDistance[" << i << "];\n";
1238             for (int i = 0; i < caseDef.numCullDistances; ++i)
1239             {
1240                 if (!caseDef.readInFragmentShader)
1241                 {
1242                     src << "    gl_out[gl_InvocationID].gl_CullDistance[" << i
1243                         << "] = (gl_in[gl_InvocationID].gl_Position.x >= 0.75f) ? -0.5f : 0.5f;\n";
1244                 }
1245                 else
1246                 {
1247                     src << "    gl_out[gl_InvocationID].gl_CullDistance[" << i
1248                         << "] = (gl_in[gl_InvocationID].gl_CullDistance[" << i << "] == 0.1f) ? ";
1249                     if (caseDef.enableGeometry)
1250                         src << "0.3f";
1251                     else
1252                         src << "((gl_in[gl_InvocationID].gl_Position.y < 0) ? -0.5f : 0.5f)";
1253                     src << " : 0.2f;\n";
1254                 }
1255             }
1256         }
1257         src << "}\n";
1258 
1259         programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
1260     }
1261 
1262     if (caseDef.enableTessellation)
1263     {
1264         DE_ASSERT(NUM_PATCH_CONTROL_POINTS == 3); // assumed in shader code
1265 
1266         std::ostringstream src;
1267         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1268             << "\n"
1269             << "layout(triangles, equal_spacing, ccw) in;\n"
1270             << "\n"
1271             << "layout(location = 0) in  vec4 in_color[];\n"
1272             << "layout(location = 0) out vec4 out_color;\n"
1273             << "\n"
1274             << "in " << perVertexBlock << " gl_in[gl_MaxPatchVertices];\n"
1275             << "\n"
1276             << "out " << perVertexBlock << ";\n"
1277             << "\n"
1278             << "void main (void)\n"
1279             << "{\n"
1280             << "    vec3 px     = gl_TessCoord.x * gl_in[0].gl_Position.xyz;\n"
1281             << "    vec3 py     = gl_TessCoord.y * gl_in[1].gl_Position.xyz;\n"
1282             << "    vec3 pz     = gl_TessCoord.z * gl_in[2].gl_Position.xyz;\n"
1283             << "    gl_Position = vec4(px + py + pz, 1.0);\n"
1284             << "    out_color   = (in_color[0] + in_color[1] + in_color[2]) / 3.0;\n"
1285             << "\n";
1286         if (caseDef.dynamicIndexing)
1287         {
1288             if (caseDef.numClipDistances > 0)
1289                 src << "    for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
1290                     << "        gl_ClipDistance[i] = gl_TessCoord.x * gl_in[0].gl_ClipDistance[i]\n"
1291                     << "                           + gl_TessCoord.y * gl_in[1].gl_ClipDistance[i]\n"
1292                     << "                           + gl_TessCoord.z * gl_in[2].gl_ClipDistance[i];\n";
1293             if (caseDef.numCullDistances > 0)
1294                 src << "    for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n"
1295                     << "        gl_CullDistance[i] = gl_TessCoord.x * gl_in[0].gl_CullDistance[i]\n"
1296                     << "                           + gl_TessCoord.y * gl_in[1].gl_CullDistance[i]\n"
1297                     << "                           + gl_TessCoord.z * gl_in[2].gl_CullDistance[i];\n";
1298         }
1299         else
1300         {
1301             for (int i = 0; i < caseDef.numClipDistances; ++i)
1302                 src << "    gl_ClipDistance[" << i << "] = gl_TessCoord.x * gl_in[0].gl_ClipDistance[" << i << "]\n"
1303                     << "                       + gl_TessCoord.y * gl_in[1].gl_ClipDistance[" << i << "]\n"
1304                     << "                       + gl_TessCoord.z * gl_in[2].gl_ClipDistance[" << i << "];\n";
1305             for (int i = 0; i < caseDef.numCullDistances; ++i)
1306                 src << "    gl_CullDistance[" << i << "] = gl_TessCoord.x * gl_in[0].gl_CullDistance[" << i << "]\n"
1307                     << "                       + gl_TessCoord.y * gl_in[1].gl_CullDistance[" << i << "]\n"
1308                     << "                       + gl_TessCoord.z * gl_in[2].gl_CullDistance[" << i << "];\n";
1309         }
1310         src << "}\n";
1311 
1312         programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
1313     }
1314 
1315     if (caseDef.enableGeometry)
1316     {
1317         std::ostringstream src;
1318         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1319             << "\n"
1320             << "layout(triangles) in;\n"
1321             << "layout(triangle_strip, max_vertices = 3) out;\n"
1322             << "\n"
1323             << "layout(location = 0) in  vec4 in_color[];\n"
1324             << "layout(location = 0) out vec4 out_color;\n"
1325             << "\n"
1326             << "in " << perVertexBlock << " gl_in[];\n"
1327             << "\n"
1328             << "out " << perVertexBlock << ";\n"
1329             << "\n"
1330             << "void main (void)\n"
1331             << "{\n";
1332         for (int vertNdx = 0; vertNdx < 3; ++vertNdx)
1333         {
1334             if (vertNdx > 0)
1335                 src << "\n";
1336             src << "    gl_Position = gl_in[" << vertNdx << "].gl_Position;\n"
1337                 << "    out_color   = in_color[" << vertNdx << "];\n";
1338             if (caseDef.dynamicIndexing)
1339             {
1340                 if (caseDef.numClipDistances > 0)
1341                     src << "    for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
1342                         << "        gl_ClipDistance[i] = gl_in[" << vertNdx << "].gl_ClipDistance[i];\n";
1343                 if (caseDef.numCullDistances > 0)
1344                 {
1345                     src << "    for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n";
1346                     src << "    {\n";
1347                     if (!caseDef.readInFragmentShader)
1348                     {
1349                         src << "    gl_CullDistance[i] = (gl_in[" << vertNdx
1350                             << "].gl_Position.x >= 0.75f) ? -0.5f : 0.5f;\n";
1351                     }
1352                     else
1353                     {
1354                         src << "        gl_CullDistance[i] = (gl_in[" << vertNdx << "].gl_CullDistance[i] == ";
1355                         if (caseDef.enableTessellation)
1356                             src << "0.3f";
1357                         else
1358                             src << "0.1f";
1359                         src << ") ? ((gl_in[" << vertNdx << "].gl_Position.y < 0) ? -0.5f : 0.5f) : 0.4f;\n";
1360                     }
1361                     src << "    }\n";
1362                 }
1363             }
1364             else
1365             {
1366                 for (int i = 0; i < caseDef.numClipDistances; ++i)
1367                     src << "    gl_ClipDistance[" << i << "] = gl_in[" << vertNdx << "].gl_ClipDistance[" << i
1368                         << "];\n";
1369 
1370                 for (int i = 0; i < caseDef.numCullDistances; ++i)
1371                 {
1372                     if (!caseDef.readInFragmentShader)
1373                     {
1374                         src << "    gl_CullDistance[" << i << "] = (gl_in[" << vertNdx
1375                             << "].gl_Position.x >= 0.75f) ? -0.5f : 0.5f;\n";
1376                     }
1377                     else
1378                     {
1379                         src << "        gl_CullDistance[" << i << "] = (gl_in[" << vertNdx << "].gl_CullDistance[" << i
1380                             << "] == ";
1381                         if (caseDef.enableTessellation)
1382                             src << "0.3f";
1383                         else
1384                             src << "0.1f";
1385                         src << ") ? ((gl_in[" << vertNdx << "].gl_Position.y < 0) ? -0.5f : 0.5f) : 0.4f;\n";
1386                     }
1387                 }
1388             }
1389             src << "    EmitVertex();\n";
1390         }
1391         src << "}\n";
1392 
1393         programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
1394     }
1395 
1396     // Fragment shader
1397     {
1398         std::ostringstream src;
1399         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1400             << "\n"
1401             << "layout(location = 0) in flat vec4 in_color;\n"
1402             << "layout(location = 0) out vec4 o_color;\n";
1403         if (caseDef.readInFragmentShader)
1404         {
1405             if (caseDef.numClipDistances > 0)
1406                 src << "in float gl_ClipDistance[" << caseDef.numClipDistances << "];\n";
1407             if (caseDef.numCullDistances > 0)
1408                 src << "in float gl_CullDistance[" << caseDef.numCullDistances << "];\n";
1409         }
1410         src << "\n"
1411             << "void main (void)\n"
1412             << "{\n";
1413 
1414         if (caseDef.readInFragmentShader)
1415         {
1416             src << "    o_color = vec4(in_color.r, "
1417                 << (caseDef.numClipDistances > 0 ?
1418                         std::string("gl_ClipDistance[") + de::toString(caseDef.numClipDistances / 2) + "], " :
1419                         "0.0, ")
1420                 << (caseDef.numCullDistances > 0 ?
1421                         std::string("gl_CullDistance[") + de::toString(caseDef.numCullDistances / 2) + "], " :
1422                         "0.0, ")
1423                 << " 1.0);\n";
1424         }
1425         else
1426         {
1427             src << "    o_color = vec4(in_color.rgb + vec3(0.0, 0.0, 0.5), 1.0);\n"; // mix with a constant color in case variable wasn't passed correctly through stages
1428         }
1429 
1430         src << "}\n";
1431 
1432         programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
1433     }
1434 }
1435 
testClipDistance(Context & context,const CaseDefinition caseDef)1436 tcu::TestStatus testClipDistance(Context &context, const CaseDefinition caseDef)
1437 {
1438     // Check test requirements
1439     {
1440         const InstanceInterface &vki        = context.getInstanceInterface();
1441         const VkPhysicalDevice physDevice   = context.getPhysicalDevice();
1442         const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(vki, physDevice).limits;
1443 
1444         FeatureFlags requirements = (FeatureFlags)0;
1445 
1446         if (caseDef.numClipDistances > 0)
1447             requirements |= FEATURE_SHADER_CLIP_DISTANCE;
1448         if (caseDef.numCullDistances > 0)
1449             requirements |= FEATURE_SHADER_CULL_DISTANCE;
1450         if (caseDef.enableTessellation)
1451             requirements |= FEATURE_TESSELLATION_SHADER;
1452         if (caseDef.enableGeometry)
1453             requirements |= FEATURE_GEOMETRY_SHADER;
1454 
1455         requireFeatures(vki, physDevice, requirements);
1456 
1457         // Check limits for supported features
1458 
1459         if (caseDef.numClipDistances > 0 && limits.maxClipDistances < MAX_CLIP_DISTANCES)
1460             return tcu::TestStatus::fail("maxClipDistances smaller than the minimum required by the spec");
1461         if (caseDef.numCullDistances > 0 && limits.maxCullDistances < MAX_CULL_DISTANCES)
1462             return tcu::TestStatus::fail("maxCullDistances smaller than the minimum required by the spec");
1463         if (caseDef.numCullDistances > 0 &&
1464             limits.maxCombinedClipAndCullDistances < MAX_COMBINED_CLIP_AND_CULL_DISTANCES)
1465             return tcu::TestStatus::fail(
1466                 "maxCombinedClipAndCullDistances smaller than the minimum required by the spec");
1467     }
1468 
1469     std::vector<VulkanShader> shaders;
1470     shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
1471     shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
1472     if (caseDef.enableTessellation)
1473     {
1474         shaders.push_back(
1475             VulkanShader(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc")));
1476         shaders.push_back(
1477             VulkanShader(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese")));
1478     }
1479     if (caseDef.enableGeometry)
1480         shaders.push_back(VulkanShader(VK_SHADER_STAGE_GEOMETRY_BIT, context.getBinaryCollection().get("geom")));
1481 
1482     const int numBars = MAX_COMBINED_CLIP_AND_CULL_DISTANCES;
1483 
1484     std::vector<Vec4> vertices;
1485     {
1486         const float dx = 2.0f / numBars;
1487         for (int i = 0; i < numBars; ++i)
1488         {
1489             const float x = -1.0f + dx * static_cast<float>(i);
1490 
1491             vertices.push_back(Vec4(x, -1.0f, 0.0f, 1.0f));
1492             vertices.push_back(Vec4(x, 1.0f, 0.0f, 1.0f));
1493             vertices.push_back(Vec4(x + dx, -1.0f, 0.0f, 1.0f));
1494 
1495             vertices.push_back(Vec4(x, 1.0f, 0.0f, 1.0f));
1496             vertices.push_back(Vec4(x + dx, 1.0f, 0.0f, 1.0f));
1497             vertices.push_back(Vec4(x + dx, -1.0f, 0.0f, 1.0f));
1498         }
1499     }
1500 
1501     tcu::TestLog &log = context.getTestContext().getLog();
1502 
1503     log << tcu::TestLog::Message << "Drawing " << numBars << " colored bars, clipping the first "
1504         << caseDef.numClipDistances << tcu::TestLog::EndMessage << tcu::TestLog::Message << "Using "
1505         << caseDef.numClipDistances << " ClipDistance(s) and " << caseDef.numCullDistances << " CullDistance(s)"
1506         << tcu::TestLog::EndMessage << tcu::TestLog::Message << "Expecting upper half of the clipped bars to be black."
1507         << tcu::TestLog::EndMessage;
1508 
1509     FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
1510     PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
1511     if (caseDef.enableTessellation)
1512         pipelineState.numPatchControlPoints = NUM_PATCH_CONTROL_POINTS;
1513     DrawCallData drawCallData(caseDef.topology, vertices);
1514     VulkanProgram vulkanProgram(shaders);
1515 
1516     VulkanDrawContext drawContext(context, framebufferState);
1517     drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
1518     drawContext.draw();
1519 
1520     // Count black pixels in the whole image.
1521     const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
1522     const IVec2 clipRegion   = IVec2(caseDef.numClipDistances * RENDER_SIZE / numBars, RENDER_SIZE / 2);
1523     // Cull is set to > 0.75 in the shader if caseDef.readInFragmentShader is false
1524     const int barsCulled = (int)deFloor((0.25f) / (1.0f / numBars));
1525     const IVec2 cullRegion =
1526         (caseDef.readInFragmentShader || caseDef.numCullDistances == 0) ? IVec2(0, 0) : IVec2(barsCulled, RENDER_SIZE);
1527     const int expectedClippedPixels = clipRegion.x() * clipRegion.y() + cullRegion.x() * cullRegion.y();
1528     // Make sure the bottom half has no black pixels (possible if image became corrupted).
1529     const int guardPixels   = countPixels(drawContext.getColorPixels(), IVec2(0, RENDER_SIZE / 2), clipRegion,
1530                                           Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
1531     const bool fragColorsOk = caseDef.readInFragmentShader ?
1532                                   checkFragColors(drawContext.getColorPixels(), clipRegion,
1533                                                   caseDef.numClipDistances / 2, caseDef.numCullDistances > 0) :
1534                                   true;
1535 
1536     return (numBlackPixels == expectedClippedPixels && guardPixels == 0 && fragColorsOk ?
1537                 tcu::TestStatus::pass("OK") :
1538                 tcu::TestStatus::fail("Rendered image(s) are incorrect"));
1539 }
1540 
1541 } // namespace ClipDistance
1542 
1543 namespace ClipDistanceComplementarity
1544 {
1545 
initPrograms(SourceCollections & programCollection,const int numClipDistances)1546 void initPrograms(SourceCollections &programCollection, const int numClipDistances)
1547 {
1548     // Vertex shader
1549     {
1550         DE_ASSERT(numClipDistances > 0);
1551         const int clipDistanceLastNdx = numClipDistances - 1;
1552 
1553         std::ostringstream src;
1554         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1555             << "\n"
1556             << "layout(location = 0) in vec4 v_position;    // we are passing ClipDistance in w component\n"
1557             << "\n"
1558             << "out gl_PerVertex {\n"
1559             << "    vec4  gl_Position;\n"
1560             << "    float gl_ClipDistance[" << numClipDistances << "];\n"
1561             << "};\n"
1562             << "\n"
1563             << "void main (void)\n"
1564             << "{\n"
1565             << "    gl_Position        = vec4(v_position.xyz, 1.0);\n";
1566         for (int i = 0; i < clipDistanceLastNdx; ++i)
1567             src << "    gl_ClipDistance[" << i << "] = 0.0;\n";
1568         src << "    gl_ClipDistance[" << clipDistanceLastNdx << "] = v_position.w;\n"
1569             << "}\n";
1570 
1571         programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
1572     }
1573 
1574     // Fragment shader
1575     {
1576         std::ostringstream src;
1577         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1578             << "\n"
1579             << "layout(location = 0) out vec4 o_color;\n"
1580             << "\n"
1581             << "void main (void)\n"
1582             << "{\n"
1583             << "    o_color = vec4(1.0, 1.0, 1.0, 0.5);\n"
1584             << "}\n";
1585 
1586         programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
1587     }
1588 }
1589 
testComplementarity(Context & context,const int numClipDistances)1590 tcu::TestStatus testComplementarity(Context &context, const int numClipDistances)
1591 {
1592     // Check test requirements
1593     {
1594         const InstanceInterface &vki      = context.getInstanceInterface();
1595         const VkPhysicalDevice physDevice = context.getPhysicalDevice();
1596 
1597         requireFeatures(vki, physDevice, FEATURE_SHADER_CLIP_DISTANCE);
1598     }
1599 
1600     std::vector<VulkanShader> shaders;
1601     shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
1602     shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
1603 
1604     std::vector<Vec4> vertices;
1605     {
1606         de::Random rnd(1234);
1607         const int numSections = 16;
1608         const int numVerticesPerSection =
1609             4; // logical verticies, due to triangle list topology we actually use 6 per section
1610 
1611         DE_ASSERT(RENDER_SIZE_LARGE % numSections == 0);
1612 
1613         std::vector<float> clipDistances(numVerticesPerSection * numSections);
1614         for (int i = 0; i < static_cast<int>(clipDistances.size()); ++i)
1615             clipDistances[i] = rnd.getFloat(-1.0f, 1.0f);
1616 
1617         // Two sets of identical primitives, but with a different ClipDistance sign.
1618         for (int setNdx = 0; setNdx < 2; ++setNdx)
1619         {
1620             const float sign = (setNdx == 0 ? 1.0f : -1.0f);
1621             const float dx   = 2.0f / static_cast<float>(numSections);
1622 
1623             for (int i = 0; i < numSections; ++i)
1624             {
1625                 const int ndxBase = numVerticesPerSection * i;
1626                 const float x     = -1.0f + dx * static_cast<float>(i);
1627                 const Vec4 p0     = Vec4(x, -1.0f, 0.0f, sign * clipDistances[ndxBase + 0]);
1628                 const Vec4 p1     = Vec4(x, 1.0f, 0.0f, sign * clipDistances[ndxBase + 1]);
1629                 const Vec4 p2     = Vec4(x + dx, 1.0f, 0.0f, sign * clipDistances[ndxBase + 2]);
1630                 const Vec4 p3     = Vec4(x + dx, -1.0f, 0.0f, sign * clipDistances[ndxBase + 3]);
1631 
1632                 vertices.push_back(p0);
1633                 vertices.push_back(p1);
1634                 vertices.push_back(p2);
1635 
1636                 vertices.push_back(p2);
1637                 vertices.push_back(p3);
1638                 vertices.push_back(p0);
1639             }
1640         }
1641     }
1642 
1643     tcu::TestLog &log = context.getTestContext().getLog();
1644 
1645     log << tcu::TestLog::Message << "Draw two sets of primitives with blending, differing only with ClipDistance sign."
1646         << tcu::TestLog::EndMessage << tcu::TestLog::Message << "Using " << numClipDistances
1647         << " clipping plane(s), one of them possibly having negative values." << tcu::TestLog::EndMessage
1648         << tcu::TestLog::Message << "Expecting a uniform gray area, no missing (black) nor overlapped (white) pixels."
1649         << tcu::TestLog::EndMessage;
1650 
1651     FrameBufferState framebufferState(RENDER_SIZE_LARGE, RENDER_SIZE_LARGE);
1652     PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
1653     pipelineState.blendEnable = true;
1654     DrawCallData drawCallData(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, vertices);
1655     VulkanProgram vulkanProgram(shaders);
1656 
1657     VulkanDrawContext drawContext(context, framebufferState);
1658     drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
1659     drawContext.draw();
1660 
1661     const int numGrayPixels =
1662         countPixels(drawContext.getColorPixels(), Vec4(0.5f, 0.5f, 0.5f, 1.0f), Vec4(0.02f, 0.02f, 0.02f, 0.0f));
1663     const int numExpectedPixels = RENDER_SIZE_LARGE * RENDER_SIZE_LARGE;
1664 
1665     return (numGrayPixels == numExpectedPixels ? tcu::TestStatus::pass("OK") :
1666                                                  tcu::TestStatus::fail("Rendered image(s) are incorrect"));
1667 }
1668 
1669 } // namespace ClipDistanceComplementarity
1670 
1671 namespace CullDistance
1672 {
checkSupport(Context & context)1673 void checkSupport(Context &context)
1674 {
1675     const InstanceInterface &vki      = context.getInstanceInterface();
1676     const VkPhysicalDevice physDevice = context.getPhysicalDevice();
1677 
1678     requireFeatures(vki, physDevice, FEATURE_SHADER_CULL_DISTANCE);
1679 }
1680 
initPrograms(SourceCollections & programCollection)1681 void initPrograms(SourceCollections &programCollection)
1682 {
1683     // setup triangle with three per-vertex cull distance values:
1684     // v0: gl_CullDistance = {  0.0,  0.0, -1.0 };
1685     // v1: gl_CullDistance = {  0.0, -1.0,  0.0 };
1686     // v2: gl_CullDistance = { -1.0,  0.0,  0.0 };
1687     // each vertex has a negative cull distance value but the triangle must not
1688     // be culled because none of the three half-spaces is negative for all vertices
1689 
1690     programCollection.glslSources.add("vert")
1691         << glu::VertexSource("#version 450\n"
1692                              "layout(location = 0) in vec4 v_position;\n"
1693                              "out gl_PerVertex {\n"
1694                              "  vec4  gl_Position;\n"
1695                              "  float gl_CullDistance[3];\n"
1696                              "};\n"
1697                              "void main (void)\n"
1698                              "{\n"
1699                              "  gl_Position = v_position;\n"
1700                              "  gl_CullDistance[0] = 0.0 - float(gl_VertexIndex == 2);\n"
1701                              "  gl_CullDistance[1] = 0.0 - float(gl_VertexIndex == 1);\n"
1702                              "  gl_CullDistance[2] = 0.0 - float(gl_VertexIndex == 0);\n"
1703                              "}\n");
1704 
1705     programCollection.glslSources.add("frag") << glu::FragmentSource("#version 450\n"
1706                                                                      "layout(location = 0) out vec4 o_color;\n"
1707                                                                      "void main (void)\n"
1708                                                                      "{\n"
1709                                                                      "  o_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
1710                                                                      "}\n");
1711 }
1712 
testCullDistance(Context & context)1713 tcu::TestStatus testCullDistance(Context &context)
1714 {
1715     std::vector<VulkanShader> shaders{
1716         VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")),
1717         VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"))};
1718 
1719     std::vector<Vec4> vertices{
1720         {-3.0f, 0.0f, 0.0f, 1.0f},
1721         {0.0f, 3.0f, 0.0f, 1.0f},
1722         {0.0f, -3.0f, 0.0f, 1.0f},
1723     };
1724 
1725     VulkanProgram vulkanProgram(shaders);
1726     FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
1727     PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
1728     DrawCallData drawCallData(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, vertices);
1729     VulkanDrawContext drawContext(context, framebufferState);
1730 
1731     drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
1732     drawContext.draw();
1733 
1734     const int numDrawnPixels =
1735         countPixels(drawContext.getColorPixels(), Vec4(1.0f, 0.0f, 0.0f, 1.0f), Vec4(0.02f, 0.02f, 0.02f, 0.0f));
1736     const int numExpectedPixels = RENDER_SIZE * RENDER_SIZE / 2;
1737 
1738     // triangle should be drawn and half of framebuffer should be filled with red color
1739     if (numDrawnPixels == numExpectedPixels)
1740         return tcu::TestStatus::pass("OK");
1741 
1742     return tcu::TestStatus::fail("Triangle was not drawn");
1743 }
1744 
1745 } // namespace CullDistance
1746 
checkTopologySupport(Context & context,const VkPrimitiveTopology topology)1747 void checkTopologySupport(Context &context, const VkPrimitiveTopology topology)
1748 {
1749 #ifndef CTS_USES_VULKANSC
1750     if (topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN &&
1751         context.isDeviceFunctionalitySupported("VK_KHR_portability_subset") &&
1752         !context.getPortabilitySubsetFeatures().triangleFans)
1753     {
1754         TCU_THROW(NotSupportedError,
1755                   "VK_KHR_portability_subset: Triangle fans are not supported by this implementation");
1756     }
1757 #else
1758     DE_UNREF(context);
1759     DE_UNREF(topology);
1760 #endif // CTS_USES_VULKANSC
1761 }
1762 
addClippingTests(tcu::TestCaseGroup * clippingTestsGroup)1763 void addClippingTests(tcu::TestCaseGroup *clippingTestsGroup)
1764 {
1765     tcu::TestContext &testCtx = clippingTestsGroup->getTestContext();
1766 
1767     // Clipping against the clip volume
1768     {
1769         using namespace ClipVolume;
1770 
1771         static const VkPrimitiveTopology cases[] = {
1772             VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
1773             VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
1774             VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,
1775             VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
1776             VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY,
1777             VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
1778             VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY,
1779             VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
1780             VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY,
1781             VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
1782         };
1783 
1784         MovePtr<tcu::TestCaseGroup> clipVolumeGroup(new tcu::TestCaseGroup(testCtx, "clip_volume"));
1785 
1786         // Fully inside the clip volume
1787         {
1788             MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "inside"));
1789 
1790             for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1791                 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1792                     group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), checkTopologySupport, initPrograms,
1793                     testPrimitivesInside, cases[caseNdx]);
1794 
1795             clipVolumeGroup->addChild(group.release());
1796         }
1797 
1798         // Fully outside the clip volume
1799         {
1800             MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "outside"));
1801 
1802             for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1803                 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1804                     group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), checkTopologySupport, initPrograms,
1805                     testPrimitivesOutside, cases[caseNdx]);
1806 
1807             clipVolumeGroup->addChild(group.release());
1808         }
1809 
1810         // Depth clamping
1811         {
1812             MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "depth_clamp"));
1813 
1814             for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1815                 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1816                     group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), checkTopologySupport, initPrograms,
1817                     testPrimitivesDepthClamp, cases[caseNdx]);
1818 
1819             clipVolumeGroup->addChild(group.release());
1820         }
1821 
1822         // Depth clipping
1823         {
1824             MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "depth_clip"));
1825 
1826             for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1827                 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1828                     group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), checkTopologySupport, initPrograms,
1829                     testPrimitivesDepthClip, cases[caseNdx]);
1830 
1831             clipVolumeGroup->addChild(group.release());
1832         }
1833 
1834         // Large points and wide lines
1835         {
1836             // \note For both points and lines, if an unsupported size/width is selected, the nearest supported size will be chosen.
1837             //       We do have to check for feature support though.
1838 
1839             MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "clipped"));
1840 
1841             addFunctionCaseWithPrograms(group.get(), "large_points", initProgramsPointSize, testLargePoints);
1842 
1843             addFunctionCaseWithPrograms<LineOrientation>(group.get(), "wide_lines_axis_aligned", initPrograms,
1844                                                          testWideLines, LINE_ORIENTATION_AXIS_ALIGNED);
1845             addFunctionCaseWithPrograms<LineOrientation>(group.get(), "wide_lines_diagonal", initPrograms,
1846                                                          testWideLines, LINE_ORIENTATION_DIAGONAL);
1847 
1848             clipVolumeGroup->addChild(group.release());
1849         }
1850 
1851         clippingTestsGroup->addChild(clipVolumeGroup.release());
1852     }
1853 
1854     // User-defined clip planes
1855     {
1856         MovePtr<tcu::TestCaseGroup> clipDistanceGroup(new tcu::TestCaseGroup(testCtx, "user_defined"));
1857 
1858         // ClipDistance, CullDistance and maxCombinedClipAndCullDistances usage
1859         {
1860             using namespace ClipDistance;
1861 
1862             static const struct
1863             {
1864                 const char *const groupName;
1865                 bool useCullDistance;
1866             } caseGroups[] = {
1867                 {"clip_distance", false},
1868                 {"clip_cull_distance", true},
1869             };
1870 
1871             static const struct
1872             {
1873                 const char *const name;
1874                 bool readInFragmentShader;
1875             } fragmentShaderReads[] = {
1876 
1877                 {"", false}, {"_fragmentshader_read", true}};
1878 
1879             const uint32_t flagTessellation = 1u << 0;
1880             const uint32_t flagGeometry     = 1u << 1;
1881 
1882             for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(caseGroups); ++groupNdx)
1883                 for (int indexingMode = 0; indexingMode < 2; ++indexingMode)
1884                 {
1885                     const bool dynamicIndexing = (indexingMode == 1);
1886                     const std::string mainGroupName =
1887                         de::toString(caseGroups[groupNdx].groupName) + (dynamicIndexing ? "_dynamic_index" : "");
1888 
1889                     MovePtr<tcu::TestCaseGroup> mainGroup(new tcu::TestCaseGroup(testCtx, mainGroupName.c_str()));
1890 
1891                     for (uint32_t shaderMask = 0u; shaderMask <= (flagTessellation | flagGeometry); ++shaderMask)
1892                     {
1893                         const bool useTessellation = (shaderMask & flagTessellation) != 0;
1894                         const bool useGeometry     = (shaderMask & flagGeometry) != 0;
1895                         const std::string shaderGroupName =
1896                             std::string("vert") + (useTessellation ? "_tess" : "") + (useGeometry ? "_geom" : "");
1897 
1898                         MovePtr<tcu::TestCaseGroup> shaderGroup(
1899                             new tcu::TestCaseGroup(testCtx, shaderGroupName.c_str()));
1900 
1901                         for (int numClipPlanes = 1; numClipPlanes <= MAX_CLIP_DISTANCES; ++numClipPlanes)
1902                             for (int fragmentShaderReadNdx = 0;
1903                                  fragmentShaderReadNdx < DE_LENGTH_OF_ARRAY(fragmentShaderReads);
1904                                  ++fragmentShaderReadNdx)
1905                             {
1906                                 const int numCullPlanes =
1907                                     (caseGroups[groupNdx].useCullDistance ?
1908                                          std::min(static_cast<int>(MAX_CULL_DISTANCES),
1909                                                   MAX_COMBINED_CLIP_AND_CULL_DISTANCES - numClipPlanes) :
1910                                          0);
1911                                 const std::string caseName =
1912                                     de::toString(numClipPlanes) +
1913                                     (numCullPlanes > 0 ? "_" + de::toString(numCullPlanes) : "") +
1914                                     de::toString(fragmentShaderReads[fragmentShaderReadNdx].name);
1915                                 const VkPrimitiveTopology topology =
1916                                     (useTessellation ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST :
1917                                                        VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
1918 
1919                                 addFunctionCaseWithPrograms<CaseDefinition>(
1920                                     shaderGroup.get(), caseName, initPrograms, testClipDistance,
1921                                     CaseDefinition(topology, numClipPlanes, numCullPlanes, useTessellation, useGeometry,
1922                                                    dynamicIndexing,
1923                                                    fragmentShaderReads[fragmentShaderReadNdx].readInFragmentShader));
1924                             }
1925                         mainGroup->addChild(shaderGroup.release());
1926                     }
1927                     clipDistanceGroup->addChild(mainGroup.release());
1928                 }
1929         }
1930         clippingTestsGroup->addChild(clipDistanceGroup.release());
1931 
1932         // Complementarity criterion (i.e. clipped and not clipped areas must add up to a complete primitive with no holes nor overlap)
1933         {
1934             using namespace ClipDistanceComplementarity;
1935 
1936             MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "complementarity"));
1937 
1938             for (int numClipDistances = 1; numClipDistances <= MAX_CLIP_DISTANCES; ++numClipDistances)
1939                 addFunctionCaseWithPrograms<int>(group.get(), de::toString(numClipDistances).c_str(), initPrograms,
1940                                                  testComplementarity, numClipDistances);
1941 
1942             clippingTestsGroup->addChild(group.release());
1943         }
1944 
1945         {
1946             using namespace CullDistance;
1947 
1948             MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "misc"));
1949 
1950             addFunctionCaseWithPrograms(group.get(), "negative_and_non_negative_cull_distance", checkSupport,
1951                                         initPrograms, testCullDistance);
1952 
1953             clippingTestsGroup->addChild(group.release());
1954         }
1955     }
1956 }
1957 
1958 } // namespace
1959 
createTests(tcu::TestContext & testCtx,const std::string & name)1960 tcu::TestCaseGroup *createTests(tcu::TestContext &testCtx, const std::string &name)
1961 {
1962     return createTestGroup(testCtx, name.c_str(), addClippingTests);
1963 }
1964 
1965 } // namespace clipping
1966 } // namespace vkt
1967