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 - Passthrough
23*//*--------------------------------------------------------------------*/
24
25#include "vktTessellationGeometryPassthroughTests.hpp"
26#include "vktTestCaseUtil.hpp"
27#include "vktTessellationUtil.hpp"
28
29#include "tcuTestLog.hpp"
30#include "tcuImageCompare.hpp"
31
32#include "vkDefs.hpp"
33#include "vkQueryUtil.hpp"
34#include "vkBuilderUtil.hpp"
35#include "vkTypeUtil.hpp"
36#include "vkImageUtil.hpp"
37
38#include "deUniquePtr.hpp"
39
40#include <string>
41#include <vector>
42
43namespace vkt
44{
45namespace tessellation
46{
47
48using namespace vk;
49
50namespace
51{
52
53void addVertexAndFragmentShaders (vk::SourceCollections&  programCollection)
54{
55	// Vertex shader
56	{
57		std::ostringstream src;
58		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
59			<< "\n"
60			<< "layout(location = 0) in  highp vec4 a_position;\n"
61			<< "layout(location = 0) out highp vec4 v_vertex_color;\n"
62			<< "\n"
63			<< "void main (void)\n"
64			<< "{\n"
65			<< "    gl_Position = a_position;\n"
66			<< "    v_vertex_color = vec4(a_position.x * 0.5 + 0.5, a_position.y * 0.5 + 0.5, 1.0, 0.4);\n"
67			<< "}\n";
68
69		programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
70	}
71
72	// Fragment shader
73	{
74		std::ostringstream src;
75		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
76			<< "\n"
77			<< "layout(location = 0) in  highp   vec4 v_fragment_color;\n"
78			<< "layout(location = 0) out mediump vec4 fragColor;\n"
79			<< "void main (void)\n"
80			<< "{\n"
81			<< "	fragColor = v_fragment_color;\n"
82			<< "}\n";
83
84		programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
85	}
86}
87
88//! Tessellation evaluation shader used in passthrough geometry shader case.
89std::string generateTessellationEvaluationShader (const TessPrimitiveType primitiveType, const std::string& colorOutputName)
90{
91	std::ostringstream	src;
92	src <<	glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
93		<< "#extension GL_EXT_tessellation_shader : require\n"
94		<< "layout(" << getTessPrimitiveTypeShaderName(primitiveType) << ") in;\n"
95		<< "\n"
96		<< "layout(location = 0) in  highp vec4 v_patch_color[];\n"
97		<< "layout(location = 0) out highp vec4 " << colorOutputName << ";\n"
98		<< "\n"
99		<< "// note: No need to use precise gl_Position since we do not require gapless geometry\n"
100		<< "void main (void)\n"
101		<< "{\n";
102
103	if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
104		src << "    vec3 weights = vec3(pow(gl_TessCoord.x, 1.3), pow(gl_TessCoord.y, 1.3), pow(gl_TessCoord.z, 1.3));\n"
105			<< "    vec3 cweights = gl_TessCoord;\n"
106			<< "    gl_Position = vec4(weights.x * gl_in[0].gl_Position.xyz + weights.y * gl_in[1].gl_Position.xyz + weights.z * gl_in[2].gl_Position.xyz, 1.0);\n"
107			<< "    " << colorOutputName << " = cweights.x * v_patch_color[0] + cweights.y * v_patch_color[1] + cweights.z * v_patch_color[2];\n";
108	else if (primitiveType == TESSPRIMITIVETYPE_QUADS || primitiveType == TESSPRIMITIVETYPE_ISOLINES)
109		src << "    vec2 normalizedCoord = (gl_TessCoord.xy * 2.0 - vec2(1.0));\n"
110			<< "    vec2 normalizedWeights = normalizedCoord * (vec2(1.0) - 0.3 * cos(normalizedCoord.yx * 1.57));\n"
111			<< "    vec2 weights = normalizedWeights * 0.5 + vec2(0.5);\n"
112			<< "    vec2 cweights = gl_TessCoord.xy;\n"
113			<< "    gl_Position = mix(mix(gl_in[0].gl_Position, gl_in[1].gl_Position, weights.y), mix(gl_in[2].gl_Position, gl_in[3].gl_Position, weights.y), weights.x);\n"
114			<< "    " << colorOutputName << " = mix(mix(v_patch_color[0], v_patch_color[1], cweights.y), mix(v_patch_color[2], v_patch_color[3], cweights.y), cweights.x);\n";
115	else
116		DE_ASSERT(false);
117
118	src <<	"}\n";
119
120	return src.str();
121}
122
123class IdentityGeometryShaderTestCase : public TestCase
124{
125public:
126	void			initPrograms	(vk::SourceCollections& programCollection) const;
127	TestInstance*	createInstance	(Context& context) const;
128
129	IdentityGeometryShaderTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType)
130		: TestCase			(testCtx, name, description)
131		, m_primitiveType	(primitiveType)
132	{
133	}
134
135private:
136	const TessPrimitiveType m_primitiveType;
137};
138
139void IdentityGeometryShaderTestCase::initPrograms (vk::SourceCollections& programCollection) const
140{
141	addVertexAndFragmentShaders(programCollection);
142
143	// Tessellation control
144	{
145		std::ostringstream src;
146		src <<	glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
147			<< "#extension GL_EXT_tessellation_shader : require\n"
148			<< "layout(vertices = 4) out;\n"
149			<< "\n"
150			<< "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
151			<< "    float inner0;\n"
152			<< "    float inner1;\n"
153			<< "    float outer0;\n"
154			<< "    float outer1;\n"
155			<< "    float outer2;\n"
156			<< "    float outer3;\n"
157			<< "} sb_levels;\n"
158			<< "\n"
159			<< "layout(location = 0) in  highp vec4 v_vertex_color[];\n"
160			<< "layout(location = 0) out highp vec4 v_patch_color[];\n"
161			<< "\n"
162			<< "void main (void)\n"
163			<< "{\n"
164			<< "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
165			<< "    v_patch_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
166			<< "\n"
167			<< "    gl_TessLevelInner[0] = sb_levels.inner0;\n"
168			<< "    gl_TessLevelInner[1] = sb_levels.inner1;\n"
169			<< "    gl_TessLevelOuter[0] = sb_levels.outer0;\n"
170			<< "    gl_TessLevelOuter[1] = sb_levels.outer1;\n"
171			<< "    gl_TessLevelOuter[2] = sb_levels.outer2;\n"
172			<< "    gl_TessLevelOuter[3] = sb_levels.outer3;\n"
173			<<	"}\n";
174
175		programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
176	}
177
178	// Tessellation evaluation shader
179	{
180		programCollection.glslSources.add("tese_to_frag")
181			<< glu::TessellationEvaluationSource(generateTessellationEvaluationShader(m_primitiveType, "v_fragment_color"));
182		programCollection.glslSources.add("tese_to_geom")
183			<< glu::TessellationEvaluationSource(generateTessellationEvaluationShader(m_primitiveType, "v_evaluated_color"));
184	}
185
186	// Geometry shader
187	{
188		std::ostringstream	src;
189		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
190			<< "#extension GL_EXT_geometry_shader : require\n"
191			<< "layout(" << getGeometryShaderInputPrimitiveTypeShaderName(m_primitiveType, false) << ") in;\n"
192			<< "layout(" << getGeometryShaderOutputPrimitiveTypeShaderName(m_primitiveType, false)
193						 << ", max_vertices=" << numVerticesPerPrimitive(m_primitiveType, false) << ") out;\n"
194			<< "\n"
195			<< "layout(location = 0) in  highp vec4 v_evaluated_color[];\n"
196			<< "layout(location = 0) out highp vec4 v_fragment_color;\n"
197			<< "\n"
198			<< "void main (void)\n"
199			<< "{\n"
200			<< "    for (int ndx = 0; ndx < gl_in.length(); ++ndx)\n"
201			<< "    {\n"
202			<< "        gl_Position = gl_in[ndx].gl_Position;\n"
203			<< "        v_fragment_color = v_evaluated_color[ndx];\n"
204			<< "        EmitVertex();\n"
205			<< "    }\n"
206			<< "}\n";
207
208		programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
209	}
210}
211
212class IdentityTessellationShaderTestCase : public TestCase
213{
214public:
215	void			initPrograms	(vk::SourceCollections& programCollection) const;
216	TestInstance*	createInstance	(Context& context) const;
217
218	IdentityTessellationShaderTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType)
219		: TestCase			(testCtx, name, description)
220		, m_primitiveType	(primitiveType)
221	{
222	}
223
224private:
225	const TessPrimitiveType m_primitiveType;
226};
227
228//! Geometry shader used in passthrough tessellation shader case.
229std::string generateGeometryShader (const TessPrimitiveType primitiveType, const std::string& colorSourceName)
230{
231	const int numEmitVertices = (primitiveType == TESSPRIMITIVETYPE_ISOLINES ? 11 : 8);
232
233	std::ostringstream src;
234	src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
235		<< "#extension GL_EXT_geometry_shader : require\n"
236		<< "layout(" << getGeometryShaderInputPrimitiveTypeShaderName(primitiveType, false) << ") in;\n"
237		<< "layout(" << getGeometryShaderOutputPrimitiveTypeShaderName(primitiveType, false)
238					  << ", max_vertices=" << numEmitVertices << ") out;\n"
239		<< "\n"
240		<< "layout(location = 0) in  highp vec4 " << colorSourceName << "[];\n"
241		<< "layout(location = 0) out highp vec4 v_fragment_color;\n"
242		<< "\n"
243		<< "void main (void)\n"
244		<< "{\n";
245
246	if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
247	{
248		src << "	vec4 centerPos = (gl_in[0].gl_Position + gl_in[1].gl_Position + gl_in[2].gl_Position) / 3.0f;\n"
249			<< "\n"
250			<< "	for (int ndx = 0; ndx < 4; ++ndx)\n"
251			<< "	{\n"
252			<< "		gl_Position = centerPos + (centerPos - gl_in[ndx % 3].gl_Position);\n"
253			<< "		v_fragment_color = " << colorSourceName << "[ndx % 3];\n"
254			<< "		EmitVertex();\n"
255			<< "\n"
256			<< "		gl_Position = centerPos + 0.7 * (centerPos - gl_in[ndx % 3].gl_Position);\n"
257			<< "		v_fragment_color = " << colorSourceName << "[ndx % 3];\n"
258			<< "		EmitVertex();\n"
259			<< "	}\n";
260	}
261	else if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
262	{
263		src << "	vec4 mdir = vec4(gl_in[0].gl_Position.y - gl_in[1].gl_Position.y, gl_in[1].gl_Position.x - gl_in[0].gl_Position.x, 0.0, 0.0);\n"
264			<< "	for (int i = 0; i <= 10; ++i)\n"
265			<< "	{\n"
266			<< "		float xweight = cos(float(i) / 10.0 * 6.28) * 0.5 + 0.5;\n"
267			<< "		float mweight = sin(float(i) / 10.0 * 6.28) * 0.1 + 0.1;\n"
268			<< "		gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, xweight) + mweight * mdir;\n"
269			<< "		v_fragment_color = mix(" << colorSourceName << "[0], " << colorSourceName << "[1], xweight);\n"
270			<< "		EmitVertex();\n"
271			<< "	}\n";
272	}
273	else
274		DE_ASSERT(false);
275
276	src << "}\n";
277
278	return src.str();
279}
280
281void IdentityTessellationShaderTestCase::initPrograms (vk::SourceCollections& programCollection) const
282{
283	addVertexAndFragmentShaders(programCollection);
284
285	// Tessellation control
286	{
287		std::ostringstream src;
288		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
289			<< "#extension GL_EXT_tessellation_shader : require\n"
290			<< "layout(vertices = " << numVerticesPerPrimitive(m_primitiveType, false) << ") out;\n"
291			<< "\n"
292			<< "layout(location = 0) in  highp vec4 v_vertex_color[];\n"
293			<< "layout(location = 0) out highp vec4 v_control_color[];\n"
294			<< "\n"
295			<< "void main (void)\n"
296			<< "{\n"
297			<< "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
298			<< "    v_control_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
299			<< "\n"
300			<< "    gl_TessLevelInner[0] = 1.0;\n"
301			<< "    gl_TessLevelInner[1] = 1.0;\n"
302			<< "    gl_TessLevelOuter[0] = 1.0;\n"
303			<< "    gl_TessLevelOuter[1] = 1.0;\n"
304			<< "    gl_TessLevelOuter[2] = 1.0;\n"
305			<< "    gl_TessLevelOuter[3] = 1.0;\n"
306			<<	"}\n";
307
308		programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
309	}
310
311	// Tessellation evaluation shader
312	{
313		std::ostringstream src;
314		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
315			<< "#extension GL_EXT_tessellation_shader : require\n"
316			<< "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ") in;\n"
317			<< "\n"
318			<< "layout(location = 0) in  highp vec4 v_control_color[];\n"
319			<< "layout(location = 0) out highp vec4 v_evaluated_color;\n"
320			<< "\n"
321			<< "// note: No need to use precise gl_Position since we do not require gapless geometry\n"
322			<< "void main (void)\n"
323			<< "{\n";
324
325		if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
326			src << "    gl_Position = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * gl_in[1].gl_Position + gl_TessCoord.z * gl_in[2].gl_Position;\n"
327				<< "    v_evaluated_color = gl_TessCoord.x * v_control_color[0] + gl_TessCoord.y * v_control_color[1] + gl_TessCoord.z * v_control_color[2];\n";
328		else if (m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
329			src << "    gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x);\n"
330				<< "    v_evaluated_color = mix(v_control_color[0], v_control_color[1], gl_TessCoord.x);\n";
331		else
332			DE_ASSERT(false);
333
334		src << "}\n";
335
336		programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
337	}
338
339	// Geometry shader
340	{
341		programCollection.glslSources.add("geom_from_tese") << glu::GeometrySource(
342			generateGeometryShader(m_primitiveType, "v_evaluated_color"));
343		programCollection.glslSources.add("geom_from_vert") << glu::GeometrySource(
344			generateGeometryShader(m_primitiveType, "v_vertex_color"));
345	}
346}
347
348inline tcu::ConstPixelBufferAccess getPixelBufferAccess (const DeviceInterface& vk,
349														 const VkDevice			device,
350														 const Buffer&			colorBuffer,
351														 const VkFormat			colorFormat,
352														 const VkDeviceSize		colorBufferSizeBytes,
353														 const tcu::IVec2&		renderSize)
354{
355	const Allocation& alloc = colorBuffer.getAllocation();
356	invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), colorBufferSizeBytes);
357	return tcu::ConstPixelBufferAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, alloc.getHostPtr());
358}
359
360//! When a test case disables tessellation stage and we need to derive a primitive type.
361VkPrimitiveTopology getPrimitiveTopology (const TessPrimitiveType primitiveType)
362{
363	switch (primitiveType)
364	{
365		case TESSPRIMITIVETYPE_TRIANGLES:
366		case TESSPRIMITIVETYPE_QUADS:
367			return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
368
369		case TESSPRIMITIVETYPE_ISOLINES:
370			return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
371
372		default:
373			DE_ASSERT(false);
374			return VK_PRIMITIVE_TOPOLOGY_LAST;
375	}
376}
377
378enum Constants
379{
380	PIPELINE_CASES	= 2,
381	RENDER_SIZE		= 256,
382};
383
384class PassthroughTestInstance : public TestInstance
385{
386public:
387	struct PipelineDescription
388	{
389		bool		useTessellation;
390		bool		useGeometry;
391		std::string	tessEvalShaderName;
392		std::string	geomShaderName;
393		std::string description;
394
395		PipelineDescription (void) : useTessellation(), useGeometry() {}
396	};
397
398	struct Params
399	{
400		bool					useTessLevels;
401		TessLevels				tessLevels;
402		TessPrimitiveType		primitiveType;
403		int						inputPatchVertices;
404		std::vector<tcu::Vec4>	vertices;
405		PipelineDescription		pipelineCases[PIPELINE_CASES];	//!< Each test case renders with two pipelines and compares results
406		std::string				message;
407
408		Params (void) : useTessLevels(), tessLevels(), primitiveType(), inputPatchVertices() {}
409	};
410
411								PassthroughTestInstance	(Context& context, const Params& params) : TestInstance(context), m_params(params) {}
412	tcu::TestStatus				iterate					(void);
413
414private:
415	const Params				m_params;
416};
417
418tcu::TestStatus PassthroughTestInstance::iterate (void)
419{
420	requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER);
421	DE_STATIC_ASSERT(PIPELINE_CASES == 2);
422
423	const DeviceInterface&	vk					= m_context.getDeviceInterface();
424	const VkDevice			device				= m_context.getDevice();
425	const VkQueue			queue				= m_context.getUniversalQueue();
426	const deUint32			queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
427	Allocator&				allocator			= m_context.getDefaultAllocator();
428
429	// Tessellation levels
430	const Buffer tessLevelsBuffer (vk, device, allocator, makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
431
432	if (m_params.useTessLevels)
433	{
434		const Allocation& alloc = tessLevelsBuffer.getAllocation();
435		TessLevels* const bufferTessLevels = static_cast<TessLevels*>(alloc.getHostPtr());
436		*bufferTessLevels = m_params.tessLevels;
437		flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels));
438	}
439
440	// Vertex attributes
441
442	const VkDeviceSize	vertexDataSizeBytes = sizeInBytes(m_params.vertices);
443	const VkFormat		vertexFormat		= VK_FORMAT_R32G32B32A32_SFLOAT;
444	const Buffer		vertexBuffer		(vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
445
446	{
447		const Allocation& alloc = vertexBuffer.getAllocation();
448		deMemcpy(alloc.getHostPtr(), &m_params.vertices[0], static_cast<std::size_t>(vertexDataSizeBytes));
449		flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
450	}
451
452	// Descriptors - make descriptor for tessellation levels, even if we don't use them, to simplify code
453
454	const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
455		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
456		.build(vk, device));
457
458	const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
459		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
460		.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
461
462	const Unique<VkDescriptorSet> descriptorSet		   (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
463	const VkDescriptorBufferInfo  tessLevelsBufferInfo = makeDescriptorBufferInfo(*tessLevelsBuffer, 0ull, sizeof(TessLevels));
464
465	DescriptorSetUpdateBuilder()
466		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
467		.update(vk, device);
468
469	// Color attachment
470
471	const tcu::IVec2			  renderSize				 = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
472	const VkFormat				  colorFormat				 = VK_FORMAT_R8G8B8A8_UNORM;
473	const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
474	const Image					  colorAttachmentImage		 (vk, device, allocator,
475															 makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
476															 MemoryRequirement::Any);
477
478	// Color output buffer: image will be copied here for verification.
479	//                      We use two buffers, one for each case.
480
481	const VkDeviceSize	colorBufferSizeBytes		= renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
482	const Buffer		colorBuffer1				(vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
483	const Buffer		colorBuffer2				(vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
484	const Buffer* const colorBuffer[PIPELINE_CASES] = { &colorBuffer1, &colorBuffer2 };
485
486	// Pipeline
487
488	const Unique<VkImageView>		colorAttachmentView(makeImageView		(vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
489	const Unique<VkRenderPass>		renderPass		   (makeRenderPass		(vk, device, colorFormat));
490	const Unique<VkFramebuffer>		framebuffer		   (makeFramebuffer		(vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
491	const Unique<VkPipelineLayout>	pipelineLayout	   (makePipelineLayout	(vk, device, *descriptorSetLayout));
492	const Unique<VkCommandPool>		cmdPool			   (makeCommandPool		(vk, device, queueFamilyIndex));
493	const Unique<VkCommandBuffer>	cmdBuffer		   (makeCommandBuffer	(vk, device, *cmdPool));
494
495	// Message explaining the test
496	{
497		tcu::TestLog& log = m_context.getTestContext().getLog();
498		log << tcu::TestLog::Message << m_params.message << tcu::TestLog::EndMessage;
499
500		if (m_params.useTessLevels)
501			log << tcu::TestLog::Message << "Tessellation levels: " << getTessellationLevelsString(m_params.tessLevels, m_params.primitiveType) << tcu::TestLog::EndMessage;
502	}
503
504	for (int pipelineNdx = 0; pipelineNdx < PIPELINE_CASES; ++pipelineNdx)
505	{
506		const PipelineDescription& pipelineDescription = m_params.pipelineCases[pipelineNdx];
507		GraphicsPipelineBuilder	   pipelineBuilder;
508
509		pipelineBuilder
510			.setPrimitiveTopology		  (getPrimitiveTopology(m_params.primitiveType))
511			.setRenderSize				  (renderSize)
512			.setBlend					  (true)
513			.setVertexInputSingleAttribute(vertexFormat, tcu::getPixelSize(mapVkFormat(vertexFormat)))
514			.setPatchControlPoints		  (m_params.inputPatchVertices)
515			.setShader					  (vk, device, VK_SHADER_STAGE_VERTEX_BIT,					m_context.getBinaryCollection().get("vert"), DE_NULL)
516			.setShader					  (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,				m_context.getBinaryCollection().get("frag"), DE_NULL);
517
518		if (pipelineDescription.useTessellation)
519			pipelineBuilder
520				.setShader				  (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,	m_context.getBinaryCollection().get("tesc"), DE_NULL)
521				.setShader				  (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get(pipelineDescription.tessEvalShaderName), DE_NULL);
522
523		if (pipelineDescription.useGeometry)
524			pipelineBuilder
525				.setShader				  (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT,				m_context.getBinaryCollection().get(pipelineDescription.geomShaderName), DE_NULL);
526
527		const Unique<VkPipeline> pipeline (pipelineBuilder.build(vk, device, *pipelineLayout, *renderPass));
528
529		// Draw commands
530
531		beginCommandBuffer(vk, *cmdBuffer);
532
533		// Change color attachment image layout
534		{
535			// State is slightly different on the first iteration.
536			const VkImageLayout currentLayout = (pipelineNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
537			const VkAccessFlags srcFlags	  = (pipelineNdx == 0 ? (VkAccessFlags)0          : (VkAccessFlags)VK_ACCESS_TRANSFER_READ_BIT);
538
539			const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
540				srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
541				currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
542				*colorAttachmentImage, colorImageSubresourceRange);
543
544			vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
545				0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
546		}
547
548		// Begin render pass
549		{
550			const VkRect2D renderArea = {
551				makeOffset2D(0, 0),
552				makeExtent2D(renderSize.x(), renderSize.y()),
553			};
554			const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
555
556			beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
557		}
558
559		vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
560		{
561			const VkDeviceSize vertexBufferOffset = 0ull;
562			vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
563		}
564
565		if (m_params.useTessLevels)
566			vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
567
568		vk.cmdDraw(*cmdBuffer, static_cast<deUint32>(m_params.vertices.size()), 1u, 0u, 0u);
569		endRenderPass(vk, *cmdBuffer);
570
571		// Copy render result to a host-visible buffer
572		{
573			const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
574				VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
575				VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
576				*colorAttachmentImage, colorImageSubresourceRange);
577
578			vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
579				0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
580		}
581		{
582			const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 1), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
583			vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorBuffer[pipelineNdx]->get(), 1u, &copyRegion);
584		}
585		{
586			const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
587				VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, colorBuffer[pipelineNdx]->get(), 0ull, colorBufferSizeBytes);
588
589			vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
590				0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
591		}
592
593		endCommandBuffer(vk, *cmdBuffer);
594		submitCommandsAndWait(vk, device, queue, *cmdBuffer);
595	}
596
597	// Verify results
598
599	tcu::ConstPixelBufferAccess image0 = getPixelBufferAccess(vk, device, *colorBuffer[0], colorFormat, colorBufferSizeBytes, renderSize);
600	tcu::ConstPixelBufferAccess image1 = getPixelBufferAccess(vk, device, *colorBuffer[1], colorFormat, colorBufferSizeBytes, renderSize);
601
602	const tcu::UVec4 colorThreshold    (8, 8, 8, 255);
603	const tcu::IVec3 positionDeviation (1, 1, 0);		// 3x3 search kernel
604	const bool		 ignoreOutOfBounds = true;
605
606	tcu::TestLog& log = m_context.getTestContext().getLog();
607	log << tcu::TestLog::Message
608		<< "In image comparison:\n"
609		<< "  Reference - " << m_params.pipelineCases[0].description << "\n"
610		<< "  Result    - " << m_params.pipelineCases[1].description << "\n"
611		<< tcu::TestLog::EndMessage;
612
613	const bool ok = tcu::intThresholdPositionDeviationCompare(
614		log, "ImageCompare", "Image comparison", image0, image1, colorThreshold, positionDeviation, ignoreOutOfBounds, tcu::COMPARE_LOG_RESULT);
615
616	return (ok ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image comparison failed"));
617}
618
619TestInstance* IdentityGeometryShaderTestCase::createInstance (Context& context) const
620{
621	PassthroughTestInstance::Params params;
622
623	const float level		   = 14.0;
624	params.useTessLevels	   = true;
625	params.tessLevels.inner[0] = level;
626	params.tessLevels.inner[1] = level;
627	params.tessLevels.outer[0] = level;
628	params.tessLevels.outer[1] = level;
629	params.tessLevels.outer[2] = level;
630	params.tessLevels.outer[3] = level;
631
632	params.primitiveType	   = m_primitiveType;
633	params.inputPatchVertices  = (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
634
635	params.vertices.push_back(tcu::Vec4( -0.9f, -0.9f, 0.0f, 1.0f ));
636	params.vertices.push_back(tcu::Vec4( -0.9f,  0.9f, 0.0f, 1.0f ));
637	params.vertices.push_back(tcu::Vec4(  0.9f, -0.9f, 0.0f, 1.0f ));
638	params.vertices.push_back(tcu::Vec4(  0.9f,  0.9f, 0.0f, 1.0f ));
639
640	params.pipelineCases[0].useTessellation		= true;
641	params.pipelineCases[0].useGeometry			= true;
642	params.pipelineCases[0].tessEvalShaderName	= "tese_to_geom";
643	params.pipelineCases[0].geomShaderName		= "geom";
644	params.pipelineCases[0].description			= "passthrough geometry shader";
645
646	params.pipelineCases[1].useTessellation		= true;
647	params.pipelineCases[1].useGeometry			= false;
648	params.pipelineCases[1].tessEvalShaderName	= "tese_to_frag";
649	params.pipelineCases[1].geomShaderName		= "geom";
650	params.pipelineCases[1].description			= "no geometry shader in the pipeline";
651
652	params.message = "Testing tessellating shader program output does not change when a passthrough geometry shader is attached.\n"
653					 "Rendering two images, first with and second without a geometry shader. Expecting similar results.\n"
654					 "Using additive blending to detect overlap.\n";
655
656	return new PassthroughTestInstance(context, params);
657};
658
659TestInstance* IdentityTessellationShaderTestCase::createInstance (Context& context) const
660{
661	PassthroughTestInstance::Params params;
662
663	params.useTessLevels	   = false;
664	params.primitiveType	   = m_primitiveType;
665	params.inputPatchVertices  = (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2);
666
667	params.vertices.push_back(	  tcu::Vec4( -0.4f,  0.4f, 0.0f, 1.0f ));
668	params.vertices.push_back(	  tcu::Vec4(  0.0f, -0.5f, 0.0f, 1.0f ));
669	if (params.inputPatchVertices == 3)
670		params.vertices.push_back(tcu::Vec4(  0.4f,  0.4f, 0.0f, 1.0f ));
671
672	params.pipelineCases[0].useTessellation		= true;
673	params.pipelineCases[0].useGeometry			= true;
674	params.pipelineCases[0].tessEvalShaderName	= "tese";
675	params.pipelineCases[0].geomShaderName		= "geom_from_tese";
676	params.pipelineCases[0].description			= "passthrough tessellation shaders";
677
678	params.pipelineCases[1].useTessellation		= false;
679	params.pipelineCases[1].useGeometry			= true;
680	params.pipelineCases[1].tessEvalShaderName	= "tese";
681	params.pipelineCases[1].geomShaderName		= "geom_from_vert";
682	params.pipelineCases[1].description			= "no tessellation shaders in the pipeline";
683
684	params.message = "Testing geometry shading shader program output does not change when a passthrough tessellation shader is attached.\n"
685					 "Rendering two images, first with and second without a tessellation shader. Expecting similar results.\n"
686					 "Using additive blending to detect overlap.\n";
687
688	return new PassthroughTestInstance(context, params);
689};
690
691inline TestCase* makeIdentityGeometryShaderCase (tcu::TestContext& testCtx, const TessPrimitiveType primitiveType)
692{
693	return new IdentityGeometryShaderTestCase(
694		testCtx,
695		"tessellate_" + de::toString(getTessPrimitiveTypeShaderName(primitiveType)) + "_passthrough_geometry_no_change",
696		"Passthrough geometry shader has no effect",
697		primitiveType);
698}
699
700inline TestCase* makeIdentityTessellationShaderCase (tcu::TestContext& testCtx, const TessPrimitiveType primitiveType)
701{
702	return new IdentityTessellationShaderTestCase(
703		testCtx,
704		"passthrough_tessellation_geometry_shade_" + de::toString(getTessPrimitiveTypeShaderName(primitiveType)) + "_no_change",
705		"Passthrough tessellation shader has no effect",
706		primitiveType);
707}
708
709} // anonymous
710
711
712//! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.render.passthrough.*
713tcu::TestCaseGroup* createGeometryPassthroughTests (tcu::TestContext& testCtx)
714{
715	de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "passthrough", "Render various types with either passthrough geometry or tessellation shader"));
716
717	// Passthrough geometry shader
718	group->addChild(makeIdentityGeometryShaderCase(testCtx, TESSPRIMITIVETYPE_TRIANGLES));
719	group->addChild(makeIdentityGeometryShaderCase(testCtx, TESSPRIMITIVETYPE_QUADS));
720	group->addChild(makeIdentityGeometryShaderCase(testCtx, TESSPRIMITIVETYPE_ISOLINES));
721
722	// Passthrough tessellation shader
723	group->addChild(makeIdentityTessellationShaderCase(testCtx, TESSPRIMITIVETYPE_TRIANGLES));
724	group->addChild(makeIdentityTessellationShaderCase(testCtx, TESSPRIMITIVETYPE_ISOLINES));
725
726	return group.release();
727}
728
729} // tessellation
730} // vkt
731