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