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