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