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 Geometry Interaction - Grid render (limits, scatter)
     23 *//*--------------------------------------------------------------------*/
     24 
     25 #include "vktTessellationGeometryGridRenderTests.hpp"
     26 #include "vktTestCaseUtil.hpp"
     27 #include "vktTessellationUtil.hpp"
     28 
     29 #include "tcuTestLog.hpp"
     30 #include "tcuTextureUtil.hpp"
     31 #include "tcuSurface.hpp"
     32 #include "tcuRGBA.hpp"
     33 
     34 #include "vkDefs.hpp"
     35 #include "vkQueryUtil.hpp"
     36 #include "vkBuilderUtil.hpp"
     37 #include "vkTypeUtil.hpp"
     38 #include "vkImageUtil.hpp"
     39 
     40 #include "deUniquePtr.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 Constants
     56 {
     57 	RENDER_SIZE = 256,
     58 };
     59 
     60 enum FlagBits
     61 {
     62 	FLAG_TESSELLATION_MAX_SPEC			= 1u << 0,
     63 	FLAG_GEOMETRY_MAX_SPEC				= 1u << 1,
     64 	FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC	= 1u << 2,
     65 
     66 	FLAG_GEOMETRY_SCATTER_INSTANCES		= 1u << 3,
     67 	FLAG_GEOMETRY_SCATTER_PRIMITIVES	= 1u << 4,
     68 	FLAG_GEOMETRY_SEPARATE_PRIMITIVES	= 1u << 5, //!< if set, geometry shader outputs separate grid cells and not continuous slices
     69 	FLAG_GEOMETRY_SCATTER_LAYERS		= 1u << 6,
     70 };
     71 typedef deUint32 Flags;
     72 
     73 class GridRenderTestCase : public TestCase
     74 {
     75 public:
     76 	void			initPrograms			(vk::SourceCollections& programCollection) const;
     77 	TestInstance*	createInstance			(Context& context) const;
     78 
     79 					GridRenderTestCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description, const Flags flags);
     80 
     81 private:
     82 	const Flags		m_flags;
     83 	const int		m_tessGenLevel;
     84 	const int		m_numGeometryInvocations;
     85 	const int		m_numLayers;
     86 	int				m_numGeometryPrimitivesPerInvocation;
     87 };
     88 
     89 GridRenderTestCase::GridRenderTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const Flags flags)
     90 	: TestCase					(testCtx, name, description)
     91 	, m_flags					(flags)
     92 	, m_tessGenLevel			((m_flags & FLAG_TESSELLATION_MAX_SPEC)			? 64 : 5)
     93 	, m_numGeometryInvocations	((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC)	? 32 : 4)
     94 	, m_numLayers				((m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)		? 8  : 1)
     95 {
     96 	DE_ASSERT(((flags & (FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS)) != 0) == ((flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0));
     97 
     98 	testCtx.getLog()
     99 		<< tcu::TestLog::Message
    100 		<< "Testing tessellation and geometry shaders that output a large number of primitives.\n"
    101 		<< getDescription()
    102 		<< tcu::TestLog::EndMessage;
    103 
    104 	if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
    105 		m_testCtx.getLog() << tcu::TestLog::Message << "Rendering to 2d texture array, numLayers = " << m_numLayers << tcu::TestLog::EndMessage;
    106 
    107 	m_testCtx.getLog()
    108 		<< tcu::TestLog::Message
    109 		<< "Tessellation level: " << m_tessGenLevel << ", mode = quad.\n"
    110 		<< "\tEach input patch produces " << (m_tessGenLevel*m_tessGenLevel) << " (" << (m_tessGenLevel*m_tessGenLevel*2) << " triangles)\n"
    111 		<< tcu::TestLog::EndMessage;
    112 
    113 	int geometryOutputComponents	  = 0;
    114 	int geometryOutputVertices		  = 0;
    115 	int geometryTotalOutputComponents = 0;
    116 
    117 	if (m_flags & FLAG_GEOMETRY_MAX_SPEC)
    118 	{
    119 		m_testCtx.getLog() << tcu::TestLog::Message << "Using geometry shader minimum maximum output limits." << tcu::TestLog::EndMessage;
    120 
    121 		geometryOutputComponents	  = 64;
    122 		geometryOutputVertices		  = 256;
    123 		geometryTotalOutputComponents = 1024;
    124 	}
    125 	else
    126 	{
    127 		geometryOutputComponents	  = 64;
    128 		geometryOutputVertices		  = 16;
    129 		geometryTotalOutputComponents = 1024;
    130 	}
    131 
    132 	if ((m_flags & FLAG_GEOMETRY_MAX_SPEC) || (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC))
    133 	{
    134 		tcu::MessageBuilder msg(&m_testCtx.getLog());
    135 
    136 		msg << "Geometry shader, targeting following limits:\n";
    137 
    138 		if (m_flags & FLAG_GEOMETRY_MAX_SPEC)
    139 			msg	<< "\tmaxGeometryOutputComponents = "	   << geometryOutputComponents << "\n"
    140 				<< "\tmaxGeometryOutputVertices = "		   << geometryOutputVertices << "\n"
    141 				<< "\tmaxGeometryTotalOutputComponents = " << geometryTotalOutputComponents << "\n";
    142 
    143 		if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC)
    144 			msg << "\tmaxGeometryShaderInvocations = "	   << m_numGeometryInvocations;
    145 
    146 		msg << tcu::TestLog::EndMessage;
    147 	}
    148 
    149 	const bool	separatePrimitives				  = (m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0;
    150 	const int	numComponentsPerVertex			  = 8; // vec4 pos, vec4 color
    151 	int			numVerticesPerInvocation		  = 0;
    152 	int			geometryVerticesPerPrimitive	  = 0;
    153 	int			geometryPrimitivesOutPerPrimitive = 0;
    154 
    155 	if (separatePrimitives)
    156 	{
    157 		const int	numComponentLimit		 = geometryTotalOutputComponents / (4 * numComponentsPerVertex);
    158 		const int	numOutputLimit			 = geometryOutputVertices / 4;
    159 
    160 		m_numGeometryPrimitivesPerInvocation = de::min(numComponentLimit, numOutputLimit);
    161 		numVerticesPerInvocation			 = m_numGeometryPrimitivesPerInvocation * 4;
    162 	}
    163 	else
    164 	{
    165 		// If FLAG_GEOMETRY_SEPARATE_PRIMITIVES is not set, geometry shader fills a rectangle area in slices.
    166 		// Each slice is a triangle strip and is generated by a single shader invocation.
    167 		// One slice with 4 segment ends (nodes) and 3 segments:
    168 		//    .__.__.__.
    169 		//    |\ |\ |\ |
    170 		//    |_\|_\|_\|
    171 
    172 		const int	numSliceNodesComponentLimit	= geometryTotalOutputComponents / (2 * numComponentsPerVertex);			// each node 2 vertices
    173 		const int	numSliceNodesOutputLimit	= geometryOutputVertices / 2;											// each node 2 vertices
    174 		const int	numSliceNodes				= de::min(numSliceNodesComponentLimit, numSliceNodesOutputLimit);
    175 
    176 		numVerticesPerInvocation				= numSliceNodes * 2;
    177 		m_numGeometryPrimitivesPerInvocation	= (numSliceNodes - 1) * 2;
    178 	}
    179 
    180 	geometryVerticesPerPrimitive	  = numVerticesPerInvocation * m_numGeometryInvocations;
    181 	geometryPrimitivesOutPerPrimitive = m_numGeometryPrimitivesPerInvocation * m_numGeometryInvocations;
    182 
    183 	m_testCtx.getLog()
    184 		<< tcu::TestLog::Message
    185 		<< "Geometry shader:\n"
    186 		<< "\tTotal output vertex count per invocation: "		  << numVerticesPerInvocation << "\n"
    187 		<< "\tTotal output primitive count per invocation: "	  << m_numGeometryPrimitivesPerInvocation << "\n"
    188 		<< "\tNumber of invocations per primitive: "			  << m_numGeometryInvocations << "\n"
    189 		<< "\tTotal output vertex count per input primitive: "	  << geometryVerticesPerPrimitive << "\n"
    190 		<< "\tTotal output primitive count per input primitive: " << geometryPrimitivesOutPerPrimitive << "\n"
    191 		<< tcu::TestLog::EndMessage;
    192 
    193 	m_testCtx.getLog()
    194 		<< tcu::TestLog::Message
    195 		<< "Program:\n"
    196 		<< "\tTotal program output vertices count per input patch: "  << (m_tessGenLevel*m_tessGenLevel*2 * geometryVerticesPerPrimitive) << "\n"
    197 		<< "\tTotal program output primitive count per input patch: " << (m_tessGenLevel*m_tessGenLevel*2 * geometryPrimitivesOutPerPrimitive) << "\n"
    198 		<< tcu::TestLog::EndMessage;
    199 }
    200 
    201 void GridRenderTestCase::initPrograms (SourceCollections& programCollection) const
    202 {
    203 	// Vertex shader
    204 	{
    205 		std::ostringstream src;
    206 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    207 			<< "\n"
    208 			<< "void main (void)\n"
    209 			<< "{\n"
    210 			<< "    gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
    211 			<< "}\n";
    212 
    213 		programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
    214 	}
    215 
    216 	// Fragment shader
    217 	{
    218 		std::ostringstream src;
    219 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    220 			<< "layout(location = 0) flat in highp   vec4 v_color;\n"
    221 			<< "layout(location = 0) out     mediump vec4 fragColor;\n"
    222 			<< "\n"
    223 			<< "void main (void)\n"
    224 			<< "{\n"
    225 			<< "    fragColor = v_color;\n"
    226 			<< "}\n";
    227 
    228 		programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
    229 	}
    230 
    231 	// Tessellation control
    232 	{
    233 		std::ostringstream src;
    234 		src <<	glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    235 				"#extension GL_EXT_tessellation_shader : require\n"
    236 				"layout(vertices = 1) out;\n"
    237 				"\n"
    238 				"void main (void)\n"
    239 				"{\n"
    240 				"    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
    241 				"    gl_TessLevelInner[0] = float(" << m_tessGenLevel << ");\n"
    242 				"    gl_TessLevelInner[1] = float(" << m_tessGenLevel << ");\n"
    243 				"    gl_TessLevelOuter[0] = float(" << m_tessGenLevel << ");\n"
    244 				"    gl_TessLevelOuter[1] = float(" << m_tessGenLevel << ");\n"
    245 				"    gl_TessLevelOuter[2] = float(" << m_tessGenLevel << ");\n"
    246 				"    gl_TessLevelOuter[3] = float(" << m_tessGenLevel << ");\n"
    247 				"}\n";
    248 
    249 		programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
    250 	}
    251 
    252 	// Tessellation evaluation
    253 	{
    254 		std::ostringstream src;
    255 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    256 			<< "#extension GL_EXT_tessellation_shader : require\n"
    257 			<< "layout(quads) in;\n"
    258 			<< "\n"
    259 			<< "layout(location = 0) out mediump ivec2 v_tessellationGridPosition;\n"
    260 			<< "\n"
    261 			<< "// note: No need to use precise gl_Position since position does not depend on order\n"
    262 			<< "void main (void)\n"
    263 			<< "{\n";
    264 
    265 		if (m_flags & (FLAG_GEOMETRY_SCATTER_INSTANCES | FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS))
    266 			src << "    // Cover only a small area in a corner. The area will be expanded in geometry shader to cover whole viewport\n"
    267 				<< "    gl_Position = vec4(gl_TessCoord.x * 0.3 - 1.0, gl_TessCoord.y * 0.3 - 1.0, 0.0, 1.0);\n";
    268 		else
    269 			src << "    // Fill the whole viewport\n"
    270 				<< "    gl_Position = vec4(gl_TessCoord.x * 2.0 - 1.0, gl_TessCoord.y * 2.0 - 1.0, 0.0, 1.0);\n";
    271 
    272 		src << "    // Calculate position in tessellation grid\n"
    273 			<< "    v_tessellationGridPosition = ivec2(round(gl_TessCoord.xy * float(" << m_tessGenLevel << ")));\n"
    274 			<< "}\n";
    275 
    276 		programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
    277 	}
    278 
    279 	// Geometry shader
    280 	{
    281 		const int numInvocations = m_numGeometryInvocations;
    282 		const int numPrimitives  = m_numGeometryPrimitivesPerInvocation;
    283 
    284 		std::ostringstream src;
    285 
    286 		src	<< glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    287 			<< "#extension GL_EXT_geometry_shader : require\n"
    288 			<< "layout(triangles, invocations = " << numInvocations << ") in;\n"
    289 			<< "layout(triangle_strip, max_vertices = " << ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) ? (4 * numPrimitives) : (numPrimitives + 2)) << ") out;\n"
    290 			<< "\n"
    291 			<< "layout(location = 0) in       mediump ivec2 v_tessellationGridPosition[];\n"
    292 			<< "layout(location = 0) flat out highp   vec4  v_color;\n"
    293 			<< "\n"
    294 			<< "void main (void)\n"
    295 			<< "{\n"
    296 			<< "    const float equalThreshold = 0.001;\n"
    297 			<< "    const float gapOffset = 0.0001; // subdivision performed by the geometry shader might produce gaps. Fill potential gaps by enlarging the output slice a little.\n"
    298 			<< "\n"
    299 			<< "    // Input triangle is generated from an axis-aligned rectangle by splitting it in half\n"
    300 			<< "    // Original rectangle can be found by finding the bounding AABB of the triangle\n"
    301 			<< "    vec4 aabb = vec4(min(gl_in[0].gl_Position.x, min(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
    302 			<< "                     min(gl_in[0].gl_Position.y, min(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)),\n"
    303 			<< "                     max(gl_in[0].gl_Position.x, max(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
    304 			<< "                     max(gl_in[0].gl_Position.y, max(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)));\n"
    305 			<< "\n"
    306 			<< "    // Location in tessellation grid\n"
    307 			<< "    ivec2 gridPosition = ivec2(min(v_tessellationGridPosition[0], min(v_tessellationGridPosition[1], v_tessellationGridPosition[2])));\n"
    308 			<< "\n"
    309 			<< "    // Which triangle of the two that split the grid cell\n"
    310 			<< "    int numVerticesOnBottomEdge = 0;\n"
    311 			<< "    for (int ndx = 0; ndx < 3; ++ndx)\n"
    312 			<< "        if (abs(gl_in[ndx].gl_Position.y - aabb.w) < equalThreshold)\n"
    313 			<< "            ++numVerticesOnBottomEdge;\n"
    314 			<< "    bool isBottomTriangle = numVerticesOnBottomEdge == 2;\n"
    315 			<< "\n";
    316 
    317 		if (m_flags & FLAG_GEOMETRY_SCATTER_PRIMITIVES)
    318 		{
    319 			// scatter primitives
    320 			src << "    // Draw grid cells\n"
    321 				<< "    int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
    322 				<< "    for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n"
    323 				<< "    {\n"
    324 				<< "        ivec2 dstGridSize = ivec2(" << m_tessGenLevel << " * " << numPrimitives << ", 2 * " << m_tessGenLevel << " * " << numInvocations << ");\n"
    325 				<< "        ivec2 dstGridNdx = ivec2(" << m_tessGenLevel << " * ndx + gridPosition.x, " << m_tessGenLevel << " * inputTriangleNdx + 2 * gridPosition.y + ndx * 127) % dstGridSize;\n"
    326 				<< "        vec4 dstArea;\n"
    327 				<< "        dstArea.x = float(dstGridNdx.x)   / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
    328 				<< "        dstArea.y = float(dstGridNdx.y)   / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
    329 				<< "        dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
    330 				<< "        dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
    331 				<< "\n"
    332 				<< "        vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
    333 				<< "        vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
    334 				<< "        vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
    335 				<< "\n"
    336 				<< "        gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
    337 				<< "        v_color = outputColor;\n"
    338 				<< "        EmitVertex();\n"
    339 				<< "\n"
    340 				<< "        gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
    341 				<< "        v_color = outputColor;\n"
    342 				<< "        EmitVertex();\n"
    343 				<< "\n"
    344 				<< "        gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
    345 				<< "        v_color = outputColor;\n"
    346 				<< "        EmitVertex();\n"
    347 				<< "\n"
    348 				<< "        gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
    349 				<< "        v_color = outputColor;\n"
    350 				<< "        EmitVertex();\n"
    351 				<< "        EndPrimitive();\n"
    352 				<< "    }\n";
    353 		}
    354 		else if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
    355 		{
    356 			// Number of subrectangle instances = num layers
    357 			DE_ASSERT(m_numLayers == numInvocations * 2);
    358 
    359 			src << "    // Draw grid cells, send each primitive to a separate layer\n"
    360 				<< "    int baseLayer = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
    361 				<< "    for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n"
    362 				<< "    {\n"
    363 				<< "        ivec2 dstGridSize = ivec2(" << m_tessGenLevel << " * " << numPrimitives << ", " << m_tessGenLevel << ");\n"
    364 				<< "        ivec2 dstGridNdx = ivec2((gridPosition.x * " << numPrimitives << " * 7 + ndx)*13, (gridPosition.y * 127 + ndx) * 19) % dstGridSize;\n"
    365 				<< "        vec4 dstArea;\n"
    366 				<< "        dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
    367 				<< "        dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
    368 				<< "        dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
    369 				<< "        dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
    370 				<< "\n"
    371 				<< "        vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
    372 				<< "        vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
    373 				<< "        vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
    374 				<< "\n"
    375 				<< "        gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
    376 				<< "        v_color = outputColor;\n"
    377 				<< "        gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
    378 				<< "        EmitVertex();\n"
    379 				<< "\n"
    380 				<< "        gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
    381 				<< "        v_color = outputColor;\n"
    382 				<< "        gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
    383 				<< "        EmitVertex();\n"
    384 				<< "\n"
    385 				<< "        gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
    386 				<< "        v_color = outputColor;\n"
    387 				<< "        gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
    388 				<< "        EmitVertex();\n"
    389 				<< "\n"
    390 				<< "        gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
    391 				<< "        v_color = outputColor;\n"
    392 				<< "        gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n"
    393 				<< "        EmitVertex();\n"
    394 				<< "        EndPrimitive();\n"
    395 				<< "    }\n";
    396 		}
    397 		else
    398 		{
    399 			if (m_flags & FLAG_GEOMETRY_SCATTER_INSTANCES)
    400 			{
    401 				src << "    // Scatter slices\n"
    402 					<< "    int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
    403 					<< "    ivec2 srcSliceNdx = ivec2(gridPosition.x, gridPosition.y * " << (numInvocations*2) << " + inputTriangleNdx);\n"
    404 					<< "    ivec2 dstSliceNdx = ivec2(7 * srcSliceNdx.x, 127 * srcSliceNdx.y) % ivec2(" << m_tessGenLevel << ", " << m_tessGenLevel << " * " << (numInvocations*2) << ");\n"
    405 					<< "\n"
    406 					<< "    // Draw slice to the dstSlice slot\n"
    407 					<< "    vec4 outputSliceArea;\n"
    408 					<< "    outputSliceArea.x = float(dstSliceNdx.x)   / float(" << m_tessGenLevel << ") * 2.0 - 1.0 - gapOffset;\n"
    409 					<< "    outputSliceArea.y = float(dstSliceNdx.y)   / float(" << (m_tessGenLevel * numInvocations * 2) << ") * 2.0 - 1.0 - gapOffset;\n"
    410 					<< "    outputSliceArea.z = float(dstSliceNdx.x+1) / float(" << m_tessGenLevel << ") * 2.0 - 1.0 + gapOffset;\n"
    411 					<< "    outputSliceArea.w = float(dstSliceNdx.y+1) / float(" << (m_tessGenLevel * numInvocations * 2) << ") * 2.0 - 1.0 + gapOffset;\n";
    412 			}
    413 			else
    414 			{
    415 				src << "    // Fill the input area with slices\n"
    416 					<< "    // Upper triangle produces slices only to the upper half of the quad and vice-versa\n"
    417 					<< "    float triangleOffset = (isBottomTriangle) ? ((aabb.w + aabb.y) / 2.0) : (aabb.y);\n"
    418 					<< "    // Each slice is a invocation\n"
    419 					<< "    float sliceHeight = (aabb.w - aabb.y) / float(2 * " << numInvocations << ");\n"
    420 					<< "    float invocationOffset = float(gl_InvocationID) * sliceHeight;\n"
    421 					<< "\n"
    422 					<< "    vec4 outputSliceArea;\n"
    423 					<< "    outputSliceArea.x = aabb.x - gapOffset;\n"
    424 					<< "    outputSliceArea.y = triangleOffset + invocationOffset - gapOffset;\n"
    425 					<< "    outputSliceArea.z = aabb.z + gapOffset;\n"
    426 					<< "    outputSliceArea.w = triangleOffset + invocationOffset + sliceHeight + gapOffset;\n";
    427 			}
    428 
    429 			src << "\n"
    430 				<< "    // Draw slice\n"
    431 				<< "    for (int ndx = 0; ndx < " << ((numPrimitives+2)/2) << "; ++ndx)\n"
    432 				<< "    {\n"
    433 				<< "        vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
    434 				<< "        vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
    435 				<< "        vec4 outputColor = (((gl_InvocationID + ndx) % 2) == 0) ? (green) : (yellow);\n"
    436 				<< "        float xpos = mix(outputSliceArea.x, outputSliceArea.z, float(ndx) / float(" << (numPrimitives/2) << "));\n"
    437 				<< "\n"
    438 				<< "        gl_Position = vec4(xpos, outputSliceArea.y, 0.0, 1.0);\n"
    439 				<< "        v_color = outputColor;\n"
    440 				<< "        EmitVertex();\n"
    441 				<< "\n"
    442 				<< "        gl_Position = vec4(xpos, outputSliceArea.w, 0.0, 1.0);\n"
    443 				<< "        v_color = outputColor;\n"
    444 				<< "        EmitVertex();\n"
    445 				<< "    }\n";
    446 		}
    447 
    448 		src <<	"}\n";
    449 
    450 		programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
    451 	}
    452 }
    453 
    454 class GridRenderTestInstance : public TestInstance
    455 {
    456 public:
    457 	struct Params
    458 	{
    459 		Flags	flags;
    460 		int		numLayers;
    461 
    462 		Params (void) : flags(), numLayers() {}
    463 	};
    464 						GridRenderTestInstance	(Context& context, const Params& params) : TestInstance(context), m_params(params) {}
    465 	tcu::TestStatus		iterate					(void);
    466 
    467 private:
    468 	Params				m_params;
    469 };
    470 
    471 TestInstance* GridRenderTestCase::createInstance (Context& context) const
    472 {
    473 	GridRenderTestInstance::Params params;
    474 
    475 	params.flags	 = m_flags;
    476 	params.numLayers = m_numLayers;
    477 
    478 	return new GridRenderTestInstance(context, params);
    479 }
    480 
    481 bool verifyResultLayer (tcu::TestLog& log, const tcu::ConstPixelBufferAccess& image, const int layerNdx)
    482 {
    483 	tcu::Surface errorMask	(image.getWidth(), image.getHeight());
    484 	bool		 foundError	= false;
    485 
    486 	tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
    487 
    488 	log << tcu::TestLog::Message << "Verifying output layer " << layerNdx  << tcu::TestLog::EndMessage;
    489 
    490 	for (int y = 0; y < image.getHeight(); ++y)
    491 	for (int x = 0; x < image.getWidth(); ++x)
    492 	{
    493 		const int		threshold	= 8;
    494 		const tcu::RGBA	color		(image.getPixel(x, y));
    495 
    496 		// Color must be a linear combination of green and yellow
    497 		if (color.getGreen() < 255 - threshold || color.getBlue() > threshold)
    498 		{
    499 			errorMask.setPixel(x, y, tcu::RGBA::red());
    500 			foundError = true;
    501 		}
    502 	}
    503 
    504 	if (!foundError)
    505 	{
    506 		log << tcu::TestLog::Message << "Image valid." << tcu::TestLog::EndMessage
    507 			<< tcu::TestLog::ImageSet("ImageVerification", "Image verification")
    508 			<< tcu::TestLog::Image("Result", "Rendered result", image)
    509 			<< tcu::TestLog::EndImageSet;
    510 		return true;
    511 	}
    512 	else
    513 	{
    514 		log	<< tcu::TestLog::Message << "Image verification failed, found invalid pixels." << tcu::TestLog::EndMessage
    515 			<< tcu::TestLog::ImageSet("ImageVerification", "Image verification")
    516 			<< tcu::TestLog::Image("Result", "Rendered result", image)
    517 			<< tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
    518 			<< tcu::TestLog::EndImageSet;
    519 		return false;
    520 	}
    521 }
    522 
    523 tcu::TestStatus GridRenderTestInstance::iterate (void)
    524 {
    525 	requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER);
    526 
    527 	m_context.getTestContext().getLog()
    528 		<< tcu::TestLog::Message
    529 		<< "Rendering single point at the origin. Expecting yellow and green colored grid-like image. (High-frequency grid may appear unicolored)."
    530 		<< tcu::TestLog::EndMessage;
    531 
    532 	const DeviceInterface&	vk					= m_context.getDeviceInterface();
    533 	const VkDevice			device				= m_context.getDevice();
    534 	const VkQueue			queue				= m_context.getUniversalQueue();
    535 	const deUint32			queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
    536 	Allocator&				allocator			= m_context.getDefaultAllocator();
    537 
    538 	// Color attachment
    539 
    540 	const tcu::IVec2			  renderSize			   = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
    541 	const VkFormat				  colorFormat			   = VK_FORMAT_R8G8B8A8_UNORM;
    542 	const VkImageSubresourceRange colorImageAllLayersRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_params.numLayers);
    543 	const VkImageCreateInfo		  colorImageCreateInfo	   = makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, m_params.numLayers);
    544 	const VkImageViewType		  colorAttachmentViewType  = (m_params.numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY);
    545 	const Image					  colorAttachmentImage	   (vk, device, allocator, colorImageCreateInfo, MemoryRequirement::Any);
    546 
    547 	// Color output buffer: image will be copied here for verification (big enough for all layers).
    548 
    549 	const VkDeviceSize	colorBufferSizeBytes	= renderSize.x()*renderSize.y() * m_params.numLayers * tcu::getPixelSize(mapVkFormat(colorFormat));
    550 	const Buffer		colorBuffer				(vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
    551 
    552 	// Pipeline: no vertex input attributes nor descriptors.
    553 
    554 	const Unique<VkImageView>		colorAttachmentView(makeImageView						(vk, device, *colorAttachmentImage, colorAttachmentViewType, colorFormat, colorImageAllLayersRange));
    555 	const Unique<VkRenderPass>		renderPass		   (makeRenderPass						(vk, device, colorFormat));
    556 	const Unique<VkFramebuffer>		framebuffer		   (makeFramebuffer						(vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), m_params.numLayers));
    557 	const Unique<VkPipelineLayout>	pipelineLayout	   (makePipelineLayoutWithoutDescriptors(vk, device));
    558 	const Unique<VkCommandPool>		cmdPool			   (makeCommandPool						(vk, device, queueFamilyIndex));
    559 	const Unique<VkCommandBuffer>	cmdBuffer		   (allocateCommandBuffer				(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
    560 
    561 	const Unique<VkPipeline> pipeline (GraphicsPipelineBuilder()
    562 		.setRenderSize	(renderSize)
    563 		.setShader		(vk, device, VK_SHADER_STAGE_VERTEX_BIT,				  m_context.getBinaryCollection().get("vert"), DE_NULL)
    564 		.setShader		(vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,				  m_context.getBinaryCollection().get("frag"), DE_NULL)
    565 		.setShader		(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,	  m_context.getBinaryCollection().get("tesc"), DE_NULL)
    566 		.setShader		(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
    567 		.setShader		(vk, device, VK_SHADER_STAGE_GEOMETRY_BIT,				  m_context.getBinaryCollection().get("geom"), DE_NULL)
    568 		.build			(vk, device, *pipelineLayout, *renderPass));
    569 
    570 	beginCommandBuffer(vk, *cmdBuffer);
    571 
    572 	// Change color attachment image layout
    573 	{
    574 		const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
    575 			(VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
    576 			VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    577 			*colorAttachmentImage, colorImageAllLayersRange);
    578 
    579 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
    580 			0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
    581 	}
    582 
    583 	// Begin render pass
    584 	{
    585 		const VkRect2D renderArea = {
    586 			makeOffset2D(0, 0),
    587 			makeExtent2D(renderSize.x(), renderSize.y()),
    588 		};
    589 		const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
    590 
    591 		beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
    592 	}
    593 
    594 	vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
    595 
    596 	vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
    597 	endRenderPass(vk, *cmdBuffer);
    598 
    599 	// Copy render result to a host-visible buffer
    600 	{
    601 		const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
    602 			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
    603 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
    604 			*colorAttachmentImage, colorImageAllLayersRange);
    605 
    606 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
    607 			0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
    608 	}
    609 	{
    610 		const VkImageSubresourceLayers subresourceLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, m_params.numLayers);
    611 		const VkBufferImageCopy		   copyRegion		 = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 1), subresourceLayers);
    612 		vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
    613 	}
    614 	{
    615 		const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
    616 			VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
    617 
    618 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
    619 			0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
    620 	}
    621 
    622 	endCommandBuffer(vk, *cmdBuffer);
    623 	submitCommandsAndWait(vk, device, queue, *cmdBuffer);
    624 
    625 	// Verify results
    626 	{
    627 		const Allocation& alloc = colorBuffer.getAllocation();
    628 		invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), colorBufferSizeBytes);
    629 
    630 		const tcu::ConstPixelBufferAccess imageAllLayers(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), m_params.numLayers, alloc.getHostPtr());
    631 
    632 		bool allOk = true;
    633 		for (int ndx = 0; ndx < m_params.numLayers; ++ndx)
    634 			allOk = allOk && verifyResultLayer(m_context.getTestContext().getLog(),
    635 											   tcu::getSubregion(imageAllLayers, 0, 0, ndx, renderSize.x(), renderSize.y(), 1),
    636 											   ndx);
    637 
    638 		return (allOk ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image comparison failed"));
    639 	}
    640 }
    641 
    642 struct TestCaseDescription
    643 {
    644 	const char*	name;
    645 	const char*	desc;
    646 	Flags		flags;
    647 };
    648 
    649 } // anonymous
    650 
    651 //! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.render.limits.*
    652 //! \note Tests that check implementation defined limits were omitted, because they rely on runtime shader source generation
    653 //!       (e.g. changing the number of vertices output from geometry shader). CTS currently doesn't support that,
    654 //!       because some platforms require precompiled shaders.
    655 tcu::TestCaseGroup* createGeometryGridRenderLimitsTests  (tcu::TestContext& testCtx)
    656 {
    657 	de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "limits", "Render with properties near their limits"));
    658 
    659 	static const TestCaseDescription cases[] =
    660 	{
    661 		{
    662 			"output_required_max_tessellation",
    663 			"Minimum maximum tessellation level",
    664 			FLAG_TESSELLATION_MAX_SPEC
    665 		},
    666 		{
    667 			"output_required_max_geometry",
    668 			"Output minimum maximum number of vertices the geometry shader",
    669 			FLAG_GEOMETRY_MAX_SPEC
    670 		},
    671 		{
    672 			"output_required_max_invocations",
    673 			"Minimum maximum number of geometry shader invocations",
    674 			FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC
    675 		},
    676 	};
    677 
    678 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx)
    679 		group->addChild(new GridRenderTestCase(testCtx, cases[ndx].name, cases[ndx].desc, cases[ndx].flags));
    680 
    681 	return group.release();
    682 }
    683 
    684 //! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.render.scatter.*
    685 tcu::TestCaseGroup* createGeometryGridRenderScatterTests (tcu::TestContext& testCtx)
    686 {
    687 	de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "scatter", "Scatter output primitives"));
    688 
    689 	static const TestCaseDescription cases[] =
    690 	{
    691 		{
    692 			"geometry_scatter_instances",
    693 			"Each geometry shader instance outputs its primitives far from other instances of the same execution",
    694 			FLAG_GEOMETRY_SCATTER_INSTANCES
    695 		},
    696 		{
    697 			"geometry_scatter_primitives",
    698 			"Each geometry shader instance outputs its primitives far from other primitives of the same instance",
    699 			FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SEPARATE_PRIMITIVES
    700 		},
    701 		{
    702 			"geometry_scatter_layers",
    703 			"Each geometry shader instance outputs its primitives to multiple layers and far from other primitives of the same instance",
    704 			FLAG_GEOMETRY_SCATTER_LAYERS | FLAG_GEOMETRY_SEPARATE_PRIMITIVES
    705 		},
    706 	};
    707 
    708 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx)
    709 		group->addChild(new GridRenderTestCase(testCtx, cases[ndx].name, cases[ndx].desc, cases[ndx].flags));
    710 
    711 	return group.release();
    712 }
    713 
    714 } // tessellation
    715 } // vkt
    716