Merge vk-gl-cts/master into vk-gl-cts/vulkan-cts-next-dev
[platform/upstream/VK-GL-CTS.git] / external / vulkancts / modules / vulkan / clipping / vktClippingTests.cpp
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 "tcuTestLog.hpp"
33 #include "deUniquePtr.hpp"
34 #include "deStringUtil.hpp"
35 #include "deRandom.hpp"
36
37 namespace vkt
38 {
39 namespace clipping
40 {
41 namespace
42 {
43 using namespace vk;
44 using de::MovePtr;
45 using tcu::UVec2;
46 using tcu::Vec4;
47 using tcu::IVec2;
48 using namespace drawutil;
49
50 enum TestConstants
51 {
52         RENDER_SIZE                                                             = 16,
53         RENDER_SIZE_LARGE                                               = 128,
54         NUM_RENDER_PIXELS                                               = RENDER_SIZE * RENDER_SIZE,
55         NUM_PATCH_CONTROL_POINTS                                = 3,
56         MAX_CLIP_DISTANCES                                              = 8,
57         MAX_CULL_DISTANCES                                              = 8,
58         MAX_COMBINED_CLIP_AND_CULL_DISTANCES    = 8,
59 };
60
61 enum FeatureFlagBits
62 {
63         FEATURE_TESSELLATION_SHADER                                                     = 1u << 0,
64         FEATURE_GEOMETRY_SHADER                                                         = 1u << 1,
65         FEATURE_SHADER_FLOAT_64                                                         = 1u << 2,
66         FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS                      = 1u << 3,
67         FEATURE_FRAGMENT_STORES_AND_ATOMICS                                     = 1u << 4,
68         FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE     = 1u << 5,
69         FEATURE_DEPTH_CLAMP                                                                     = 1u << 6,
70         FEATURE_LARGE_POINTS                                                            = 1u << 7,
71         FEATURE_WIDE_LINES                                                                      = 1u << 8,
72         FEATURE_SHADER_CLIP_DISTANCE                                            = 1u << 9,
73         FEATURE_SHADER_CULL_DISTANCE                                            = 1u << 10,
74 };
75 typedef deUint32 FeatureFlags;
76
77 void requireFeatures (const InstanceInterface& vki, const VkPhysicalDevice physDevice, const FeatureFlags flags)
78 {
79         const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice);
80
81         if (((flags & FEATURE_TESSELLATION_SHADER) != 0) && !features.tessellationShader)
82                 throw tcu::NotSupportedError("Tessellation shader not supported");
83
84         if (((flags & FEATURE_GEOMETRY_SHADER) != 0) && !features.geometryShader)
85                 throw tcu::NotSupportedError("Geometry shader not supported");
86
87         if (((flags & FEATURE_SHADER_FLOAT_64) != 0) && !features.shaderFloat64)
88                 throw tcu::NotSupportedError("Double-precision floats not supported");
89
90         if (((flags & FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS) != 0) && !features.vertexPipelineStoresAndAtomics)
91                 throw tcu::NotSupportedError("SSBO and image writes not supported in vertex pipeline");
92
93         if (((flags & FEATURE_FRAGMENT_STORES_AND_ATOMICS) != 0) && !features.fragmentStoresAndAtomics)
94                 throw tcu::NotSupportedError("SSBO and image writes not supported in fragment shader");
95
96         if (((flags & FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE) != 0) && !features.shaderTessellationAndGeometryPointSize)
97                 throw tcu::NotSupportedError("Tessellation and geometry shaders don't support PointSize built-in");
98
99         if (((flags & FEATURE_DEPTH_CLAMP) != 0) && !features.depthClamp)
100                 throw tcu::NotSupportedError("Depth clamp not supported");
101
102         if (((flags & FEATURE_LARGE_POINTS) != 0) && !features.largePoints)
103                 throw tcu::NotSupportedError("Large points not supported");
104
105         if (((flags & FEATURE_WIDE_LINES) != 0) && !features.wideLines)
106                 throw tcu::NotSupportedError("Wide lines not supported");
107
108         if (((flags & FEATURE_SHADER_CLIP_DISTANCE) != 0) && !features.shaderClipDistance)
109                 throw tcu::NotSupportedError("Shader ClipDistance not supported");
110
111         if (((flags & FEATURE_SHADER_CULL_DISTANCE) != 0) && !features.shaderCullDistance)
112                 throw tcu::NotSupportedError("Shader CullDistance not supported");
113 }
114
115 std::vector<Vec4> genVertices (const VkPrimitiveTopology topology, const Vec4& offset, const float slope)
116 {
117         const float p  = 1.0f;
118         const float hp = 0.5f;
119         const float z  = 0.0f;
120         const float w  = 1.0f;
121
122         std::vector<Vec4> vertices;
123
124         // We're setting adjacent vertices to zero where needed, as we don't use them in meaningful way.
125
126         switch (topology)
127         {
128                 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
129                         vertices.push_back(offset + Vec4(0.0f, 0.0f, slope/2.0f + z, w));
130                         vertices.push_back(offset + Vec4( -hp,  -hp,              z, w));
131                         vertices.push_back(offset + Vec4(  hp,  -hp,      slope + z, w));
132                         vertices.push_back(offset + Vec4( -hp,   hp,              z, w));
133                         vertices.push_back(offset + Vec4(  hp,   hp,      slope + z, w));
134                         break;
135
136                 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
137                         vertices.push_back(offset + Vec4(-p, -p,         z, w));
138                         vertices.push_back(offset + Vec4( p,  p, slope + z, w));        // line 0
139                         vertices.push_back(offset + Vec4( p,  p, slope + z, w));
140                         vertices.push_back(offset + Vec4( p, -p, slope + z, w));        // line 1
141                         vertices.push_back(offset + Vec4( p, -p, slope + z, w));
142                         vertices.push_back(offset + Vec4(-p,  p,         z, w));        // line 2
143                         break;
144
145                 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
146                         vertices.push_back(Vec4());
147                         vertices.push_back(offset + Vec4(-p, -p,         z, w));
148                         vertices.push_back(offset + Vec4( p,  p, slope + z, w));        // line 0
149                         vertices.push_back(Vec4());
150                         vertices.push_back(Vec4());
151                         vertices.push_back(offset + Vec4( p,  p, slope + z, w));
152                         vertices.push_back(offset + Vec4( p, -p, slope + z, w));        // line 1
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,         z, w));        // line 2
157                         vertices.push_back(Vec4());
158                         break;
159
160                 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
161                         vertices.push_back(offset + Vec4(-p, -p,         z, w));
162                         vertices.push_back(offset + Vec4( p,  p, slope + z, w));        // line 0
163                         vertices.push_back(offset + Vec4( p, -p, slope + z, w));        // line 1
164                         vertices.push_back(offset + Vec4(-p,  p,         z, w));        // line 2
165                         break;
166
167                 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
168                         vertices.push_back(Vec4());
169                         vertices.push_back(offset + Vec4(-p, -p,         z, w));
170                         vertices.push_back(offset + Vec4( p,  p, slope + z, w));        // line 0
171                         vertices.push_back(offset + Vec4( p, -p, slope + z, w));        // line 1
172                         vertices.push_back(offset + Vec4(-p,  p,         z, w));        // line 2
173                         vertices.push_back(Vec4());
174                         break;
175
176                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
177                         vertices.push_back(offset + Vec4( p, -p, slope + z, w));
178                         vertices.push_back(offset + Vec4(-p, -p,         z, w));
179                         vertices.push_back(offset + Vec4(-p,  p,         z, w));        // triangle 0
180                         vertices.push_back(offset + Vec4(-p,  p,         z, w));
181                         vertices.push_back(offset + Vec4( p,  p, slope + z, w));
182                         vertices.push_back(offset + Vec4( p, -p, slope + z, w));        // triangle 1
183                         break;
184
185                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
186                         vertices.push_back(offset + Vec4( p, -p, slope + z, w));
187                         vertices.push_back(Vec4());
188                         vertices.push_back(offset + Vec4(-p, -p,         z, w));
189                         vertices.push_back(Vec4());
190                         vertices.push_back(offset + Vec4(-p,  p,         z, w));        // triangle 0
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, slope + z, w));
195                         vertices.push_back(Vec4());
196                         vertices.push_back(offset + Vec4( p, -p, slope + z, w));        // triangle 1
197                         vertices.push_back(Vec4());
198                         break;
199
200                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
201                         vertices.push_back(offset + Vec4(-p, -p,         z, w));
202                         vertices.push_back(offset + Vec4(-p,  p,         z, w));
203                         vertices.push_back(offset + Vec4( p, -p, slope + z, w));        // triangle 0
204                         vertices.push_back(offset + Vec4( p,  p, slope + z, w));        // triangle 1
205                         break;
206
207                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
208                         vertices.push_back(offset + Vec4(-p, -p,         z, w));
209                         vertices.push_back(Vec4());
210                         vertices.push_back(offset + Vec4(-p,  p,         z, w));
211                         vertices.push_back(Vec4());
212                         vertices.push_back(offset + Vec4( p, -p, slope + z, w));        // triangle 0
213                         vertices.push_back(Vec4());
214                         vertices.push_back(offset + Vec4( p,  p, slope + z, w));        // triangle 1
215                         vertices.push_back(Vec4());
216                         break;
217
218                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
219                         vertices.push_back(offset + Vec4( p, -p, slope + z, w));
220                         vertices.push_back(offset + Vec4(-p, -p,         z, w));
221                         vertices.push_back(offset + Vec4(-p,  p,         z, w));        // triangle 0
222                         vertices.push_back(offset + Vec4( p,  p, slope + z, w));        // triangle 1
223                         break;
224
225                 case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST:
226                         DE_ASSERT(0);
227                         break;
228
229                 default:
230                         DE_ASSERT(0);
231                         break;
232         }
233         return vertices;
234 }
235
236 bool inline isColorInRange (const Vec4& color, const Vec4& minColor, const Vec4& maxColor)
237 {
238         return (minColor.x() <= color.x() && color.x() <= maxColor.x())
239                 && (minColor.y() <= color.y() && color.y() <= maxColor.y())
240                 && (minColor.z() <= color.z() && color.z() <= maxColor.z())
241                 && (minColor.w() <= color.w() && color.w() <= maxColor.w());
242 }
243
244 //! Count pixels that match color within threshold, in the specified region.
245 int countPixels (const tcu::ConstPixelBufferAccess pixels, const IVec2& regionOffset, const IVec2& regionSize, const Vec4& color, const Vec4& colorThreshold)
246 {
247         const Vec4      minColor        = color - colorThreshold;
248         const Vec4      maxColor        = color + colorThreshold;
249         const int       xEnd            = regionOffset.x() + regionSize.x();
250         const int       yEnd            = regionOffset.y() + regionSize.y();
251         int                     numPixels       = 0;
252
253         DE_ASSERT(xEnd <= pixels.getWidth());
254         DE_ASSERT(yEnd <= pixels.getHeight());
255
256         for (int y = regionOffset.y(); y < yEnd; ++y)
257         for (int x = regionOffset.x(); x < xEnd; ++x)
258         {
259                 if (isColorInRange(pixels.getPixel(x, y), minColor, maxColor))
260                         ++numPixels;
261         }
262
263         return numPixels;
264 }
265
266 int countPixels (const tcu::ConstPixelBufferAccess pixels, const Vec4& color, const Vec4& colorThreshold)
267 {
268         return countPixels(pixels, IVec2(), IVec2(pixels.getWidth(), pixels.getHeight()), color, colorThreshold);
269 }
270
271 //! Clipping against the default clip volume.
272 namespace ClipVolume
273 {
274
275 //! Used by wide lines test.
276 enum LineOrientation
277 {
278         LINE_ORIENTATION_AXIS_ALIGNED,
279         LINE_ORIENTATION_DIAGONAL,
280 };
281
282 const VkPointClippingBehaviorKHR invalidClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_KHR_LAST;
283
284 VkPointClippingBehaviorKHR getClippingBehavior (const InstanceInterface& vk, VkPhysicalDevice physicalDevice)
285 {
286         VkPhysicalDevicePointClippingPropertiesKHR      behaviorProperties      =
287         {
288                 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR,        // VkStructureType                              sType
289                 DE_NULL,                                                                                                                        // void*                                                pNext
290                 invalidClippingBehavior                                                                                         // VkPointClippingBehaviorKHR   pointClippingBehavior
291         };
292         VkPhysicalDeviceProperties2KHR                          properties2;
293
294         DE_ASSERT(getPointClippingBehaviorKHRName(invalidClippingBehavior) == DE_NULL);
295
296         deMemset(&properties2, 0, sizeof(properties2));
297
298         properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
299         properties2.pNext = &behaviorProperties;
300
301         vk.getPhysicalDeviceProperties2KHR(physicalDevice, &properties2);
302
303         return behaviorProperties.pointClippingBehavior;
304 }
305
306 void addSimplePrograms (SourceCollections& programCollection, const float pointSize = 0.0f)
307 {
308         // Vertex shader
309         {
310                 const bool usePointSize = pointSize > 0.0f;
311
312                 std::ostringstream src;
313                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
314                         << "\n"
315                         << "layout(location = 0) in vec4 v_position;\n"
316                         << "\n"
317                         << "out gl_PerVertex {\n"
318                         << "    vec4  gl_Position;\n"
319                         << (usePointSize ? "    float gl_PointSize;\n" : "")
320                         << "};\n"
321                         << "\n"
322                         << "void main (void)\n"
323                         << "{\n"
324                         << "    gl_Position = v_position;\n"
325                         << (usePointSize ? "    gl_PointSize = " + de::floatToString(pointSize, 1) + ";\n" : "")
326                         << "}\n";
327
328                 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
329         }
330
331         // Fragment shader
332         {
333                 std::ostringstream src;
334                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
335                         << "\n"
336                         << "layout(location = 0) out vec4 o_color;\n"
337                         << "\n"
338                         << "void main (void)\n"
339                         << "{\n"
340                         << "    o_color = vec4(1.0, gl_FragCoord.z, 0.0, 1.0);\n"
341                         << "}\n";
342
343                 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
344         }
345 }
346
347 void initPrograms (SourceCollections& programCollection, const VkPrimitiveTopology topology)
348 {
349         const float pointSize = (topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST ? 1.0f : 0.0f);
350         addSimplePrograms(programCollection, pointSize);
351 }
352
353 void initPrograms (SourceCollections& programCollection, const LineOrientation lineOrientation)
354 {
355         DE_UNREF(lineOrientation);
356         addSimplePrograms(programCollection);
357 }
358
359 void initProgramsPointSize (SourceCollections& programCollection)
360 {
361         addSimplePrograms(programCollection, 0.75f * RENDER_SIZE);
362 }
363
364 //! Primitives fully inside the clip volume.
365 tcu::TestStatus testPrimitivesInside (Context& context, const VkPrimitiveTopology topology)
366 {
367         int minExpectedBlackPixels = 0;
368
369         switch (topology)
370         {
371                 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
372                         // We draw only 5 points.
373                         minExpectedBlackPixels = NUM_RENDER_PIXELS - 5;
374                         break;
375
376                 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
377                 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
378                 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
379                 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
380                         // Allow for some error.
381                         minExpectedBlackPixels = NUM_RENDER_PIXELS - 3 * RENDER_SIZE;
382                         break;
383
384                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
385                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
386                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
387                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
388                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
389                         // All render area should be covered.
390                         minExpectedBlackPixels = 0;
391                         break;
392
393                 default:
394                         DE_ASSERT(0);
395                         break;
396         }
397
398         std::vector<VulkanShader> shaders;
399         shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT,              context.getBinaryCollection().get("vert")));
400         shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT,    context.getBinaryCollection().get("frag")));
401
402         tcu::TestLog&   log                     = context.getTestContext().getLog();
403         int                             numPassed       = 0;
404
405         static const struct
406         {
407                 const char* const       desc;
408                 float                           zPos;
409         } cases[] =
410         {
411                 { "Draw primitives at near clipping plane, z = 0.0",    0.0f, },
412                 { "Draw primitives at z = 0.5",                                                 0.5f, },
413                 { "Draw primitives at far clipping plane, z = 1.0",             1.0f, },
414         };
415
416         for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
417         {
418                 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
419
420                 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 0.0f);
421                 DrawState                       drawState               (topology, RENDER_SIZE, RENDER_SIZE);
422                 DrawCallData            drawCallData    (vertices);
423                 VulkanProgram           vulkanProgram   (shaders);
424
425                 VulkanDrawContext       drawContext(context, drawState, drawCallData, vulkanProgram);
426                 drawContext.draw();
427
428                 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
429                 if (numBlackPixels >= minExpectedBlackPixels)
430                         ++numPassed;
431         }
432
433         return (numPassed == DE_LENGTH_OF_ARRAY(cases) ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
434 }
435
436 //! Primitives fully outside the clip volume.
437 tcu::TestStatus testPrimitivesOutside (Context& context, const VkPrimitiveTopology topology)
438 {
439         std::vector<VulkanShader> shaders;
440         shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT,              context.getBinaryCollection().get("vert")));
441         shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT,    context.getBinaryCollection().get("frag")));
442
443         tcu::TestLog&   log                     = context.getTestContext().getLog();
444         int                             numPassed       = 0;
445
446         static const struct
447         {
448                 const char* const       desc;
449                 float                           zPos;
450         } cases[] =
451         {
452                 { "Draw primitives in front of the near clipping plane, z < 0.0",       -0.5f, },
453                 { "Draw primitives behind the far clipping plane, z > 1.0",                      1.5f, },
454         };
455
456         log << tcu::TestLog::Message << "Drawing primitives outside the clip volume. Expecting an empty image." << tcu::TestLog::EndMessage;
457
458         for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
459         {
460                 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
461
462                 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 0.0f);
463                 DrawState                               drawState               (topology, RENDER_SIZE, RENDER_SIZE);
464                 DrawCallData                    drawCallData    (vertices);
465                 VulkanProgram                   vulkanProgram   (shaders);
466
467                 VulkanDrawContext               drawContext(context, drawState, drawCallData, vulkanProgram);
468                 drawContext.draw();
469
470                 // All pixels must be black -- nothing is drawn.
471                 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
472                 if (numBlackPixels == NUM_RENDER_PIXELS)
473                         ++numPassed;
474         }
475
476         return (numPassed == DE_LENGTH_OF_ARRAY(cases) ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
477 }
478
479 //! Primitives partially outside the clip volume, but depth clamped
480 tcu::TestStatus testPrimitivesDepthClamp (Context& context, const VkPrimitiveTopology topology)
481 {
482         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_DEPTH_CLAMP);
483
484         std::vector<VulkanShader> shaders;
485         shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT,              context.getBinaryCollection().get("vert")));
486         shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT,    context.getBinaryCollection().get("frag")));
487
488         const int               numCases                = 4;
489         const IVec2             regionSize              = IVec2(RENDER_SIZE/2, RENDER_SIZE);    //! size of the clamped region
490         const int               regionPixels    = regionSize.x() * regionSize.y();
491         tcu::TestLog&   log                             = context.getTestContext().getLog();
492         int                             numPassed               = 0;
493
494         static const struct
495         {
496                 const char* const       desc;
497                 float                           zPos;
498                 bool                            depthClampEnable;
499                 IVec2                           regionOffset;
500                 Vec4                            color;
501         } cases[numCases] =
502         {
503                 { "Draw primitives intersecting the near clipping plane, depth clamp disabled", -0.5f,  false,  IVec2(0, 0),                            Vec4(0.0f, 0.0f, 0.0f, 1.0f) },
504                 { "Draw primitives intersecting the near clipping plane, depth clamp enabled",  -0.5f,  true,   IVec2(0, 0),                            Vec4(1.0f, 0.0f, 0.0f, 1.0f) },
505                 { "Draw primitives intersecting the far clipping plane, depth clamp disabled",   0.5f,  false,  IVec2(RENDER_SIZE/2, 0),        Vec4(0.0f, 0.0f, 0.0f, 1.0f) },
506                 { "Draw primitives intersecting the far clipping plane, depth clamp enabled",    0.5f,  true,   IVec2(RENDER_SIZE/2, 0),        Vec4(1.0f, 1.0f, 0.0f, 1.0f) },
507         };
508
509         // Per case minimum number of colored pixels.
510         int caseMinPixels[numCases] = { 0, 0, 0, 0 };
511
512         switch (topology)
513         {
514                 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
515                         caseMinPixels[0] = caseMinPixels[2] = regionPixels - 1;
516                         caseMinPixels[1] = caseMinPixels[3] = 2;
517                         break;
518
519                 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
520                 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
521                 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
522                 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
523                         caseMinPixels[0] = regionPixels;
524                         caseMinPixels[1] = RENDER_SIZE - 2;
525                         caseMinPixels[2] = regionPixels;
526                         caseMinPixels[3] = 2 * (RENDER_SIZE - 2);
527                         break;
528
529                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
530                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
531                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
532                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
533                 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
534                         caseMinPixels[0] = caseMinPixels[1] = caseMinPixels[2] = caseMinPixels[3] = regionPixels;
535                         break;
536
537                 default:
538                         DE_ASSERT(0);
539                         break;
540         }
541
542         for (int caseNdx = 0; caseNdx < numCases; ++caseNdx)
543         {
544                 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
545
546                 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 1.0f);
547
548                 DrawState                                       drawState               (topology, RENDER_SIZE, RENDER_SIZE);
549                 DrawCallData                            drawCallData    (vertices);
550                 VulkanProgram                           vulkanProgram   (shaders);
551                 drawState.depthClampEnable = cases[caseNdx].depthClampEnable;
552
553                 VulkanDrawContext                       drawContext(context, drawState, drawCallData, vulkanProgram);
554                 drawContext.draw();
555
556                 const int numPixels = countPixels(drawContext.getColorPixels(), cases[caseNdx].regionOffset, regionSize, cases[caseNdx].color, Vec4());
557
558                 if (numPixels >= caseMinPixels[caseNdx])
559                         ++numPassed;
560         }
561
562         return (numPassed == numCases ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
563 }
564
565 //! Large point clipping
566 //! Spec: If the primitive under consideration is a point, then clipping passes it unchanged if it lies within the clip volume;
567 //!       otherwise, it is discarded.
568 tcu::TestStatus testLargePoints (Context& context)
569 {
570         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_LARGE_POINTS);
571
572         bool pointClippingOutside = true;
573
574         if (de::contains(context.getDeviceExtensions().begin(), context.getDeviceExtensions().end(), "VK_KHR_maintenance2"))
575         {
576                 VkPointClippingBehaviorKHR clippingBehavior = getClippingBehavior(context.getInstanceInterface(), context.getPhysicalDevice());
577
578                 switch (clippingBehavior)
579                 {
580                         case VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR:            pointClippingOutside = true;                            break;
581                         case VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR:      pointClippingOutside = false;                           break;
582                         case invalidClippingBehavior:                                                           TCU_FAIL("Clipping behavior read failure");     break;
583                         default:
584                         {
585                                 TCU_FAIL("Unexpected clipping behavior reported");
586                         }
587                 }
588         }
589
590         std::vector<VulkanShader> shaders;
591         shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT,              context.getBinaryCollection().get("vert")));
592         shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT,    context.getBinaryCollection().get("frag")));
593
594         std::vector<Vec4> vertices;
595         {
596                 const float delta       = 0.1f;  // much smaller than the point size
597                 const float p           = 1.0f + delta;
598
599                 vertices.push_back(Vec4(  -p,   -p, 0.1f, 1.0f));
600                 vertices.push_back(Vec4(  -p,    p, 0.2f, 1.0f));
601                 vertices.push_back(Vec4(   p,    p, 0.4f, 1.0f));
602                 vertices.push_back(Vec4(   p,   -p, 0.6f, 1.0f));
603                 vertices.push_back(Vec4(0.0f,   -p, 0.8f, 1.0f));
604                 vertices.push_back(Vec4(   p, 0.0f, 0.7f, 1.0f));
605                 vertices.push_back(Vec4(0.0f,    p, 0.5f, 1.0f));
606                 vertices.push_back(Vec4(  -p, 0.0f, 0.3f, 1.0f));
607         }
608
609         tcu::TestLog&   log     = context.getTestContext().getLog();
610
611         log << tcu::TestLog::Message << "Drawing several large points just outside the clip volume. Expecting an empty image or all points rendered." << tcu::TestLog::EndMessage;
612
613         DrawState                       drawState               (VK_PRIMITIVE_TOPOLOGY_POINT_LIST, RENDER_SIZE, RENDER_SIZE);
614         DrawCallData            drawCallData    (vertices);
615         VulkanProgram           vulkanProgram   (shaders);
616
617         VulkanDrawContext       drawContext(context, drawState, drawCallData, vulkanProgram);
618         drawContext.draw();
619
620         // Popful case: All pixels must be black -- nothing is drawn.
621         const int       numBlackPixels  = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
622         bool            result                  = false;
623
624         // Pop-free case: All points must be rendered.
625         bool allPointsRendered = true;
626         for (std::vector<Vec4>::iterator i = vertices.begin(); i != vertices.end(); ++i)
627         {
628                 if (countPixels(drawContext.getColorPixels(), Vec4(1.0f, i->z(), 0.0f, 1.0f), Vec4(0.01f)) == 0)
629                         allPointsRendered = false;
630         }
631
632         if (pointClippingOutside)
633         {
634                 result = (numBlackPixels == NUM_RENDER_PIXELS || allPointsRendered);
635         }
636         else
637         {
638                 // Rendering pixels without clipping: all points should be drawn.
639                 result = (allPointsRendered == true);
640         }
641
642         return (result ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
643 }
644
645 //! Wide line clipping
646 //! Spec: If the primitive is a line segment, then clipping does nothing to it if it lies entirely within the clip volume, and discards it
647 //!       if it lies entirely outside the volume.
648 tcu::TestStatus testWideLines (Context& context, const LineOrientation lineOrientation)
649 {
650         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_WIDE_LINES);
651
652         std::vector<VulkanShader> shaders;
653         shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT,              context.getBinaryCollection().get("vert")));
654         shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT,    context.getBinaryCollection().get("frag")));
655
656         const float delta = 0.1f;  // much smaller than the line width
657
658         std::vector<Vec4> vertices;
659         if (lineOrientation == LINE_ORIENTATION_AXIS_ALIGNED)
660         {
661                 // Axis-aligned lines just outside the clip volume.
662                 const float p = 1.0f + delta;
663                 const float q = 0.9f;
664
665                 vertices.push_back(Vec4(-p, -q, 0.1f, 1.0f));
666                 vertices.push_back(Vec4(-p,  q, 0.9f, 1.0f));   // line 0
667                 vertices.push_back(Vec4(-q,  p, 0.1f, 1.0f));
668                 vertices.push_back(Vec4( q,  p, 0.9f, 1.0f));   // line 1
669                 vertices.push_back(Vec4( p,  q, 0.1f, 1.0f));
670                 vertices.push_back(Vec4( p, -q, 0.9f, 1.0f));   // line 2
671                 vertices.push_back(Vec4( q, -p, 0.1f, 1.0f));
672                 vertices.push_back(Vec4(-q, -p, 0.9f, 1.0f));   // line 3
673         }
674         else if (lineOrientation == LINE_ORIENTATION_DIAGONAL)
675         {
676                 // Diagonal lines just outside the clip volume.
677                 const float p = 2.0f + delta;
678
679                 vertices.push_back(Vec4(  -p, 0.0f, 0.1f, 1.0f));
680                 vertices.push_back(Vec4(0.0f,   -p, 0.9f, 1.0f));       // line 0
681                 vertices.push_back(Vec4(0.0f,   -p, 0.1f, 1.0f));
682                 vertices.push_back(Vec4(   p, 0.0f, 0.9f, 1.0f));       // line 1
683                 vertices.push_back(Vec4(   p, 0.0f, 0.1f, 1.0f));
684                 vertices.push_back(Vec4(0.0f,    p, 0.9f, 1.0f));       // line 2
685                 vertices.push_back(Vec4(0.0f,    p, 0.1f, 1.0f));
686                 vertices.push_back(Vec4(  -p, 0.0f, 0.9f, 1.0f));       // line 3
687         }
688         else
689                 DE_ASSERT(0);
690
691         const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(context.getInstanceInterface(), context.getPhysicalDevice()).limits;
692
693         const float             lineWidth       = std::min(static_cast<float>(RENDER_SIZE), limits.lineWidthRange[1]);
694         tcu::TestLog&   log                     = context.getTestContext().getLog();
695
696         log << tcu::TestLog::Message << "Drawing several wide lines just outside the clip volume. Expecting an empty image." << tcu::TestLog::EndMessage
697                 << tcu::TestLog::Message << "Line width is " << lineWidth << "." << tcu::TestLog::EndMessage;
698
699         DrawState                                       drawState               (VK_PRIMITIVE_TOPOLOGY_LINE_LIST, RENDER_SIZE, RENDER_SIZE);
700         DrawCallData                            drawCallData    (vertices);
701         VulkanProgram                           vulkanProgram   (shaders);
702         drawState.lineWidth                     = lineWidth;
703
704         VulkanDrawContext                       drawContext(context, drawState, drawCallData, vulkanProgram);
705         drawContext.draw();
706
707         // All pixels must be black -- nothing is drawn.
708         const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
709
710         return (numBlackPixels == NUM_RENDER_PIXELS ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
711 }
712
713 } // ClipVolume ns
714
715 namespace ClipDistance
716 {
717
718 struct CaseDefinition
719 {
720         const VkPrimitiveTopology       topology;
721         const bool                                      dynamicIndexing;
722         const bool                                      enableTessellation;
723         const bool                                      enableGeometry;
724         const int                                       numClipDistances;
725         const int                                       numCullDistances;
726
727         CaseDefinition (const VkPrimitiveTopology       topology_,
728                                         const int                                       numClipDistances_,
729                                         const int                                       numCullDistances_,
730                                         const bool                                      enableTessellation_,
731                                         const bool                                      enableGeometry_,
732                                         const bool                                      dynamicIndexing_)
733                 : topology                                      (topology_)
734                 , dynamicIndexing                       (dynamicIndexing_)
735                 , enableTessellation            (enableTessellation_)
736                 , enableGeometry                        (enableGeometry_)
737                 , numClipDistances                      (numClipDistances_)
738                 , numCullDistances                      (numCullDistances_)
739         {
740         }
741 };
742
743 void initPrograms (SourceCollections& programCollection, const CaseDefinition caseDef)
744 {
745         DE_ASSERT(caseDef.numClipDistances + caseDef.numCullDistances <= MAX_COMBINED_CLIP_AND_CULL_DISTANCES);
746
747         std::string perVertexBlock;
748         {
749                 std::ostringstream str;
750                 str << "gl_PerVertex {\n"
751                         << "    vec4  gl_Position;\n";
752                 if (caseDef.numClipDistances > 0)
753                         str << "    float gl_ClipDistance[" << caseDef.numClipDistances << "];\n";
754                 if (caseDef.numCullDistances > 0)
755                         str << "    float gl_CullDistance[" << caseDef.numCullDistances << "];\n";
756                 str << "}";
757                 perVertexBlock = str.str();
758         }
759
760         // Vertex shader
761         {
762                 std::ostringstream src;
763                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
764                         << "\n"
765                         << "layout(location = 0) in  vec4 v_position;\n"
766                         << "layout(location = 0) out vec4 out_color;\n"
767                         << "\n"
768                         << "out " << perVertexBlock << ";\n"
769                         << "\n"
770                         << "void main (void)\n"
771                         << "{\n"
772                         << "    gl_Position = v_position;\n"
773                         << "    out_color   = vec4(1.0, 0.5 * (v_position.x + 1.0), 0.0, 1.0);\n"
774                         << "\n"
775                         << "    const int barNdx = gl_VertexIndex / 6;\n";
776                 if (caseDef.dynamicIndexing)
777                 {
778                         if (caseDef.numClipDistances > 0)
779                                 src << "    for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
780                                         << "        gl_ClipDistance[i] = (barNdx == i ? v_position.y : 0.0);\n";
781                         if (caseDef.numCullDistances > 0)
782                                 src << "    for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n"
783                                         << "        gl_CullDistance[i] = 0.0;\n";
784                 }
785                 else
786                 {
787                         for (int i = 0; i < caseDef.numClipDistances; ++i)
788                                 src << "    gl_ClipDistance[" << i << "] = (barNdx == " << i << " ? v_position.y : 0.0);\n";
789                         for (int i = 0; i < caseDef.numCullDistances; ++i)
790                                 src << "    gl_CullDistance[" << i << "] = 0.0;\n";             // don't cull anything
791                 }
792                 src     << "}\n";
793
794                 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
795         }
796
797         if (caseDef.enableTessellation)
798         {
799                 std::ostringstream src;
800                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
801                         << "\n"
802                         << "layout(vertices = " << NUM_PATCH_CONTROL_POINTS << ") out;\n"
803                         << "\n"
804                         << "layout(location = 0) in  vec4 in_color[];\n"
805                         << "layout(location = 0) out vec4 out_color[];\n"
806                         << "\n"
807                         << "in " << perVertexBlock << " gl_in[gl_MaxPatchVertices];\n"
808                         << "\n"
809                         << "out " << perVertexBlock << " gl_out[];\n"
810                         << "\n"
811                         << "void main (void)\n"
812                         << "{\n"
813                         << "    gl_TessLevelInner[0] = 1.0;\n"
814                         << "    gl_TessLevelInner[1] = 1.0;\n"
815                         << "\n"
816                         << "    gl_TessLevelOuter[0] = 1.0;\n"
817                         << "    gl_TessLevelOuter[1] = 1.0;\n"
818                         << "    gl_TessLevelOuter[2] = 1.0;\n"
819                         << "    gl_TessLevelOuter[3] = 1.0;\n"
820                         << "\n"
821                         << "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
822                         << "    out_color[gl_InvocationID]          = in_color[gl_InvocationID];\n"
823                         << "\n";
824                 if (caseDef.dynamicIndexing)
825                 {
826                         if (caseDef.numClipDistances > 0)
827                                 src << "    for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
828                                         << "        gl_out[gl_InvocationID].gl_ClipDistance[i] = gl_in[gl_InvocationID].gl_ClipDistance[i];\n";
829                         if (caseDef.numCullDistances > 0)
830                                 src << "    for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n"
831                                         << "        gl_out[gl_InvocationID].gl_CullDistance[i] = gl_in[gl_InvocationID].gl_CullDistance[i];\n";
832                 }
833                 else
834                 {
835                         for (int i = 0; i < caseDef.numClipDistances; ++i)
836                                 src << "    gl_out[gl_InvocationID].gl_ClipDistance[" << i << "] = gl_in[gl_InvocationID].gl_ClipDistance[" << i << "];\n";
837                         for (int i = 0; i < caseDef.numCullDistances; ++i)
838                                 src << "    gl_out[gl_InvocationID].gl_CullDistance[" << i << "] = gl_in[gl_InvocationID].gl_CullDistance[" << i << "];\n";
839                 }
840                 src << "}\n";
841
842                 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
843         }
844
845         if (caseDef.enableTessellation)
846         {
847                 DE_ASSERT(NUM_PATCH_CONTROL_POINTS == 3);  // assumed in shader code
848
849                 std::ostringstream src;
850                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
851                         << "\n"
852                         << "layout(triangles, equal_spacing, ccw) in;\n"
853                         << "\n"
854                         << "layout(location = 0) in  vec4 in_color[];\n"
855                         << "layout(location = 0) out vec4 out_color;\n"
856                         << "\n"
857                         << "in " << perVertexBlock << " gl_in[gl_MaxPatchVertices];\n"
858                         << "\n"
859                         << "out " << perVertexBlock << ";\n"
860                         << "\n"
861                         << "void main (void)\n"
862                         << "{\n"
863                         << "    vec3 px     = gl_TessCoord.x * gl_in[0].gl_Position.xyz;\n"
864                         << "    vec3 py     = gl_TessCoord.y * gl_in[1].gl_Position.xyz;\n"
865                         << "    vec3 pz     = gl_TessCoord.z * gl_in[2].gl_Position.xyz;\n"
866                         << "    gl_Position = vec4(px + py + pz, 1.0);\n"
867                         << "    out_color   = (in_color[0] + in_color[1] + in_color[2]) / 3.0;\n"
868                         << "\n";
869                 if (caseDef.dynamicIndexing)
870                 {
871                         if (caseDef.numClipDistances > 0)
872                                 src << "    for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
873                                         << "        gl_ClipDistance[i] = gl_TessCoord.x * gl_in[0].gl_ClipDistance[i]\n"
874                                         << "                           + gl_TessCoord.y * gl_in[1].gl_ClipDistance[i]\n"
875                                         << "                           + gl_TessCoord.z * gl_in[2].gl_ClipDistance[i];\n";
876                         if (caseDef.numCullDistances > 0)
877                                 src << "    for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n"
878                                         << "        gl_CullDistance[i] = gl_TessCoord.x * gl_in[0].gl_CullDistance[i]\n"
879                                         << "                           + gl_TessCoord.y * gl_in[1].gl_CullDistance[i]\n"
880                                         << "                           + gl_TessCoord.z * gl_in[2].gl_CullDistance[i];\n";
881                 }
882                 else
883                 {
884                         for (int i = 0; i < caseDef.numClipDistances; ++i)
885                                 src << "    gl_ClipDistance[" << i << "] = gl_TessCoord.x * gl_in[0].gl_ClipDistance[" << i << "]\n"
886                                         << "                       + gl_TessCoord.y * gl_in[1].gl_ClipDistance[" << i << "]\n"
887                                         << "                       + gl_TessCoord.z * gl_in[2].gl_ClipDistance[" << i << "];\n";
888                         for (int i = 0; i < caseDef.numCullDistances; ++i)
889                                 src << "    gl_CullDistance[" << i << "] = gl_TessCoord.x * gl_in[0].gl_CullDistance[" << i << "]\n"
890                                         << "                       + gl_TessCoord.y * gl_in[1].gl_CullDistance[" << i << "]\n"
891                                         << "                       + gl_TessCoord.z * gl_in[2].gl_CullDistance[" << i << "];\n";
892                 }
893                 src << "}\n";
894
895                 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
896         }
897
898         if (caseDef.enableGeometry)
899         {
900                 std::ostringstream src;
901                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
902                         << "\n"
903                         << "layout(triangles) in;\n"
904                         << "layout(triangle_strip, max_vertices = 3) out;\n"
905                         << "\n"
906                         << "layout(location = 0) in  vec4 in_color[];\n"
907                         << "layout(location = 0) out vec4 out_color;\n"
908                         << "\n"
909                         << "in " << perVertexBlock << " gl_in[];\n"
910                         << "\n"
911                         << "out " << perVertexBlock << ";\n"
912                         << "\n"
913                         << "void main (void)\n"
914                         << "{\n";
915                 for (int vertNdx = 0; vertNdx < 3; ++vertNdx)
916                 {
917                         if (vertNdx > 0)
918                                 src << "\n";
919                         src << "    gl_Position = gl_in[" << vertNdx << "].gl_Position;\n"
920                                 << "    out_color   = in_color[" << vertNdx << "];\n";
921                         if (caseDef.dynamicIndexing)
922                         {
923                                 if (caseDef.numClipDistances > 0)
924                                         src << "    for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
925                                                 << "        gl_ClipDistance[i] = gl_in[" << vertNdx << "].gl_ClipDistance[i];\n";
926                                 if (caseDef.numCullDistances > 0)
927                                         src << "    for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n"
928                                                 << "        gl_CullDistance[i] = gl_in[" << vertNdx << "].gl_CullDistance[i];\n";
929                         }
930                         else
931                         {
932                                 for (int i = 0; i < caseDef.numClipDistances; ++i)
933                                         src << "    gl_ClipDistance[" << i << "] = gl_in[" << vertNdx << "].gl_ClipDistance[" << i << "];\n";
934                                 for (int i = 0; i < caseDef.numCullDistances; ++i)
935                                         src << "    gl_CullDistance[" << i << "] = gl_in[" << vertNdx << "].gl_CullDistance[" << i << "];\n";
936                         }
937                         src << "    EmitVertex();\n";
938                 }
939                 src     << "}\n";
940
941                 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
942         }
943
944         // Fragment shader
945         {
946                 std::ostringstream src;
947                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
948                         << "\n"
949                         << "layout(location = 0) in flat vec4 in_color;\n"
950                         << "layout(location = 0) out vec4 o_color;\n"
951                         << "\n"
952                         << "void main (void)\n"
953                         << "{\n"
954                         << "    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
955                         << "}\n";
956
957                 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
958         }
959 }
960
961 tcu::TestStatus testClipDistance (Context& context, const CaseDefinition caseDef)
962 {
963         // Check test requirements
964         {
965                 const InstanceInterface&                vki                     = context.getInstanceInterface();
966                 const VkPhysicalDevice                  physDevice      = context.getPhysicalDevice();
967                 const VkPhysicalDeviceLimits    limits          = getPhysicalDeviceProperties(vki, physDevice).limits;
968
969                 FeatureFlags requirements = (FeatureFlags)0;
970
971                 if (caseDef.numClipDistances > 0)
972                         requirements |= FEATURE_SHADER_CLIP_DISTANCE;
973                 if (caseDef.numCullDistances > 0)
974                         requirements |= FEATURE_SHADER_CULL_DISTANCE;
975                 if (caseDef.enableTessellation)
976                         requirements |= FEATURE_TESSELLATION_SHADER;
977                 if (caseDef.enableGeometry)
978                         requirements |= FEATURE_GEOMETRY_SHADER;
979
980                 requireFeatures(vki, physDevice, requirements);
981
982                 // Check limits for supported features
983
984                 if (caseDef.numClipDistances > 0 && limits.maxClipDistances < MAX_CLIP_DISTANCES)
985                         return tcu::TestStatus::fail("maxClipDistances smaller than the minimum required by the spec");
986                 if (caseDef.numCullDistances > 0 && limits.maxCullDistances < MAX_CULL_DISTANCES)
987                         return tcu::TestStatus::fail("maxCullDistances smaller than the minimum required by the spec");
988                 if (caseDef.numCullDistances > 0 && limits.maxCombinedClipAndCullDistances < MAX_COMBINED_CLIP_AND_CULL_DISTANCES)
989                         return tcu::TestStatus::fail("maxCombinedClipAndCullDistances smaller than the minimum required by the spec");
990         }
991
992         std::vector<VulkanShader> shaders;
993         shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT,              context.getBinaryCollection().get("vert")));
994         shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT,    context.getBinaryCollection().get("frag")));
995         if (caseDef.enableTessellation)
996         {
997                 shaders.push_back(VulkanShader(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,        context.getBinaryCollection().get("tesc")));
998                 shaders.push_back(VulkanShader(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,     context.getBinaryCollection().get("tese")));
999         }
1000         if (caseDef.enableGeometry)
1001                 shaders.push_back(VulkanShader(VK_SHADER_STAGE_GEOMETRY_BIT,    context.getBinaryCollection().get("geom")));
1002
1003         const int numBars = MAX_COMBINED_CLIP_AND_CULL_DISTANCES;
1004
1005         std::vector<Vec4> vertices;
1006         {
1007                 const float     dx = 2.0f / numBars;
1008                 for (int i = 0; i < numBars; ++i)
1009                 {
1010                         const float x = -1.0f + dx * static_cast<float>(i);
1011
1012                         vertices.push_back(Vec4(x,      -1.0f, 0.0f, 1.0f));
1013                         vertices.push_back(Vec4(x,       1.0f, 0.0f, 1.0f));
1014                         vertices.push_back(Vec4(x + dx, -1.0f, 0.0f, 1.0f));
1015
1016                         vertices.push_back(Vec4(x,       1.0f, 0.0f, 1.0f));
1017                         vertices.push_back(Vec4(x + dx,  1.0f, 0.0f, 1.0f));
1018                         vertices.push_back(Vec4(x + dx, -1.0f, 0.0f, 1.0f));
1019                 }
1020         }
1021
1022         tcu::TestLog& log = context.getTestContext().getLog();
1023
1024         log << tcu::TestLog::Message << "Drawing " << numBars << " colored bars, clipping the first " << caseDef.numClipDistances << tcu::TestLog::EndMessage
1025                 << tcu::TestLog::Message << "Using " << caseDef.numClipDistances << " ClipDistance(s) and " << caseDef.numCullDistances << " CullDistance(s)" << tcu::TestLog::EndMessage
1026                 << tcu::TestLog::Message << "Expecting upper half of the clipped bars to be black." << tcu::TestLog::EndMessage;
1027
1028         DrawState                       drawState               (caseDef.topology, RENDER_SIZE, RENDER_SIZE);
1029         DrawCallData            drawCallData    (vertices);
1030         VulkanProgram           vulkanProgram   (shaders);
1031
1032         if (caseDef.enableTessellation)
1033                 drawState.numPatchControlPoints = NUM_PATCH_CONTROL_POINTS;
1034
1035         VulkanDrawContext       drawContext(context, drawState, drawCallData, vulkanProgram);
1036         drawContext.draw();
1037
1038         // Count black pixels in the whole image.
1039         const int numBlackPixels                = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
1040         const IVec2     clipRegion                      = IVec2(caseDef.numClipDistances * RENDER_SIZE / numBars, RENDER_SIZE / 2);
1041         const int expectedClippedPixels = clipRegion.x() * clipRegion.y();
1042         // Make sure the bottom half has no black pixels (possible if image became corrupted).
1043         const int guardPixels                   = countPixels(drawContext.getColorPixels(), IVec2(0, RENDER_SIZE/2), clipRegion, Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
1044
1045         return (numBlackPixels == expectedClippedPixels && guardPixels == 0 ? tcu::TestStatus::pass("OK")
1046                                                                                                                                                 : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
1047 }
1048
1049 } // ClipDistance ns
1050
1051 namespace ClipDistanceComplementarity
1052 {
1053
1054 void initPrograms (SourceCollections& programCollection, const int numClipDistances)
1055 {
1056         // Vertex shader
1057         {
1058                 DE_ASSERT(numClipDistances > 0);
1059                 const int clipDistanceLastNdx = numClipDistances - 1;
1060
1061                 std::ostringstream src;
1062                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1063                         << "\n"
1064                         << "layout(location = 0) in vec4 v_position;    // we are passing ClipDistance in w component\n"
1065                         << "\n"
1066                         << "out gl_PerVertex {\n"
1067                         << "    vec4  gl_Position;\n"
1068                         << "    float gl_ClipDistance[" << numClipDistances << "];\n"
1069                         << "};\n"
1070                         << "\n"
1071                         << "void main (void)\n"
1072                         << "{\n"
1073                         << "    gl_Position        = vec4(v_position.xyz, 1.0);\n";
1074                 for (int i = 0; i < clipDistanceLastNdx; ++i)
1075                         src << "    gl_ClipDistance[" << i << "] = 0.0;\n";
1076                 src << "    gl_ClipDistance[" << clipDistanceLastNdx << "] = v_position.w;\n"
1077                         << "}\n";
1078
1079                 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
1080         }
1081
1082         // Fragment shader
1083         {
1084                 std::ostringstream src;
1085                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1086                         << "\n"
1087                         << "layout(location = 0) out vec4 o_color;\n"
1088                         << "\n"
1089                         << "void main (void)\n"
1090                         << "{\n"
1091                         << "    o_color = vec4(1.0, 1.0, 1.0, 0.5);\n"
1092                         << "}\n";
1093
1094                 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
1095         }
1096 }
1097
1098 tcu::TestStatus testComplementarity (Context& context, const int numClipDistances)
1099 {
1100         // Check test requirements
1101         {
1102                 const InstanceInterface&                vki                     = context.getInstanceInterface();
1103                 const VkPhysicalDevice                  physDevice      = context.getPhysicalDevice();
1104
1105                 requireFeatures(vki, physDevice, FEATURE_SHADER_CLIP_DISTANCE);
1106         }
1107
1108         std::vector<VulkanShader> shaders;
1109         shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT,              context.getBinaryCollection().get("vert")));
1110         shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT,    context.getBinaryCollection().get("frag")));
1111
1112         std::vector<Vec4> vertices;
1113         {
1114                 de::Random      rnd                                             (1234);
1115                 const int       numSections                             = 16;
1116                 const int       numVerticesPerSection   = 4;    // logical verticies, due to triangle list topology we actually use 6 per section
1117
1118                 DE_ASSERT(RENDER_SIZE_LARGE % numSections == 0);
1119
1120                 std::vector<float> clipDistances(numVerticesPerSection * numSections);
1121                 for (int i = 0; i < static_cast<int>(clipDistances.size()); ++i)
1122                         clipDistances[i] = rnd.getFloat(-1.0f, 1.0f);
1123
1124                 // Two sets of identical primitives, but with a different ClipDistance sign.
1125                 for (int setNdx = 0; setNdx < 2; ++setNdx)
1126                 {
1127                         const float sign = (setNdx == 0 ? 1.0f : -1.0f);
1128                         const float     dx       = 2.0f / static_cast<float>(numSections);
1129
1130                         for (int i = 0; i < numSections; ++i)
1131                         {
1132                                 const int       ndxBase = numVerticesPerSection * i;
1133                                 const float x           = -1.0f + dx * static_cast<float>(i);
1134                                 const Vec4      p0              = Vec4(x,      -1.0f, 0.0f, sign * clipDistances[ndxBase + 0]);
1135                                 const Vec4      p1              = Vec4(x,       1.0f, 0.0f, sign * clipDistances[ndxBase + 1]);
1136                                 const Vec4      p2              = Vec4(x + dx,  1.0f, 0.0f, sign * clipDistances[ndxBase + 2]);
1137                                 const Vec4      p3              = Vec4(x + dx, -1.0f, 0.0f, sign * clipDistances[ndxBase + 3]);
1138
1139                                 vertices.push_back(p0);
1140                                 vertices.push_back(p1);
1141                                 vertices.push_back(p2);
1142
1143                                 vertices.push_back(p2);
1144                                 vertices.push_back(p3);
1145                                 vertices.push_back(p0);
1146                         }
1147                 }
1148         }
1149
1150         tcu::TestLog& log = context.getTestContext().getLog();
1151
1152         log << tcu::TestLog::Message << "Draw two sets of primitives with blending, differing only with ClipDistance sign." << tcu::TestLog::EndMessage
1153                 << tcu::TestLog::Message << "Using " << numClipDistances << " clipping plane(s), one of them possibly having negative values." << tcu::TestLog::EndMessage
1154                 << tcu::TestLog::Message << "Expecting a uniform gray area, no missing (black) nor overlapped (white) pixels." << tcu::TestLog::EndMessage;
1155
1156         DrawState                                       drawState               (VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, RENDER_SIZE_LARGE, RENDER_SIZE_LARGE);
1157         DrawCallData                            drawCallData    (vertices);
1158         VulkanProgram                           vulkanProgram   (shaders);
1159         drawState.blendEnable           = true;
1160
1161         VulkanDrawContext                       drawContext(context, drawState, drawCallData, vulkanProgram);
1162         drawContext.draw();
1163
1164         const int numGrayPixels         = countPixels(drawContext.getColorPixels(), Vec4(0.5f, 0.5f, 0.5f, 1.0f), Vec4(0.02f, 0.02f, 0.02f, 0.0f));
1165         const int numExpectedPixels     = RENDER_SIZE_LARGE * RENDER_SIZE_LARGE;
1166
1167         return (numGrayPixels == numExpectedPixels ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
1168 }
1169
1170 } // ClipDistanceComplementarity ns
1171
1172 void addClippingTests (tcu::TestCaseGroup* clippingTestsGroup)
1173 {
1174         tcu::TestContext& testCtx = clippingTestsGroup->getTestContext();
1175
1176         // Clipping against the clip volume
1177         {
1178                 using namespace ClipVolume;
1179
1180                 static const VkPrimitiveTopology cases[] =
1181                 {
1182                         VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
1183                         VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
1184                         VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,
1185                         VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
1186                         VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY,
1187                         VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
1188                         VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY,
1189                         VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
1190                         VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY,
1191                         VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
1192                 };
1193
1194                 MovePtr<tcu::TestCaseGroup> clipVolumeGroup(new tcu::TestCaseGroup(testCtx, "clip_volume", "clipping with the clip volume"));
1195
1196                 // Fully inside the clip volume
1197                 {
1198                         MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "inside", ""));
1199
1200                         for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1201                                 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1202                                         group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), "", initPrograms, testPrimitivesInside, cases[caseNdx]);
1203
1204                         clipVolumeGroup->addChild(group.release());
1205                 }
1206
1207                 // Fully outside the clip volume
1208                 {
1209                         MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "outside", ""));
1210
1211                         for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1212                                 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1213                                         group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), "", initPrograms, testPrimitivesOutside, cases[caseNdx]);
1214
1215                         clipVolumeGroup->addChild(group.release());
1216                 }
1217
1218                 // Depth clamping
1219                 {
1220                         MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "depth_clamp", ""));
1221
1222                         for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1223                                 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1224                                         group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), "", initPrograms, testPrimitivesDepthClamp, cases[caseNdx]);
1225
1226                         clipVolumeGroup->addChild(group.release());
1227                 }
1228
1229                 // Large points and wide lines
1230                 {
1231                         // \note For both points and lines, if an unsupported size/width is selected, the nearest supported size will be chosen.
1232                         //       We do have to check for feature support though.
1233
1234                         MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "clipped", ""));
1235
1236                         addFunctionCaseWithPrograms(group.get(), "large_points", "", initProgramsPointSize, testLargePoints);
1237
1238                         addFunctionCaseWithPrograms<LineOrientation>(group.get(), "wide_lines_axis_aligned", "", initPrograms, testWideLines, LINE_ORIENTATION_AXIS_ALIGNED);
1239                         addFunctionCaseWithPrograms<LineOrientation>(group.get(), "wide_lines_diagonal",         "", initPrograms, testWideLines, LINE_ORIENTATION_DIAGONAL);
1240
1241                         clipVolumeGroup->addChild(group.release());
1242                 }
1243
1244                 clippingTestsGroup->addChild(clipVolumeGroup.release());
1245         }
1246
1247         // User-defined clip planes
1248         {
1249                 MovePtr<tcu::TestCaseGroup> clipDistanceGroup(new tcu::TestCaseGroup(testCtx, "user_defined", "user-defined clip planes"));
1250
1251                 // ClipDistance, CullDistance and maxCombinedClipAndCullDistances usage
1252                 {
1253                         using namespace ClipDistance;
1254
1255                         static const struct
1256                         {
1257                                 const char* const       groupName;
1258                                 const char* const       description;
1259                                 bool                            useCullDistance;
1260                         } caseGroups[] =
1261                         {
1262                                 { "clip_distance",              "use ClipDistance",                                                                             false },
1263                                 { "clip_cull_distance", "use ClipDistance and CullDistance at the same time",   true  },
1264                         };
1265
1266                         const deUint32 flagTessellation = 1u << 0;
1267                         const deUint32 flagGeometry             = 1u << 1;
1268
1269                         for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(caseGroups); ++groupNdx)
1270                         for (int indexingMode = 0; indexingMode < 2; ++indexingMode)
1271                         {
1272                                 const bool                      dynamicIndexing = (indexingMode == 1);
1273                                 const std::string       mainGroupName   = de::toString(caseGroups[groupNdx].groupName) + (dynamicIndexing ? "_dynamic_index" : "");
1274
1275                                 MovePtr<tcu::TestCaseGroup>     mainGroup(new tcu::TestCaseGroup(testCtx, mainGroupName.c_str(), ""));
1276
1277                                 for (deUint32 shaderMask = 0u; shaderMask <= (flagTessellation | flagGeometry); ++shaderMask)
1278                                 {
1279                                         const bool                      useTessellation = (shaderMask & flagTessellation) != 0;
1280                                         const bool                      useGeometry             = (shaderMask & flagGeometry) != 0;
1281                                         const std::string       shaderGroupName = std::string("vert") + (useTessellation ? "_tess" : "") + (useGeometry ? "_geom" : "");
1282
1283                                         MovePtr<tcu::TestCaseGroup>     shaderGroup(new tcu::TestCaseGroup(testCtx, shaderGroupName.c_str(), ""));
1284
1285                                         for (int numClipPlanes = 1; numClipPlanes <= MAX_CLIP_DISTANCES; ++numClipPlanes)
1286                                         {
1287                                                 const int                                       numCullPlanes   = (caseGroups[groupNdx].useCullDistance
1288                                                                                                                                                 ? std::min(static_cast<int>(MAX_CULL_DISTANCES), MAX_COMBINED_CLIP_AND_CULL_DISTANCES - numClipPlanes)
1289                                                                                                                                                 : 0);
1290                                                 const std::string                       caseName                = de::toString(numClipPlanes) + (numCullPlanes > 0 ? "_" + de::toString(numCullPlanes) : "");
1291                                                 const VkPrimitiveTopology       topology                = (useTessellation ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
1292
1293                                                 addFunctionCaseWithPrograms<CaseDefinition>(
1294                                                         shaderGroup.get(), caseName, caseGroups[groupNdx].description, initPrograms, testClipDistance,
1295                                                         CaseDefinition(topology, numClipPlanes, numCullPlanes, useTessellation, useGeometry, dynamicIndexing));
1296                                         }
1297                                         mainGroup->addChild(shaderGroup.release());
1298                                 }
1299                                 clipDistanceGroup->addChild(mainGroup.release());
1300                         }
1301                 }
1302
1303                 // Complementarity criterion (i.e. clipped and not clipped areas must add up to a complete primitive with no holes nor overlap)
1304                 {
1305                         using namespace ClipDistanceComplementarity;
1306
1307                         MovePtr<tcu::TestCaseGroup>     group(new tcu::TestCaseGroup(testCtx, "complementarity", ""));
1308
1309                         for (int numClipDistances = 1; numClipDistances <= MAX_CLIP_DISTANCES; ++numClipDistances)
1310                                 addFunctionCaseWithPrograms<int>(group.get(), de::toString(numClipDistances).c_str(), "", initPrograms, testComplementarity, numClipDistances);
1311
1312                         clippingTestsGroup->addChild(group.release());
1313                 }
1314
1315                 clippingTestsGroup->addChild(clipDistanceGroup.release());
1316         }
1317 }
1318
1319 } // anonymous
1320
1321 tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx)
1322 {
1323         return createTestGroup(testCtx, "clipping", "Clipping tests", addClippingTests);
1324 }
1325
1326 } // clipping
1327 } // vkt