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