Home | History | Annotate | Download | only in tessellation
      1 /*------------------------------------------------------------------------
      2  * Vulkan Conformance Tests
      3  * ------------------------
      4  *
      5  * Copyright (c) 2014 The Android Open Source Project
      6  * Copyright (c) 2016 The Khronos Group Inc.
      7  *
      8  * Licensed under the Apache License, Version 2.0 (the "License");
      9  * you may not use this file except in compliance with the License.
     10  * You may obtain a copy of the License at
     11  *
     12  *      http://www.apache.org/licenses/LICENSE-2.0
     13  *
     14  * Unless required by applicable law or agreed to in writing, software
     15  * distributed under the License is distributed on an "AS IS" BASIS,
     16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     17  * See the License for the specific language governing permissions and
     18  * limitations under the License.
     19  *
     20  *//*!
     21  * \file
     22  * \brief Tessellation Common Edge Tests
     23  *//*--------------------------------------------------------------------*/
     24 
     25 #include "vktTessellationCommonEdgeTests.hpp"
     26 #include "vktTestCaseUtil.hpp"
     27 #include "vktTessellationUtil.hpp"
     28 
     29 #include "tcuTestLog.hpp"
     30 #include "tcuTexture.hpp"
     31 
     32 #include "vkDefs.hpp"
     33 #include "vkQueryUtil.hpp"
     34 #include "vkBuilderUtil.hpp"
     35 #include "vkImageUtil.hpp"
     36 #include "vkTypeUtil.hpp"
     37 #include "vkStrUtil.hpp"
     38 
     39 #include "deUniquePtr.hpp"
     40 #include "deStringUtil.hpp"
     41 
     42 #include <string>
     43 #include <vector>
     44 
     45 namespace vkt
     46 {
     47 namespace tessellation
     48 {
     49 
     50 using namespace vk;
     51 
     52 namespace
     53 {
     54 
     55 enum CaseType
     56 {
     57 	CASETYPE_BASIC = 0,		//!< Order patch vertices such that when two patches share a vertex, it's at the same index for both.
     58 	CASETYPE_PRECISE,		//!< Vertex indices don't match like for CASETYPE_BASIC, but other measures are taken, using the 'precise' qualifier.
     59 
     60 	CASETYPE_LAST
     61 };
     62 
     63 struct CaseDefinition
     64 {
     65 	TessPrimitiveType	primitiveType;
     66 	SpacingMode			spacingMode;
     67 	CaseType			caseType;
     68 };
     69 
     70 //! Check that a certain rectangle in the image contains no black pixels.
     71 //! Returns true if an image successfully passess the verification.
     72 bool verifyResult (tcu::TestLog& log, const tcu::ConstPixelBufferAccess image)
     73 {
     74 	const int startX = static_cast<int>(0.15f * (float)image.getWidth());
     75 	const int endX	 = static_cast<int>(0.85f * (float)image.getWidth());
     76 	const int startY = static_cast<int>(0.15f * (float)image.getHeight());
     77 	const int endY	 = static_cast<int>(0.85f * (float)image.getHeight());
     78 
     79 	for (int y = startY; y < endY; ++y)
     80 	for (int x = startX; x < endX; ++x)
     81 	{
     82 		const tcu::Vec4 pixel = image.getPixel(x, y);
     83 
     84 		if (pixel.x() == 0 && pixel.y() == 0 && pixel.z() == 0)
     85 		{
     86 			log << tcu::TestLog::Message << "Failure: there seem to be cracks in the rendered result" << tcu::TestLog::EndMessage
     87 				<< tcu::TestLog::Message << "Note: pixel with zero r, g and b channels found at " << tcu::IVec2(x, y) << tcu::TestLog::EndMessage;
     88 
     89 			return false;
     90 		}
     91 	}
     92 
     93 	log << tcu::TestLog::Message << "Success: there seem to be no cracks in the rendered result" << tcu::TestLog::EndMessage;
     94 
     95 	return true;
     96 }
     97 
     98 void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
     99 {
    100 	DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
    101 
    102 	// Vertex shader
    103 	{
    104 		std::ostringstream src;
    105 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    106 			<< "\n"
    107 			<< "layout(location = 0) in highp vec2  in_v_position;\n"
    108 			<< "layout(location = 1) in highp float in_v_tessParam;\n"
    109 			<< "\n"
    110 			<< "layout(location = 0) out highp vec2  in_tc_position;\n"
    111 			<< "layout(location = 1) out highp float in_tc_tessParam;\n"
    112 			<< "\n"
    113 			<< "void main (void)\n"
    114 			<< "{\n"
    115 			<< "    in_tc_position = in_v_position;\n"
    116 			<< "    in_tc_tessParam = in_v_tessParam;\n"
    117 			<< "}\n";
    118 
    119 		programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
    120 	}
    121 
    122 	// Tessellation control shader
    123 	{
    124 		const int numVertices = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
    125 
    126 		std::ostringstream src;
    127 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    128 			<< "#extension GL_EXT_tessellation_shader : require\n"
    129 			<< (caseDef.caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "")
    130 			<< "\n"
    131 			<< "layout(vertices = " << numVertices << ") out;\n"
    132 			<< "\n"
    133 			<< "layout(location = 0) in highp vec2  in_tc_position[];\n"
    134 			<< "layout(location = 1) in highp float in_tc_tessParam[];\n"
    135 			<< "\n"
    136 			<< "layout(location = 0) out highp vec2 in_te_position[];\n"
    137 			<< "\n"
    138 			<< (caseDef.caseType == CASETYPE_PRECISE ? "precise gl_TessLevelOuter;\n\n" : "")
    139 			<< "void main (void)\n"
    140 			<< "{\n"
    141 			<< "    in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
    142 			<< "\n"
    143 			<< "    gl_TessLevelInner[0] = 5.0;\n"
    144 			<< "    gl_TessLevelInner[1] = 5.0;\n"
    145 			<< "\n"
    146 			<< (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
    147 				"    gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[2]);\n"
    148 				"    gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[0]);\n"
    149 				"    gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[1]);\n"
    150 				: caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ?
    151 				"    gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[2]);\n"
    152 				"    gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[0]);\n"
    153 				"    gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[3] + in_tc_tessParam[1]);\n"
    154 				"    gl_TessLevelOuter[3] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[3]);\n"
    155 				: "")
    156 			<< "}\n";
    157 
    158 		programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
    159 	}
    160 
    161 	// Tessellation evaluation shader
    162 	{
    163 		std::ostringstream primitiveSpecificCode;
    164 		if (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
    165 			primitiveSpecificCode
    166 			<< "    highp vec2 pos = gl_TessCoord.x*in_te_position[0] + gl_TessCoord.y*in_te_position[1] + gl_TessCoord.z*in_te_position[2];\n"
    167 			<< "\n"
    168 			<< "    highp float f = sqrt(3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z))) * 0.5 + 0.5;\n"
    169 			<< "    in_f_color = vec4(gl_TessCoord*f, 1.0);\n";
    170 		else if (caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
    171 			primitiveSpecificCode
    172 			<< (caseDef.caseType == CASETYPE_BASIC ?
    173 				"    highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0]\n"
    174 				"                   + (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1]\n"
    175 				"                   + (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[2]\n"
    176 				"                   + (    gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[3];\n"
    177 				: caseDef.caseType == CASETYPE_PRECISE ?
    178 				"    highp vec2 a = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0];\n"
    179 				"    highp vec2 b = (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1];\n"
    180 				"    highp vec2 c = (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[2];\n"
    181 				"    highp vec2 d = (    gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[3];\n"
    182 				"    highp vec2 pos = a+b+c+d;\n"
    183 				: "")
    184 			<< "\n"
    185 			<< "    highp float f = sqrt(1.0 - 2.0 * max(abs(gl_TessCoord.x - 0.5), abs(gl_TessCoord.y - 0.5)))*0.5 + 0.5;\n"
    186 			<< "    in_f_color = vec4(0.1, gl_TessCoord.xy*f, 1.0);\n";
    187 
    188 		std::ostringstream src;
    189 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    190 			<< "#extension GL_EXT_tessellation_shader : require\n"
    191 			<< (caseDef.caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "")
    192 			<< "\n"
    193 			<< "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
    194 						 << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
    195 			<< "\n"
    196 			<< "layout(location = 0) in highp vec2 in_te_position[];\n"
    197 			<< "\n"
    198 			<< "layout(location = 0) out mediump vec4 in_f_color;\n"
    199 			<< "\n"
    200 			<< (caseDef.caseType == CASETYPE_PRECISE ? "precise gl_Position;\n\n" : "")
    201 			<< "void main (void)\n"
    202 			<< "{\n"
    203 			<< primitiveSpecificCode.str()
    204 			<< "\n"
    205 			<< "    // Offset the position slightly, based on the parity of the bits in the float representation.\n"
    206 			<< "    // This is done to detect possible small differences in edge vertex positions between patches.\n"
    207 			<< "    uvec2 bits = floatBitsToUint(pos);\n"
    208 			<< "    uint numBits = 0u;\n"
    209 			<< "    for (uint i = 0u; i < 32u; i++)\n"
    210 			<< "        numBits += ((bits[0] >> i) & 1u) + ((bits[1] >> i) & 1u);\n"
    211 			<< "    pos += float(numBits&1u)*0.04;\n"
    212 			<< "\n"
    213 			<< "    gl_Position = vec4(pos, 0.0, 1.0);\n"
    214 			<< "}\n";
    215 
    216 		programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
    217 	}
    218 
    219 	// Fragment shader
    220 	{
    221 		std::ostringstream src;
    222 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    223 			<< "\n"
    224 			<< "layout(location = 0) in mediump vec4 in_f_color;\n"
    225 			<< "\n"
    226 			<< "layout(location = 0) out mediump vec4 o_color;\n"
    227 			<< "\n"
    228 			<< "void main (void)\n"
    229 			<< "{\n"
    230 			<< "    o_color = in_f_color;\n"
    231 			<< "}\n";
    232 
    233 		programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
    234 	}
    235 }
    236 
    237 //! Generic test code used by all test cases.
    238 tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
    239 {
    240 	DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
    241 	DE_ASSERT(caseDef.caseType == CASETYPE_BASIC || caseDef.caseType == CASETYPE_PRECISE);
    242 
    243 	requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
    244 
    245 	const DeviceInterface&	vk					= context.getDeviceInterface();
    246 	const VkDevice			device				= context.getDevice();
    247 	const VkQueue			queue				= context.getUniversalQueue();
    248 	const deUint32			queueFamilyIndex	= context.getUniversalQueueFamilyIndex();
    249 	Allocator&				allocator			= context.getDefaultAllocator();
    250 
    251 	// Prepare test data
    252 
    253 	std::vector<float>		gridPosComps;
    254 	std::vector<float>		gridTessParams;
    255 	std::vector<deUint16>	gridIndices;
    256 
    257 	{
    258 		const int gridWidth				= 4;
    259 		const int gridHeight			= 4;
    260 		const int numVertices			= (gridWidth+1)*(gridHeight+1);
    261 		const int numIndices			= gridWidth*gridHeight * (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3*2 : 4);
    262 		const int numPosCompsPerVertex	= 2;
    263 		const int totalNumPosComps		= numPosCompsPerVertex*numVertices;
    264 
    265 		gridPosComps.reserve(totalNumPosComps);
    266 		gridTessParams.reserve(numVertices);
    267 		gridIndices.reserve(numIndices);
    268 
    269 		{
    270 			for (int i = 0; i < gridHeight+1; ++i)
    271 			for (int j = 0; j < gridWidth+1; ++j)
    272 			{
    273 				gridPosComps.push_back(-1.0f + 2.0f * ((float)j + 0.5f) / (float)(gridWidth+1));
    274 				gridPosComps.push_back(-1.0f + 2.0f * ((float)i + 0.5f) / (float)(gridHeight+1));
    275 				gridTessParams.push_back((float)(i*(gridWidth+1) + j) / (float)(numVertices-1));
    276 			}
    277 		}
    278 
    279 		// Generate patch vertex indices.
    280 		// \note If CASETYPE_BASIC, the vertices are ordered such that when multiple
    281 		//		 triangles/quads share a vertex, it's at the same index for everyone.
    282 
    283 		if (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
    284 		{
    285 			for (int i = 0; i < gridHeight; i++)
    286 			for (int j = 0; j < gridWidth; j++)
    287 			{
    288 				const deUint16 corners[4] =
    289 				{
    290 					(deUint16)((i+0)*(gridWidth+1) + j+0),
    291 					(deUint16)((i+0)*(gridWidth+1) + j+1),
    292 					(deUint16)((i+1)*(gridWidth+1) + j+0),
    293 					(deUint16)((i+1)*(gridWidth+1) + j+1)
    294 				};
    295 
    296 				const int secondTriangleVertexIndexOffset = caseDef.caseType == CASETYPE_BASIC   ? 0 : 1;
    297 
    298 				for (int k = 0; k < 3; k++)
    299 					gridIndices.push_back(corners[(k+0 + i + (2-j%3)) % 3]);
    300 				for (int k = 0; k < 3; k++)
    301 					gridIndices.push_back(corners[(k+2 + i + (2-j%3) + secondTriangleVertexIndexOffset) % 3 + 1]);
    302 			}
    303 		}
    304 		else if (caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
    305 		{
    306 			for (int i = 0; i < gridHeight; ++i)
    307 			for (int j = 0; j < gridWidth; ++j)
    308 			{
    309 				for (int m = 0; m < 2; m++)
    310 				for (int n = 0; n < 2; n++)
    311 					gridIndices.push_back((deUint16)((i+(i+m)%2)*(gridWidth+1) + j+(j+n)%2));
    312 
    313 				if (caseDef.caseType == CASETYPE_PRECISE && (i+j) % 2 == 0)
    314 					std::reverse(gridIndices.begin() + (gridIndices.size() - 4),
    315 								 gridIndices.begin() + gridIndices.size());
    316 			}
    317 		}
    318 		else
    319 			DE_ASSERT(false);
    320 
    321 		DE_ASSERT(static_cast<int>(gridPosComps.size()) == totalNumPosComps);
    322 		DE_ASSERT(static_cast<int>(gridTessParams.size()) == numVertices);
    323 		DE_ASSERT(static_cast<int>(gridIndices.size()) == numIndices);
    324 	}
    325 
    326 	// Vertex input buffer: we put both attributes and indices in here.
    327 
    328 	const VkDeviceSize vertexDataSizeBytes    = sizeInBytes(gridPosComps) + sizeInBytes(gridTessParams) + sizeInBytes(gridIndices);
    329 	const std::size_t  vertexPositionsOffset  = 0;
    330 	const std::size_t  vertexTessParamsOffset = sizeInBytes(gridPosComps);
    331 	const std::size_t  vertexIndicesOffset    = vertexTessParamsOffset + sizeInBytes(gridTessParams);
    332 
    333 	const Buffer vertexBuffer(vk, device, allocator,
    334 		makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT), MemoryRequirement::HostVisible);
    335 
    336 	{
    337 		const Allocation& alloc = vertexBuffer.getAllocation();
    338 		deUint8* const pData = static_cast<deUint8*>(alloc.getHostPtr());
    339 
    340 		deMemcpy(pData + vertexPositionsOffset,  &gridPosComps[0],   static_cast<std::size_t>(sizeInBytes(gridPosComps)));
    341 		deMemcpy(pData + vertexTessParamsOffset, &gridTessParams[0], static_cast<std::size_t>(sizeInBytes(gridTessParams)));
    342 		deMemcpy(pData + vertexIndicesOffset,    &gridIndices[0],    static_cast<std::size_t>(sizeInBytes(gridIndices)));
    343 
    344 		flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
    345 		// No barrier needed, flushed memory is automatically visible
    346 	}
    347 
    348 	// Color attachment
    349 
    350 	const tcu::IVec2			  renderSize				 = tcu::IVec2(256, 256);
    351 	const VkFormat				  colorFormat				 = VK_FORMAT_R8G8B8A8_UNORM;
    352 	const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
    353 	const Image					  colorAttachmentImage		 (vk, device, allocator,
    354 															 makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
    355 															 MemoryRequirement::Any);
    356 
    357 	// Color output buffer: image will be copied here for verification
    358 
    359 	const VkDeviceSize	colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
    360 	const Buffer		colorBuffer			 (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
    361 
    362 	// Pipeline
    363 
    364 	const Unique<VkImageView>	   colorAttachmentView	(makeImageView						 (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
    365 	const Unique<VkRenderPass>	   renderPass			(makeRenderPass						 (vk, device, colorFormat));
    366 	const Unique<VkFramebuffer>	   framebuffer			(makeFramebuffer					 (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
    367 	const Unique<VkCommandPool>	   cmdPool				(makeCommandPool					 (vk, device, queueFamilyIndex));
    368 	const Unique<VkCommandBuffer>  cmdBuffer			(allocateCommandBuffer				 (vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
    369 	const Unique<VkPipelineLayout> pipelineLayout		(makePipelineLayoutWithoutDescriptors(vk, device));
    370 
    371 	const int inPatchSize = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
    372 	const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
    373 		.setRenderSize		  (renderSize)
    374 		.setPatchControlPoints(inPatchSize)
    375 		.addVertexBinding	  (makeVertexInputBindingDescription(0u, sizeof(tcu::Vec2), VK_VERTEX_INPUT_RATE_VERTEX))
    376 		.addVertexBinding	  (makeVertexInputBindingDescription(1u, sizeof(float),     VK_VERTEX_INPUT_RATE_VERTEX))
    377 		.addVertexAttribute	  (makeVertexInputAttributeDescription(0u, 0u, VK_FORMAT_R32G32_SFLOAT, 0u))
    378 		.addVertexAttribute	  (makeVertexInputAttributeDescription(1u, 1u, VK_FORMAT_R32_SFLOAT,    0u))
    379 		.setShader			  (vk, device, VK_SHADER_STAGE_VERTEX_BIT,					context.getBinaryCollection().get("vert"), DE_NULL)
    380 		.setShader			  (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,	context.getBinaryCollection().get("tesc"), DE_NULL)
    381 		.setShader			  (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL)
    382 		.setShader			  (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,				context.getBinaryCollection().get("frag"), DE_NULL)
    383 		.build				  (vk, device, *pipelineLayout, *renderPass));
    384 
    385 	// Draw commands
    386 
    387 	beginCommandBuffer(vk, *cmdBuffer);
    388 
    389 	// Change color attachment image layout
    390 	{
    391 		const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
    392 			(VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
    393 			VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    394 			*colorAttachmentImage, colorImageSubresourceRange);
    395 
    396 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
    397 			0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
    398 	}
    399 
    400 	// Begin render pass
    401 	{
    402 		const VkRect2D renderArea = {
    403 			makeOffset2D(0, 0),
    404 			makeExtent2D(renderSize.x(), renderSize.y()),
    405 		};
    406 		const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
    407 
    408 		beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
    409 	}
    410 
    411 	vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
    412 	{
    413 		const VkBuffer buffers[] = { *vertexBuffer, *vertexBuffer };
    414 		const VkDeviceSize offsets[] = { vertexPositionsOffset, vertexTessParamsOffset, };
    415 		vk.cmdBindVertexBuffers(*cmdBuffer, 0u, DE_LENGTH_OF_ARRAY(buffers), buffers, offsets);
    416 
    417 		vk.cmdBindIndexBuffer(*cmdBuffer, *vertexBuffer, vertexIndicesOffset, VK_INDEX_TYPE_UINT16);
    418 	}
    419 
    420 	vk.cmdDrawIndexed(*cmdBuffer, static_cast<deUint32>(gridIndices.size()), 1u, 0u, 0, 0u);
    421 	endRenderPass(vk, *cmdBuffer);
    422 
    423 	// Copy render result to a host-visible buffer
    424 	{
    425 		const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
    426 			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
    427 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
    428 			*colorAttachmentImage, colorImageSubresourceRange);
    429 
    430 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
    431 			0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
    432 	}
    433 	{
    434 		const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 1), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
    435 		vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
    436 	}
    437 	{
    438 		const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
    439 			VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
    440 
    441 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
    442 			0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
    443 	}
    444 
    445 	endCommandBuffer(vk, *cmdBuffer);
    446 	submitCommandsAndWait(vk, device, queue, *cmdBuffer);
    447 
    448 	{
    449 		// Log the result image.
    450 
    451 		const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
    452 		invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
    453 		const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
    454 
    455 		tcu::TestLog& log = context.getTestContext().getLog();
    456 		log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess)
    457 			<< tcu::TestLog::Message
    458 			<< "Note: coloring is done to clarify the positioning and orientation of the "
    459 			<< (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "triangles" :
    460 				caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS	 ? "quads"	   : "")
    461 			<< "; the color of a vertex corresponds to the index of that vertex in the patch"
    462 			<< tcu::TestLog::EndMessage;
    463 
    464 		if (caseDef.caseType == CASETYPE_BASIC)
    465 			log << tcu::TestLog::Message << "Note: each shared vertex has the same index among the primitives it belongs to" << tcu::TestLog::EndMessage;
    466 		else if (caseDef.caseType == CASETYPE_PRECISE)
    467 			log << tcu::TestLog::Message << "Note: the 'precise' qualifier is used to avoid cracks between primitives" << tcu::TestLog::EndMessage;
    468 		else
    469 			DE_ASSERT(false);
    470 
    471 		// Verify the result.
    472 
    473 		const bool ok = verifyResult(log, imagePixelAccess);
    474 		return (ok ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
    475 	}
    476 }
    477 
    478 std::string getCaseName (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const CaseType caseType)
    479 {
    480 	std::ostringstream str;
    481 	str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getSpacingModeShaderName(spacingMode)
    482 		<< (caseType == CASETYPE_PRECISE ? "_precise" : "");
    483 	return str.str();
    484 }
    485 
    486 } // anonymous
    487 
    488 //! These tests correspond to dEQP-GLES31.functional.tessellation.common_edge.*
    489 tcu::TestCaseGroup* createCommonEdgeTests (tcu::TestContext& testCtx)
    490 {
    491 	de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "common_edge", "Draw multiple adjacent shapes and check that no cracks appear between them"));
    492 
    493 	static const TessPrimitiveType primitiveTypes[] =
    494 	{
    495 		TESSPRIMITIVETYPE_TRIANGLES,
    496 		TESSPRIMITIVETYPE_QUADS,
    497 	};
    498 
    499 	for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx)
    500 	for (int caseTypeNdx = 0; caseTypeNdx < CASETYPE_LAST; ++caseTypeNdx)
    501 	for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
    502 	{
    503 		const TessPrimitiveType primitiveType = primitiveTypes[primitiveTypeNdx];
    504 		const CaseType			caseType	  = static_cast<CaseType>(caseTypeNdx);
    505 		const SpacingMode		spacingMode   = static_cast<SpacingMode>(spacingModeNdx);
    506 		const CaseDefinition	caseDef		  = { primitiveType, spacingMode, caseType };
    507 
    508 		addFunctionCaseWithPrograms(group.get(), getCaseName(primitiveType, spacingMode, caseType), "", initPrograms, test, caseDef);
    509 	}
    510 
    511 	return group.release();
    512 }
    513 
    514 } // tessellation
    515 } // vkt
    516