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