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 Winding Tests
23 *//*--------------------------------------------------------------------*/
24
25#include "vktTessellationWindingTests.hpp"
26#include "vktTestCaseUtil.hpp"
27#include "vktTessellationUtil.hpp"
28
29#include "tcuTestLog.hpp"
30#include "tcuRGBA.hpp"
31
32#include "vkDefs.hpp"
33#include "vkQueryUtil.hpp"
34#include "vkBuilderUtil.hpp"
35#include "vkImageUtil.hpp"
36#include "vkTypeUtil.hpp"
37#include "vkStrUtil.hpp"
38
39#include "deUniquePtr.hpp"
40
41namespace vkt
42{
43namespace tessellation
44{
45
46using namespace vk;
47
48namespace
49{
50
51std::string getCaseName (const TessPrimitiveType primitiveType, const Winding winding)
52{
53	std::ostringstream str;
54	str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getWindingShaderName(winding);
55	return str.str();
56}
57
58inline VkFrontFace mapFrontFace (const Winding winding)
59{
60	switch (winding)
61	{
62		case WINDING_CCW:	return VK_FRONT_FACE_COUNTER_CLOCKWISE;
63		case WINDING_CW:	return VK_FRONT_FACE_CLOCKWISE;
64		default:
65			DE_ASSERT(false);
66			return VK_FRONT_FACE_LAST;
67	}
68}
69
70//! Returns true when the image passes the verification.
71bool verifyResultImage (tcu::TestLog&						log,
72						const tcu::ConstPixelBufferAccess	image,
73						const TessPrimitiveType				primitiveType,
74						const Winding						winding,
75						const Winding						frontFaceWinding)
76{
77	const int totalNumPixels	= image.getWidth()*image.getHeight();
78	const int badPixelTolerance = (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 5*de::max(image.getWidth(), image.getHeight()) : 0);
79
80	const tcu::Vec4 white = tcu::RGBA::white().toVec();
81	const tcu::Vec4 red   = tcu::RGBA::red().toVec();
82
83	int numWhitePixels = 0;
84	int numRedPixels   = 0;
85	for (int y = 0; y < image.getHeight();	y++)
86	for (int x = 0; x < image.getWidth();	x++)
87	{
88		numWhitePixels += image.getPixel(x, y) == white ? 1 : 0;
89		numRedPixels   += image.getPixel(x, y) == red   ? 1 : 0;
90	}
91
92	DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels);
93
94	log << tcu::TestLog::Message << "Note: got " << numWhitePixels << " white and " << numRedPixels << " red pixels" << tcu::TestLog::EndMessage;
95
96	const int otherPixels = totalNumPixels - numWhitePixels - numRedPixels;
97	if (otherPixels > badPixelTolerance)
98	{
99		log << tcu::TestLog::Message
100			<< "Failure: Got " << otherPixels << " other than white or red pixels (maximum tolerance " << badPixelTolerance << ")"
101			<< tcu::TestLog::EndMessage;
102		return false;
103	}
104
105	if (frontFaceWinding == winding)
106	{
107		if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
108		{
109			if (de::abs(numWhitePixels - totalNumPixels/2) > badPixelTolerance)
110			{
111				log << tcu::TestLog::Message << "Failure: wrong number of white pixels; expected approximately " << totalNumPixels/2 << tcu::TestLog::EndMessage;
112				return false;
113			}
114		}
115		else if (primitiveType == TESSPRIMITIVETYPE_QUADS)
116		{
117			if (numWhitePixels != totalNumPixels)
118			{
119				log << tcu::TestLog::Message << "Failure: expected only white pixels (full-viewport quad)" << tcu::TestLog::EndMessage;
120				return false;
121			}
122		}
123		else
124			DE_ASSERT(false);
125	}
126	else
127	{
128		if (numWhitePixels != 0)
129		{
130			log << tcu::TestLog::Message << "Failure: expected only red pixels (everything culled)" << tcu::TestLog::EndMessage;
131			return false;
132		}
133	}
134
135	return true;
136}
137
138class WindingTest : public TestCase
139{
140public:
141								WindingTest		(tcu::TestContext&			testCtx,
142												 const TessPrimitiveType	primitiveType,
143												 const Winding				winding);
144
145	void						initPrograms	(SourceCollections&			programCollection) const;
146	TestInstance*				createInstance	(Context&					context) const;
147
148private:
149	const TessPrimitiveType		m_primitiveType;
150	const Winding				m_winding;
151};
152
153WindingTest::WindingTest (tcu::TestContext&			testCtx,
154						  const TessPrimitiveType	primitiveType,
155						  const Winding				winding)
156	: TestCase			(testCtx, getCaseName(primitiveType, winding), "")
157	, m_primitiveType	(primitiveType)
158	, m_winding			(winding)
159{
160}
161
162void WindingTest::initPrograms (SourceCollections& programCollection) const
163{
164	// Vertex shader - no inputs
165	{
166		std::ostringstream src;
167		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
168			<< "\n"
169			<< "void main (void)\n"
170			<< "{\n"
171			<< "}\n";
172
173		programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
174	}
175
176	// Tessellation control shader
177	{
178		std::ostringstream src;
179		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
180			<< "#extension GL_EXT_tessellation_shader : require\n"
181			<< "\n"
182			<< "layout(vertices = 1) out;\n"
183			<< "\n"
184			<< "void main (void)\n"
185			<< "{\n"
186			<< "    gl_TessLevelInner[0] = 5.0;\n"
187			<< "    gl_TessLevelInner[1] = 5.0;\n"
188			<< "\n"
189			<< "    gl_TessLevelOuter[0] = 5.0;\n"
190			<< "    gl_TessLevelOuter[1] = 5.0;\n"
191			<< "    gl_TessLevelOuter[2] = 5.0;\n"
192			<< "    gl_TessLevelOuter[3] = 5.0;\n"
193			<< "}\n";
194
195		programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
196	}
197
198	// Tessellation evaluation 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			<< "\n"
204			<< "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ", "
205						 << getWindingShaderName(m_winding) << ") in;\n"
206			<< "\n"
207			<< "void main (void)\n"
208			<< "{\n"
209			<< "    gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n"
210			<< "}\n";
211
212		programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
213	}
214
215	// Fragment shader
216	{
217		std::ostringstream src;
218		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
219			<< "\n"
220			<< "layout(location = 0) out mediump vec4 o_color;\n"
221			<< "\n"
222			<< "void main (void)\n"
223			<< "{\n"
224			<< "    o_color = vec4(1.0);\n"
225			<< "}\n";
226
227		programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
228	}
229}
230
231class WindingTestInstance : public TestInstance
232{
233public:
234								WindingTestInstance (Context&					context,
235													 const TessPrimitiveType	primitiveType,
236													 const Winding				winding);
237
238	tcu::TestStatus				iterate				(void);
239
240private:
241	const TessPrimitiveType		m_primitiveType;
242	const Winding				m_winding;
243};
244
245WindingTestInstance::WindingTestInstance (Context&					context,
246										  const TessPrimitiveType	primitiveType,
247										  const Winding				winding)
248	: TestInstance		(context)
249	, m_primitiveType	(primitiveType)
250	, m_winding			(winding)
251{
252}
253
254tcu::TestStatus WindingTestInstance::iterate (void)
255{
256	const DeviceInterface&	vk					= m_context.getDeviceInterface();
257	const VkDevice			device				= m_context.getDevice();
258	const VkQueue			queue				= m_context.getUniversalQueue();
259	const deUint32			queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
260	Allocator&				allocator			= m_context.getDefaultAllocator();
261
262	// Color attachment
263
264	const tcu::IVec2			  renderSize				 = tcu::IVec2(64, 64);
265	const VkFormat				  colorFormat				 = VK_FORMAT_R8G8B8A8_UNORM;
266	const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
267	const Image					  colorAttachmentImage		 (vk, device, allocator,
268															 makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
269															 MemoryRequirement::Any);
270
271	// Color output buffer: image will be copied here for verification
272
273	const VkDeviceSize	colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
274	const Buffer		colorBuffer			 (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
275
276	// Pipeline
277
278	const Unique<VkImageView>		colorAttachmentView(makeImageView                       (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
279	const Unique<VkRenderPass>		renderPass         (makeRenderPass                      (vk, device, colorFormat));
280	const Unique<VkFramebuffer>		framebuffer        (makeFramebuffer                     (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
281	const Unique<VkPipelineLayout>	pipelineLayout     (makePipelineLayoutWithoutDescriptors(vk, device));
282
283	const VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT;
284
285	// Front face is static state, so we have to create two pipelines.
286
287	const Unique<VkPipeline> pipelineCounterClockwise(GraphicsPipelineBuilder()
288		.setRenderSize	 (renderSize)
289		.setCullModeFlags(cullMode)
290		.setFrontFace	 (VK_FRONT_FACE_COUNTER_CLOCKWISE)
291		.setShader		 (vk, device, VK_SHADER_STAGE_VERTEX_BIT,				   m_context.getBinaryCollection().get("vert"), DE_NULL)
292		.setShader		 (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,    m_context.getBinaryCollection().get("tesc"), DE_NULL)
293		.setShader		 (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
294		.setShader		 (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,				   m_context.getBinaryCollection().get("frag"), DE_NULL)
295		.build			 (vk, device, *pipelineLayout, *renderPass));
296
297	const Unique<VkPipeline> pipelineClockwise(GraphicsPipelineBuilder()
298		.setRenderSize   (renderSize)
299		.setCullModeFlags(cullMode)
300		.setFrontFace    (VK_FRONT_FACE_CLOCKWISE)
301		.setShader		 (vk, device, VK_SHADER_STAGE_VERTEX_BIT,				   m_context.getBinaryCollection().get("vert"), DE_NULL)
302		.setShader		 (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,	   m_context.getBinaryCollection().get("tesc"), DE_NULL)
303		.setShader		 (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
304		.setShader		 (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,				   m_context.getBinaryCollection().get("frag"), DE_NULL)
305		.build			 (vk, device, *pipelineLayout, *renderPass));
306
307	const struct // not static
308	{
309		Winding		frontFaceWinding;
310		VkPipeline	pipeline;
311	} testCases[] =
312	{
313		{ WINDING_CCW,	*pipelineCounterClockwise },
314		{ WINDING_CW,	*pipelineClockwise		  },
315	};
316
317	tcu::TestLog& log = m_context.getTestContext().getLog();
318	log << tcu::TestLog::Message << "Pipeline uses " << getCullModeFlagsStr(cullMode) << tcu::TestLog::EndMessage;
319
320	bool success = true;
321
322	// Draw commands
323
324	const Unique<VkCommandPool>   cmdPool  (makeCommandPool  (vk, device, queueFamilyIndex));
325	const Unique<VkCommandBuffer> cmdBuffer(makeCommandBuffer(vk, device, *cmdPool));
326
327	for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(testCases); ++caseNdx)
328	{
329		const Winding frontFaceWinding = testCases[caseNdx].frontFaceWinding;
330
331		log << tcu::TestLog::Message << "Setting " << getFrontFaceName(mapFrontFace(frontFaceWinding)) << tcu::TestLog::EndMessage;
332
333		// Reset the command buffer and begin.
334		beginCommandBuffer(vk, *cmdBuffer);
335
336		// Change color attachment image layout
337		{
338			// State is slightly different on the first iteration.
339			const VkImageLayout currentLayout = (caseNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
340			const VkAccessFlags srcFlags	  = (caseNdx == 0 ? (VkAccessFlags)0          : (VkAccessFlags)VK_ACCESS_TRANSFER_READ_BIT);
341
342			const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
343				srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
344				currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
345				*colorAttachmentImage, colorImageSubresourceRange);
346
347			vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
348				0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
349		}
350
351		// Begin render pass
352		{
353			const VkRect2D renderArea = {
354				makeOffset2D(0, 0),
355				makeExtent2D(renderSize.x(), renderSize.y()),
356			};
357			const tcu::Vec4 clearColor = tcu::RGBA::red().toVec();
358
359			beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
360		}
361
362		vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, testCases[caseNdx].pipeline);
363
364		// Process a single abstract vertex.
365		vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
366		endRenderPass(vk, *cmdBuffer);
367
368		// Copy render result to a host-visible buffer
369		{
370			const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
371				VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
372				VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
373				*colorAttachmentImage, colorImageSubresourceRange);
374
375			vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
376				0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
377		}
378		{
379			const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 1), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
380			vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
381		}
382		{
383			const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
384				VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
385
386			vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
387				0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
388		}
389
390		endCommandBuffer(vk, *cmdBuffer);
391		submitCommandsAndWait(vk, device, queue, *cmdBuffer);
392
393		{
394			// Log rendered image
395			const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
396			invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
397
398			const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
399			log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess);
400
401			// Verify case result
402			success = success && verifyResultImage(log, imagePixelAccess, m_primitiveType, m_winding, frontFaceWinding);
403		}
404	}  // for windingNdx
405
406	return (success ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
407}
408
409TestInstance* WindingTest::createInstance (Context& context) const
410{
411	requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
412
413	return new WindingTestInstance(context, m_primitiveType, m_winding);
414}
415
416} // anonymous
417
418//! These tests correspond to dEQP-GLES31.functional.tessellation.winding.*
419tcu::TestCaseGroup* createWindingTests (tcu::TestContext& testCtx)
420{
421	de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "winding", "Test the cw and ccw input layout qualifiers"));
422
423	static const TessPrimitiveType primitivesNoIsolines[] =
424	{
425		TESSPRIMITIVETYPE_TRIANGLES,
426		TESSPRIMITIVETYPE_QUADS,
427	};
428
429	for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx)
430	for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx)
431		group->addChild(new WindingTest(testCtx, primitivesNoIsolines[primitiveTypeNdx], (Winding)windingNdx));
432
433	return group.release();
434}
435
436} // tessellation
437} // vkt
438