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