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 - Point Size
23*//*--------------------------------------------------------------------*/
24
25#include "vktTessellationGeometryPassthroughTests.hpp"
26#include "vktTestCaseUtil.hpp"
27#include "vktTessellationUtil.hpp"
28
29#include "tcuTestLog.hpp"
30
31#include "vkDefs.hpp"
32#include "vkQueryUtil.hpp"
33#include "vkBuilderUtil.hpp"
34#include "vkTypeUtil.hpp"
35#include "vkImageUtil.hpp"
36
37#include "deUniquePtr.hpp"
38
39#include <string>
40#include <vector>
41
42namespace vkt
43{
44namespace tessellation
45{
46
47using namespace vk;
48
49namespace
50{
51
52enum Constants
53{
54	RENDER_SIZE = 32,
55};
56
57enum FlagBits
58{
59	FLAG_VERTEX_SET						= 1u << 0,		// !< set gl_PointSize in vertex shader
60	FLAG_TESSELLATION_EVALUATION_SET	= 1u << 1,		// !< set gl_PointSize in tessellation evaluation shader
61	FLAG_TESSELLATION_ADD				= 1u << 2,		// !< read and add to gl_PointSize in tessellation shader pair
62	FLAG_GEOMETRY_SET					= 1u << 3,		// !< set gl_PointSize in geometry shader
63	FLAG_GEOMETRY_ADD					= 1u << 4,		// !< read and add to gl_PointSize in geometry shader
64};
65typedef deUint32 Flags;
66
67void checkPointSizeRequirements (const InstanceInterface& vki, const VkPhysicalDevice physDevice, const int maxPointSize)
68{
69	const VkPhysicalDeviceProperties properties = getPhysicalDeviceProperties(vki, physDevice);
70	if (maxPointSize > static_cast<int>(properties.limits.pointSizeRange[1]))
71		throw tcu::NotSupportedError("Test requires point size " + de::toString(maxPointSize));
72	// Point size granularity must be 1.0 at most, so no need to check it for this test.
73}
74
75int getExpectedPointSize (const Flags flags)
76{
77	int addition = 0;
78
79	// geometry
80	if (flags & FLAG_GEOMETRY_SET)
81		return 6;
82	else if (flags & FLAG_GEOMETRY_ADD)
83		addition += 2;
84
85	// tessellation
86	if (flags & FLAG_TESSELLATION_EVALUATION_SET)
87		return 4 + addition;
88	else if (flags & FLAG_TESSELLATION_ADD)
89		addition += 2;
90
91	// vertex
92	if (flags & FLAG_VERTEX_SET)
93		return 2 + addition;
94
95	// undefined
96	DE_ASSERT(false);
97	return -1;
98}
99
100inline bool isTessellationStage (const Flags flags)
101{
102	return (flags & (FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD)) != 0;
103}
104
105inline bool isGeometryStage (const Flags flags)
106{
107	return (flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD)) != 0;
108}
109
110bool verifyImage (tcu::TestLog& log, const tcu::ConstPixelBufferAccess image, const int expectedSize)
111{
112	log << tcu::TestLog::Message << "Verifying rendered point size. Expecting " << expectedSize << " pixels." << tcu::TestLog::EndMessage;
113
114	bool			resultAreaFound	= false;
115	tcu::IVec4		resultArea;
116	const tcu::Vec4	black(0.0, 0.0, 0.0, 1.0);
117
118	// Find rasterization output area
119
120	for (int y = 0; y < image.getHeight(); ++y)
121	for (int x = 0; x < image.getWidth();  ++x)
122		if (image.getPixel(x, y) != black)
123		{
124			if (!resultAreaFound)
125			{
126				// first fragment
127				resultArea = tcu::IVec4(x, y, x + 1, y + 1);
128				resultAreaFound = true;
129			}
130			else
131			{
132				// union area
133				resultArea.x() = de::min(resultArea.x(), x);
134				resultArea.y() = de::min(resultArea.y(), y);
135				resultArea.z() = de::max(resultArea.z(), x+1);
136				resultArea.w() = de::max(resultArea.w(), y+1);
137			}
138		}
139
140	if (!resultAreaFound)
141	{
142		log << tcu::TestLog::Message << "Verification failed, could not find any point fragments." << tcu::TestLog::EndMessage;
143		return false;
144	}
145
146	const tcu::IVec2 pointSize = resultArea.swizzle(2,3) - resultArea.swizzle(0, 1);
147
148	if (pointSize.x() != pointSize.y())
149	{
150		log << tcu::TestLog::Message << "ERROR! Rasterized point is not a square. Point size was " << pointSize << tcu::TestLog::EndMessage;
151		return false;
152	}
153
154	if (pointSize.x() != expectedSize)
155	{
156		log << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize << ", got " << pointSize.x() << tcu::TestLog::EndMessage;
157		return false;
158	}
159
160	return true;
161}
162
163void initPrograms (vk::SourceCollections& programCollection, const Flags flags)
164{
165	// Vertex shader
166	{
167		std::ostringstream src;
168		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
169			<< "\n"
170			<< "void main (void)\n"
171			<< "{\n"
172			<< "    gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n";
173
174		if (flags & FLAG_VERTEX_SET)
175			src << "    gl_PointSize = 2.0;\n";
176
177		src << "}\n";
178
179		programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
180	}
181
182	// Fragment shader
183	{
184		std::ostringstream src;
185		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
186			<< "layout(location = 0) out mediump vec4 fragColor;\n"
187			<< "\n"
188			<< "void main (void)\n"
189			<< "{\n"
190			<< "    fragColor = vec4(1.0);\n"
191			<< "}\n";
192
193		programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
194	}
195
196	if (isTessellationStage(flags))
197	{
198		// Tessellation control shader
199		{
200			std::ostringstream src;
201			src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
202				<< "#extension GL_EXT_tessellation_shader : require\n"
203				<< "#extension GL_EXT_tessellation_point_size : require\n"
204				<< "layout(vertices = 1) out;\n"
205				<< "\n"
206				<< "void main (void)\n"
207				<< "{\n"
208				<< "    gl_TessLevelOuter[0] = 3.0;\n"
209				<< "    gl_TessLevelOuter[1] = 3.0;\n"
210				<< "    gl_TessLevelOuter[2] = 3.0;\n"
211				<< "    gl_TessLevelInner[0] = 3.0;\n"
212				<< "\n"
213				<< "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n";
214
215			if (flags & FLAG_TESSELLATION_ADD)
216				src << "    // pass as is to eval\n"
217					<< "    gl_out[gl_InvocationID].gl_PointSize = gl_in[gl_InvocationID].gl_PointSize;\n";
218
219			src << "}\n";
220
221			programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
222		}
223
224		// Tessellation evaluation shader
225		{
226			std::ostringstream src;
227			src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
228				<< "#extension GL_EXT_tessellation_shader : require\n"
229				<< "#extension GL_EXT_tessellation_point_size : require\n"
230				<< "layout(triangles, point_mode) in;\n"
231				<< "\n"
232				<< "void main (void)\n"
233				<< "{\n"
234				<< "    // hide all but one vertex\n"
235				<< "    if (gl_TessCoord.x < 0.99)\n"
236				<< "        gl_Position = vec4(-2.0, 0.0, 0.0, 1.0);\n"
237				<< "    else\n"
238				<< "        gl_Position = gl_in[0].gl_Position;\n";
239
240			if (flags & FLAG_TESSELLATION_ADD)
241				src << "\n"
242					<< "    // add to point size\n"
243					<< "    gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
244			else if (flags & FLAG_TESSELLATION_EVALUATION_SET)
245				src << "\n"
246					<< "    // set point size\n"
247					<< "    gl_PointSize = 4.0;\n";
248
249			src << "}\n";
250
251			programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
252		}
253	}
254
255	if (isGeometryStage(flags))
256	{
257		// Geometry shader
258		std::ostringstream src;
259		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
260			<< "#extension GL_EXT_geometry_shader : require\n"
261			<< "#extension GL_EXT_geometry_point_size : require\n"
262			<< "layout(points) in;\n"
263			<< "layout(points, max_vertices = 1) out;\n"
264			<< "\n"
265			<< "void main (void)\n"
266			<< "{\n"
267			<< "    gl_Position  = gl_in[0].gl_Position;\n";
268
269		if (flags & FLAG_GEOMETRY_SET)
270			src << "    gl_PointSize = 6.0;\n";
271		else if (flags & FLAG_GEOMETRY_ADD)
272			src << "    gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
273
274		src << "\n"
275			<< "    EmitVertex();\n"
276			<< "}\n";
277
278		programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
279	}
280}
281
282tcu::TestStatus test (Context& context, const Flags flags)
283{
284	const int expectedPointSize = getExpectedPointSize(flags);
285	{
286		const InstanceInterface& vki        = context.getInstanceInterface();
287		const VkPhysicalDevice   physDevice = context.getPhysicalDevice();
288
289		requireFeatures           (vki, physDevice, FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER | FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE);
290		checkPointSizeRequirements(vki, physDevice, expectedPointSize);
291	}
292	{
293		tcu::TestLog& log = context.getTestContext().getLog();
294
295		if (flags & FLAG_VERTEX_SET)
296			log << tcu::TestLog::Message << "Setting point size in vertex shader to 2.0." << tcu::TestLog::EndMessage;
297		if (flags & FLAG_TESSELLATION_EVALUATION_SET)
298			log << tcu::TestLog::Message << "Setting point size in tessellation evaluation shader to 4.0." << tcu::TestLog::EndMessage;
299		if (flags & FLAG_TESSELLATION_ADD)
300			log << tcu::TestLog::Message << "Reading point size in tessellation control shader and adding 2.0 to it in evaluation." << tcu::TestLog::EndMessage;
301		if (flags & FLAG_GEOMETRY_SET)
302			log << tcu::TestLog::Message << "Setting point size in geometry shader to 6.0." << tcu::TestLog::EndMessage;
303		if (flags & FLAG_GEOMETRY_ADD)
304			log << tcu::TestLog::Message << "Reading point size in geometry shader and adding 2.0." << tcu::TestLog::EndMessage;
305	}
306
307	const DeviceInterface&	vk					= context.getDeviceInterface();
308	const VkDevice			device				= context.getDevice();
309	const VkQueue			queue				= context.getUniversalQueue();
310	const deUint32			queueFamilyIndex	= context.getUniversalQueueFamilyIndex();
311	Allocator&				allocator			= context.getDefaultAllocator();
312
313	// Color attachment
314
315	const tcu::IVec2			  renderSize				 = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
316	const VkFormat				  colorFormat				 = VK_FORMAT_R8G8B8A8_UNORM;
317	const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
318	const Image					  colorAttachmentImage		 (vk, device, allocator,
319															 makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
320															 MemoryRequirement::Any);
321
322	// Color output buffer
323
324	const VkDeviceSize	colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
325	const Buffer		colorBuffer          (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
326
327	// Pipeline
328
329	const Unique<VkImageView>		colorAttachmentView(makeImageView						(vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
330	const Unique<VkRenderPass>		renderPass		   (makeRenderPass						(vk, device, colorFormat));
331	const Unique<VkFramebuffer>		framebuffer		   (makeFramebuffer						(vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
332	const Unique<VkPipelineLayout>	pipelineLayout	   (makePipelineLayoutWithoutDescriptors(vk, device));
333	const Unique<VkCommandPool>		cmdPool			   (makeCommandPool						(vk, device, queueFamilyIndex));
334	const Unique<VkCommandBuffer>	cmdBuffer		   (allocateCommandBuffer				(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
335
336	GraphicsPipelineBuilder			pipelineBuilder;
337
338	pipelineBuilder
339		.setPrimitiveTopology		  (VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
340		.setRenderSize				  (renderSize)
341		.setPatchControlPoints		  (1)
342		.setShader					  (vk, device, VK_SHADER_STAGE_VERTEX_BIT,					context.getBinaryCollection().get("vert"), DE_NULL)
343		.setShader					  (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,				context.getBinaryCollection().get("frag"), DE_NULL);
344
345	if (isTessellationStage(flags))
346		pipelineBuilder
347			.setShader				  (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,	context.getBinaryCollection().get("tesc"), DE_NULL)
348			.setShader				  (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL);
349
350	if (isGeometryStage(flags))
351		pipelineBuilder
352			.setShader				  (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT,				context.getBinaryCollection().get("geom"), DE_NULL);
353
354	const Unique<VkPipeline> pipeline(pipelineBuilder.build(vk, device, *pipelineLayout, *renderPass));
355
356	// Draw commands
357
358	beginCommandBuffer(vk, *cmdBuffer);
359
360	{
361		const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
362			(VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
363			VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
364			*colorAttachmentImage, colorImageSubresourceRange);
365
366		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
367			0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
368	}
369
370	// Begin render pass
371	{
372		const VkRect2D renderArea = {
373			makeOffset2D(0, 0),
374			makeExtent2D(renderSize.x(), renderSize.y()),
375		};
376		const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
377
378		beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
379	}
380
381	vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
382
383	vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
384	endRenderPass(vk, *cmdBuffer);
385
386	// Copy render result to a host-visible buffer
387	{
388		const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
389			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
390			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
391			*colorAttachmentImage, colorImageSubresourceRange);
392
393		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
394			0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
395	}
396	{
397		const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 1), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
398		vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
399	}
400	{
401		const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
402			VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
403
404		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
405			0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
406	}
407
408	endCommandBuffer(vk, *cmdBuffer);
409	submitCommandsAndWait(vk, device, queue, *cmdBuffer);
410
411	// Verify results
412	{
413		const Allocation& alloc = colorBuffer.getAllocation();
414		invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), colorBufferSizeBytes);
415		tcu::ConstPixelBufferAccess image(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, alloc.getHostPtr());
416
417		tcu::TestLog& log = context.getTestContext().getLog();
418		log << tcu::LogImage("color0", "", image);
419
420		if (verifyImage(log, image, expectedPointSize))
421			return tcu::TestStatus::pass("OK");
422		else
423			return tcu::TestStatus::fail("Didn't render expected point");
424	}
425}
426
427std::string getTestCaseName (const Flags flags)
428{
429	std::ostringstream buf;
430
431	// join per-bit descriptions into a single string with '_' separator
432	if (flags & FLAG_VERTEX_SET)					buf																		<< "vertex_set";
433	if (flags & FLAG_TESSELLATION_EVALUATION_SET)	buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1))	? ("_") : (""))	<< "evaluation_set";
434	if (flags & FLAG_TESSELLATION_ADD)				buf << ((flags & (FLAG_TESSELLATION_ADD-1))				? ("_") : (""))	<< "control_pass_eval_add";
435	if (flags & FLAG_GEOMETRY_SET)					buf << ((flags & (FLAG_GEOMETRY_SET-1))					? ("_") : (""))	<< "geometry_set";
436	if (flags & FLAG_GEOMETRY_ADD)					buf << ((flags & (FLAG_GEOMETRY_ADD-1))					? ("_") : (""))	<< "geometry_add";
437
438	return buf.str();
439}
440
441std::string getTestCaseDescription (const Flags flags)
442{
443	std::ostringstream buf;
444
445	// join per-bit descriptions into a single string with ", " separator
446	if (flags & FLAG_VERTEX_SET)					buf																			<< "set point size in vertex shader";
447	if (flags & FLAG_TESSELLATION_EVALUATION_SET)	buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1))	? (", ") : (""))	<< "set point size in tessellation evaluation shader";
448	if (flags & FLAG_TESSELLATION_ADD)				buf << ((flags & (FLAG_TESSELLATION_ADD-1))				? (", ") : (""))	<< "add to point size in tessellation shader";
449	if (flags & FLAG_GEOMETRY_SET)					buf << ((flags & (FLAG_GEOMETRY_SET-1))					? (", ") : (""))	<< "set point size in geometry shader";
450	if (flags & FLAG_GEOMETRY_ADD)					buf << ((flags & (FLAG_GEOMETRY_ADD-1))					? (", ") : (""))	<< "add to point size in geometry shader";
451
452	return buf.str();
453}
454
455} // anonymous
456
457//! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.point_size.*
458//! with the exception of the default 1.0 point size cases (not valid in Vulkan).
459tcu::TestCaseGroup* createGeometryPointSizeTests (tcu::TestContext& testCtx)
460{
461	de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "point_size", "Test point size"));
462
463	static const Flags caseFlags[] =
464	{
465		FLAG_VERTEX_SET,
466							FLAG_TESSELLATION_EVALUATION_SET,
467																	FLAG_GEOMETRY_SET,
468		FLAG_VERTEX_SET	|	FLAG_TESSELLATION_EVALUATION_SET,
469		FLAG_VERTEX_SET |											FLAG_GEOMETRY_SET,
470		FLAG_VERTEX_SET	|	FLAG_TESSELLATION_EVALUATION_SET	|	FLAG_GEOMETRY_SET,
471		FLAG_VERTEX_SET	|	FLAG_TESSELLATION_ADD				|	FLAG_GEOMETRY_ADD,
472	};
473
474	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseFlags); ++ndx)
475	{
476		const std::string name = getTestCaseName       (caseFlags[ndx]);
477		const std::string desc = getTestCaseDescription(caseFlags[ndx]);
478
479		addFunctionCaseWithPrograms(group.get(), name, desc, initPrograms, test, caseFlags[ndx]);
480	}
481
482	return group.release();
483}
484
485} // tessellation
486} // vkt
487