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