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