1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2016 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Negative viewport height (part of VK_KHR_maintenance1)
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktDrawNegativeViewportHeightTests.hpp"
25 #include "vktDrawCreateInfoUtil.hpp"
26 #include "vktDrawImageObjectUtil.hpp"
27 #include "vktDrawBufferObjectUtil.hpp"
28 #include "vktTestGroupUtil.hpp"
29 #include "vktTestCaseUtil.hpp"
30 
31 #include "vkPrograms.hpp"
32 #include "vkTypeUtil.hpp"
33 #include "vkImageUtil.hpp"
34 #include "vkQueryUtil.hpp"
35 #include "vkCmdUtil.hpp"
36 
37 #include "tcuVector.hpp"
38 #include "tcuTextureUtil.hpp"
39 #include "tcuImageCompare.hpp"
40 #include "tcuTestLog.hpp"
41 
42 #include "deSharedPtr.hpp"
43 
44 namespace vkt
45 {
46 namespace Draw
47 {
48 namespace
49 {
50 using namespace vk;
51 using tcu::Vec4;
52 using de::SharedPtr;
53 using de::MovePtr;
54 
55 enum Constants
56 {
57 	WIDTH	= 256,
58 	HEIGHT	= WIDTH/2,
59 };
60 
61 struct TestParams
62 {
63 	VkFrontFace			frontFace;
64 	VkCullModeFlagBits	cullMode;
65 	bool				zeroViewportHeight;
66 };
67 
68 class NegativeViewportHeightTestInstance : public TestInstance
69 {
70 public:
71 									NegativeViewportHeightTestInstance	(Context& context, const TestParams& params);
72 	tcu::TestStatus					iterate								(void);
73 	tcu::ConstPixelBufferAccess		draw								(const VkViewport viewport);
74 	MovePtr<tcu::TextureLevel>		generateReferenceImage				(void) const;
75 	bool							isCulled							(const VkFrontFace triangleFace) const;
76 
77 private:
78 	const TestParams				m_params;
79 	const VkFormat					m_colorAttachmentFormat;
80 	SharedPtr<Image>				m_colorTargetImage;
81 	Move<VkImageView>				m_colorTargetView;
82 	SharedPtr<Buffer>				m_vertexBuffer;
83 	Move<VkRenderPass>				m_renderPass;
84 	Move<VkFramebuffer>				m_framebuffer;
85 	Move<VkPipelineLayout>			m_pipelineLayout;
86 	Move<VkPipeline>				m_pipeline;
87 };
88 
NegativeViewportHeightTestInstance(Context & context,const TestParams & params)89 NegativeViewportHeightTestInstance::NegativeViewportHeightTestInstance (Context& context, const TestParams& params)
90 	: TestInstance				(context)
91 	, m_params					(params)
92 	, m_colorAttachmentFormat	(VK_FORMAT_R8G8B8A8_UNORM)
93 {
94 	const DeviceInterface&	vk		= m_context.getDeviceInterface();
95 	const VkDevice			device	= m_context.getDevice();
96 
97 	// Vertex data
98 	{
99 		std::vector<Vec4> vertexData;
100 
101 		// CCW triangle
102 		vertexData.push_back(Vec4(-0.8f, -0.6f, 0.0f, 1.0f));	//  0-----2
103 		vertexData.push_back(Vec4(-0.8f,  0.6f, 0.0f, 1.0f));	//   |  /
104 		vertexData.push_back(Vec4(-0.2f, -0.6f, 0.0f, 1.0f));	//  1|/
105 
106 		// CW triangle
107 		vertexData.push_back(Vec4( 0.2f, -0.6f, 0.0f, 1.0f));	//  0-----1
108 		vertexData.push_back(Vec4( 0.8f, -0.6f, 0.0f, 1.0f));	//    \  |
109 		vertexData.push_back(Vec4( 0.8f,  0.6f, 0.0f, 1.0f));	//      \|2
110 
111 		const VkDeviceSize dataSize = vertexData.size() * sizeof(Vec4);
112 		m_vertexBuffer = Buffer::createAndAlloc(vk, device, BufferCreateInfo(dataSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT),
113 												m_context.getDefaultAllocator(), MemoryRequirement::HostVisible);
114 
115 		deMemcpy(m_vertexBuffer->getBoundMemory().getHostPtr(), &vertexData[0], static_cast<std::size_t>(dataSize));
116 		flushMappedMemoryRange(vk, device, m_vertexBuffer->getBoundMemory().getMemory(), m_vertexBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE);
117 	}
118 
119 	// Render pass
120 	{
121 		const VkExtent3D		targetImageExtent		= { WIDTH, HEIGHT, 1 };
122 		const VkImageUsageFlags	targetImageUsageFlags	= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
123 
124 		const ImageCreateInfo	targetImageCreateInfo(
125 			VK_IMAGE_TYPE_2D,						// imageType,
126 			m_colorAttachmentFormat,				// format,
127 			targetImageExtent,						// extent,
128 			1u,										// mipLevels,
129 			1u,										// arrayLayers,
130 			VK_SAMPLE_COUNT_1_BIT,					// samples,
131 			VK_IMAGE_TILING_OPTIMAL,				// tiling,
132 			targetImageUsageFlags);					// usage,
133 
134 		m_colorTargetImage = Image::createAndAlloc(vk, device, targetImageCreateInfo, m_context.getDefaultAllocator(), m_context.getUniversalQueueFamilyIndex());
135 
136 		RenderPassCreateInfo	renderPassCreateInfo;
137 		renderPassCreateInfo.addAttachment(AttachmentDescription(
138 			m_colorAttachmentFormat,				// format
139 			VK_SAMPLE_COUNT_1_BIT,					// samples
140 			VK_ATTACHMENT_LOAD_OP_LOAD,				// loadOp
141 			VK_ATTACHMENT_STORE_OP_STORE,			// storeOp
142 			VK_ATTACHMENT_LOAD_OP_DONT_CARE,		// stencilLoadOp
143 			VK_ATTACHMENT_STORE_OP_DONT_CARE,		// stencilStoreOp
144 			VK_IMAGE_LAYOUT_GENERAL,				// initialLayout
145 			VK_IMAGE_LAYOUT_GENERAL));				// finalLayout
146 
147 		const VkAttachmentReference colorAttachmentReference =
148 		{
149 			0u,
150 			VK_IMAGE_LAYOUT_GENERAL
151 		};
152 
153 		renderPassCreateInfo.addSubpass(SubpassDescription(
154 			VK_PIPELINE_BIND_POINT_GRAPHICS,		// pipelineBindPoint
155 			(VkSubpassDescriptionFlags)0,			// flags
156 			0u,										// inputAttachmentCount
157 			DE_NULL,								// inputAttachments
158 			1u,										// colorAttachmentCount
159 			&colorAttachmentReference,				// colorAttachments
160 			DE_NULL,								// resolveAttachments
161 			AttachmentReference(),					// depthStencilAttachment
162 			0u,										// preserveAttachmentCount
163 			DE_NULL));								// preserveAttachments
164 
165 		m_renderPass = createRenderPass(vk, device, &renderPassCreateInfo);
166 	}
167 
168 	// Framebuffer
169 	{
170 		const ImageViewCreateInfo colorTargetViewInfo (m_colorTargetImage->object(), VK_IMAGE_VIEW_TYPE_2D, m_colorAttachmentFormat);
171 		m_colorTargetView = createImageView(vk, device, &colorTargetViewInfo);
172 
173 		std::vector<VkImageView> colorAttachments(1);
174 		colorAttachments[0] = *m_colorTargetView;
175 
176 		const FramebufferCreateInfo	framebufferCreateInfo(*m_renderPass, colorAttachments, WIDTH, HEIGHT, 1);
177 		m_framebuffer = createFramebuffer(vk, device, &framebufferCreateInfo);
178 	}
179 
180 	// Vertex input
181 
182 	const VkVertexInputBindingDescription		vertexInputBindingDescription =
183 	{
184 		0u,										// uint32_t             binding;
185 		sizeof(Vec4),							// uint32_t             stride;
186 		VK_VERTEX_INPUT_RATE_VERTEX,			// VkVertexInputRate    inputRate;
187 	};
188 
189 	const VkVertexInputAttributeDescription		vertexInputAttributeDescription =
190 	{
191 		0u,										// uint32_t    location;
192 		0u,										// uint32_t    binding;
193 		VK_FORMAT_R32G32B32A32_SFLOAT,			// VkFormat    format;
194 		0u										// uint32_t    offset;
195 	};
196 
197 	const PipelineCreateInfo::VertexInputState	vertexInputState = PipelineCreateInfo::VertexInputState(1, &vertexInputBindingDescription,
198 																										1, &vertexInputAttributeDescription);
199 
200 	// Graphics pipeline
201 
202 	const VkRect2D scissor = makeRect2D(WIDTH, HEIGHT);
203 
204 	std::vector<VkDynamicState>		dynamicStates;
205 	dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT);
206 
207 	const Unique<VkShaderModule>	vertexModule	(createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0));
208 	const Unique<VkShaderModule>	fragmentModule	(createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0));
209 
210 	const PipelineLayoutCreateInfo	pipelineLayoutCreateInfo;
211 	m_pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
212 
213 	const PipelineCreateInfo::ColorBlendState::Attachment colorBlendAttachmentState;
214 
215 	PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, (VkPipelineCreateFlags)0);
216 	pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vertexModule,   "main", VK_SHADER_STAGE_VERTEX_BIT));
217 	pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fragmentModule, "main", VK_SHADER_STAGE_FRAGMENT_BIT));
218 	pipelineCreateInfo.addState (PipelineCreateInfo::VertexInputState	(vertexInputState));
219 	pipelineCreateInfo.addState (PipelineCreateInfo::InputAssemblerState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST));
220 	pipelineCreateInfo.addState (PipelineCreateInfo::ColorBlendState	(1, &colorBlendAttachmentState));
221 	pipelineCreateInfo.addState (PipelineCreateInfo::ViewportState		(1, std::vector<VkViewport>(), std::vector<VkRect2D>(1, scissor)));
222 	pipelineCreateInfo.addState (PipelineCreateInfo::DepthStencilState	());
223 	pipelineCreateInfo.addState (PipelineCreateInfo::RasterizerState	(
224 		VK_FALSE,					// depthClampEnable
225 		VK_FALSE,					// rasterizerDiscardEnable
226 		VK_POLYGON_MODE_FILL,		// polygonMode
227 		m_params.cullMode,			// cullMode
228 		m_params.frontFace,			// frontFace
229 		VK_FALSE,					// depthBiasEnable
230 		0.0f,						// depthBiasConstantFactor
231 		0.0f,						// depthBiasClamp
232 		0.0f,						// depthBiasSlopeFactor
233 		1.0f));						// lineWidth
234 	pipelineCreateInfo.addState (PipelineCreateInfo::MultiSampleState	());
235 	pipelineCreateInfo.addState (PipelineCreateInfo::DynamicState		(dynamicStates));
236 
237 	m_pipeline = createGraphicsPipeline(vk, device, DE_NULL, &pipelineCreateInfo);
238 }
239 
draw(const VkViewport viewport)240 tcu::ConstPixelBufferAccess NegativeViewportHeightTestInstance::draw (const VkViewport viewport)
241 {
242 	const DeviceInterface&	vk					= m_context.getDeviceInterface();
243 	const VkDevice			device				= m_context.getDevice();
244 	const VkQueue			queue				= m_context.getUniversalQueue();
245 	const deUint32			queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
246 
247 	// Command buffer
248 
249 	const CmdPoolCreateInfo			cmdPoolCreateInfo	(queueFamilyIndex);
250 	const Unique<VkCommandPool>		cmdPool				(createCommandPool(vk, device, &cmdPoolCreateInfo));
251 	const Unique<VkCommandBuffer>	cmdBuffer			(allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
252 
253 	// Draw
254 
255 	beginCommandBuffer(vk, *cmdBuffer);
256 
257 	vk.cmdSetViewport(*cmdBuffer, 0u, 1u, &viewport);
258 
259 	{
260 		const VkClearColorValue		clearColor			= makeClearValueColorF32(0.125f, 0.25f, 0.5f, 1.0f).color;
261 		const ImageSubresourceRange subresourceRange	(VK_IMAGE_ASPECT_COLOR_BIT);
262 
263 		initialTransitionColor2DImage(vk, *cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL,
264 									  VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
265 		vk.cmdClearColorImage(*cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &subresourceRange);
266 	}
267 
268 	if (m_params.zeroViewportHeight)
269 	{
270 		// Set zero viewport height
271 		const VkViewport zeroViewportHeight =
272 		{
273 			viewport.x,			// float    x;
274 			viewport.y / 2.0f,	// float    y;
275 			viewport.width,		// float    width;
276 			0.0f,				// float    height;
277 			viewport.minDepth,	// float    minDepth;
278 			viewport.maxDepth	// float    maxDepth;
279 		};
280 
281 		vk.cmdSetViewport(*cmdBuffer, 0u, 1u, &zeroViewportHeight);
282 	}
283 
284 	{
285 		const VkMemoryBarrier memBarrier =
286 		{
287 			VK_STRUCTURE_TYPE_MEMORY_BARRIER,												// VkStructureType    sType;
288 			DE_NULL,																		// const void*        pNext;
289 			VK_ACCESS_TRANSFER_WRITE_BIT,													// VkAccessFlags      srcAccessMask;
290 			VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT		// VkAccessFlags      dstAccessMask;
291 		};
292 
293 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 1, &memBarrier, 0, DE_NULL, 0, DE_NULL);
294 	}
295 
296 	beginRenderPass(vk, *cmdBuffer, *m_renderPass, *m_framebuffer, makeRect2D(0, 0, WIDTH, HEIGHT));
297 
298 	{
299 		const VkDeviceSize	offset	= 0;
300 		const VkBuffer		buffer	= m_vertexBuffer->object();
301 
302 		vk.cmdBindVertexBuffers(*cmdBuffer, 0, 1, &buffer, &offset);
303 	}
304 
305 	vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
306 	vk.cmdDraw(*cmdBuffer, 6, 1, 0, 0);
307 	endRenderPass(vk, *cmdBuffer);
308 	endCommandBuffer(vk, *cmdBuffer);
309 
310 	// Submit
311 	submitCommandsAndWait(vk, device, queue, cmdBuffer.get());
312 
313 	// Get result
314 	{
315 		const VkOffset3D zeroOffset = { 0, 0, 0 };
316 		return m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(), VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, VK_IMAGE_ASPECT_COLOR_BIT);
317 	}
318 }
319 
320 //! Determine if a triangle with triangleFace orientation will be culled or not
isCulled(const VkFrontFace triangleFace) const321 bool NegativeViewportHeightTestInstance::isCulled (const VkFrontFace triangleFace) const
322 {
323 	const bool isFrontFacing = (triangleFace == m_params.frontFace);
324 
325 	if (m_params.cullMode == VK_CULL_MODE_FRONT_BIT && isFrontFacing)
326 		return true;
327 	if (m_params.cullMode == VK_CULL_MODE_BACK_BIT  && !isFrontFacing)
328 		return true;
329 
330 	return m_params.cullMode == VK_CULL_MODE_FRONT_AND_BACK;
331 }
332 
generateReferenceImage(void) const333 MovePtr<tcu::TextureLevel> NegativeViewportHeightTestInstance::generateReferenceImage (void) const
334 {
335 	DE_ASSERT(HEIGHT == WIDTH/2);
336 
337 	MovePtr<tcu::TextureLevel>		image	(new tcu::TextureLevel(mapVkFormat(m_colorAttachmentFormat), WIDTH, HEIGHT));
338 	const tcu::PixelBufferAccess	access	(image->getAccess());
339 	const Vec4						blue	(0.125f, 0.25f, 0.5f, 1.0f);
340 	const Vec4						white	(1.0f);
341 	const Vec4						gray	(0.5f, 0.5f, 0.5f, 1.0f);
342 
343 	tcu::clear(access, blue);
344 
345 	// Zero viewport height
346 	if (m_params.zeroViewportHeight)
347 	{
348 		return image;
349 	}
350 	// Negative viewport height
351 	else
352 	{
353 		const int p1 =      static_cast<int>(static_cast<float>(HEIGHT) * (1.0f - 0.6f) / 2.0f);
354 		const int p2 = p1 + static_cast<int>(static_cast<float>(HEIGHT) * (2.0f * 0.6f) / 2.0f);
355 
356 		// left triangle (CCW -> CW after y-flip)
357 		if (!isCulled(VK_FRONT_FACE_CLOCKWISE))
358 		{
359 			const Vec4& color = (m_params.frontFace == VK_FRONT_FACE_CLOCKWISE ? white : gray);
360 
361 			for (int y = p1; y <= p2; ++y)
362 			for (int x = p1; x <  y;  ++x)
363 				access.setPixel(color, x, y);
364 		}
365 
366 		// right triangle (CW -> CCW after y-flip)
367 		if (!isCulled(VK_FRONT_FACE_COUNTER_CLOCKWISE))
368 		{
369 			const Vec4& color = (m_params.frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE ? white : gray);
370 
371 			for (int y = p1;        y <= p2;          ++y)
372 			for (int x = WIDTH - y; x <  p2 + HEIGHT; ++x)
373 				access.setPixel(color, x, y);
374 		}
375 
376 		return image;
377 	}
378 }
379 
getCullModeStr(const VkCullModeFlagBits cullMode)380 std::string getCullModeStr (const VkCullModeFlagBits cullMode)
381 {
382 	// Cull mode flags are a bit special, because there's a meaning to 0 and or'ed flags.
383 	// The function getCullModeFlagsStr() doesn't work too well in this case.
384 
385 	switch (cullMode)
386 	{
387 		case VK_CULL_MODE_NONE:				return "VK_CULL_MODE_NONE";
388 		case VK_CULL_MODE_FRONT_BIT:		return "VK_CULL_MODE_FRONT_BIT";
389 		case VK_CULL_MODE_BACK_BIT:			return "VK_CULL_MODE_BACK_BIT";
390 		case VK_CULL_MODE_FRONT_AND_BACK:	return "VK_CULL_MODE_FRONT_AND_BACK";
391 
392 		default:
393 			DE_ASSERT(0);
394 			return std::string();
395 	}
396 }
397 
iterate(void)398 tcu::TestStatus NegativeViewportHeightTestInstance::iterate (void)
399 {
400 	// Set up the viewport and draw
401 
402 	const VkViewport viewport =
403 	{
404 		0.0f,							// float    x;
405 		static_cast<float>(HEIGHT),		// float    y;
406 		static_cast<float>(WIDTH),		// float    width;
407 		-static_cast<float>(HEIGHT),	// float    height;
408 		0.0f,							// float    minDepth;
409 		1.0f,							// float    maxDepth;
410 	};
411 
412 	const tcu::ConstPixelBufferAccess	resultImage	= draw(viewport);
413 
414 	// Verify the results
415 
416 	tcu::TestLog&				log				= m_context.getTestContext().getLog();
417 	MovePtr<tcu::TextureLevel>	referenceImage	= generateReferenceImage();
418 
419 	// Zero viewport height
420 	if (m_params.zeroViewportHeight)
421 	{
422 		log << tcu::TestLog::Message
423 			<< "Drawing two triangles with zero viewport height."
424 			<< tcu::TestLog::EndMessage;
425 		log << tcu::TestLog::Message
426 			<< "Result image should be empty."
427 			<< tcu::TestLog::EndMessage;
428 	}
429 	// Negative viewport height
430 	else
431 	{
432 		log << tcu::TestLog::Message
433 			<< "Drawing two triangles with negative viewport height, which will cause a y-flip. This changes the sign of the triangle's area."
434 			<< tcu::TestLog::EndMessage;
435 		log << tcu::TestLog::Message
436 			<< "After the flip, the triangle on the left is CW and the triangle on the right is CCW. Right angles of the both triangles should be at the bottom of the image."
437 			<< " Front face is white, back face is gray."
438 			<< tcu::TestLog::EndMessage;
439 	}
440 
441 	log << tcu::TestLog::Message
442 		<< "Front face: " << getFrontFaceName(m_params.frontFace) << "\n"
443 		<< "Cull mode: "  << getCullModeStr  (m_params.cullMode)  << "\n"
444 		<< tcu::TestLog::EndMessage;
445 
446 	if (!tcu::fuzzyCompare(log, "Image compare", "Image compare", referenceImage->getAccess(), resultImage, 0.02f, tcu::COMPARE_LOG_RESULT))
447 		return tcu::TestStatus::fail("Rendered image is incorrect");
448 	else
449 		return tcu::TestStatus::pass("Pass");
450 }
451 
452 class NegativeViewportHeightTest : public TestCase
453 {
454 public:
NegativeViewportHeightTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TestParams & params)455 	NegativeViewportHeightTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
456 		: TestCase	(testCtx, name, description)
457 		, m_params	(params)
458 	{
459 	}
460 
initPrograms(SourceCollections & programCollection) const461 	void initPrograms (SourceCollections& programCollection) const
462 	{
463 		// Vertex shader
464 		{
465 			std::ostringstream src;
466 			src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
467 				<< "\n"
468 				<< "layout(location = 0) in vec4 in_position;\n"
469 				<< "\n"
470 				<< "out gl_PerVertex {\n"
471 				<< "    vec4  gl_Position;\n"
472 				<< "};\n"
473 				<< "\n"
474 				<< "void main(void)\n"
475 				<< "{\n"
476 				<< "    gl_Position = in_position;\n"
477 				<< "}\n";
478 
479 			programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
480 		}
481 
482 		// Fragment shader
483 		{
484 			std::ostringstream src;
485 			src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
486 				<< "\n"
487 				<< "layout(location = 0) out vec4 out_color;\n"
488 				<< "\n"
489 				<< "void main(void)\n"
490 				<< "{\n"
491 				<< "    if (gl_FrontFacing)\n"
492 				<< "        out_color = vec4(1.0);\n"
493 				<< "    else\n"
494 				<< "        out_color = vec4(vec3(0.5), 1.0);\n"
495 				<< "}\n";
496 
497 			programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
498 		}
499 	}
500 
checkSupport(Context & context) const501 	virtual void checkSupport (Context& context) const
502 	{
503 		context.requireDeviceFunctionality("VK_KHR_maintenance1");
504 	}
505 
createInstance(Context & context) const506 	virtual TestInstance* createInstance (Context& context) const
507 	{
508 		return new NegativeViewportHeightTestInstance(context, m_params);
509 	}
510 
511 private:
512 	const TestParams	m_params;
513 };
514 
populateTestGroup(tcu::TestCaseGroup * testGroup,bool zeroViewportHeight)515 void populateTestGroup (tcu::TestCaseGroup* testGroup, bool zeroViewportHeight)
516 {
517 	const struct
518 	{
519 		const char* const	name;
520 		VkFrontFace			frontFace;
521 	} frontFace[] =
522 	{
523 		{ "front_ccw",	VK_FRONT_FACE_COUNTER_CLOCKWISE	},
524 		{ "front_cw",	VK_FRONT_FACE_CLOCKWISE			},
525 	};
526 
527 	const struct
528 	{
529 		const char* const	name;
530 		VkCullModeFlagBits	cullMode;
531 	} cullMode[] =
532 	{
533 		{ "cull_none",	VK_CULL_MODE_NONE			},
534 		{ "cull_front",	VK_CULL_MODE_FRONT_BIT		},
535 		{ "cull_back",	VK_CULL_MODE_BACK_BIT		},
536 		{ "cull_both",	VK_CULL_MODE_FRONT_AND_BACK	},
537 	};
538 
539 	for (int ndxFrontFace = 0; ndxFrontFace < DE_LENGTH_OF_ARRAY(frontFace); ++ndxFrontFace)
540 	for (int ndxCullMode  = 0; ndxCullMode  < DE_LENGTH_OF_ARRAY(cullMode);  ++ndxCullMode)
541 	{
542 		const TestParams params =
543 		{
544 			frontFace[ndxFrontFace].frontFace,
545 			cullMode[ndxCullMode].cullMode,
546 			zeroViewportHeight
547 		};
548 		std::ostringstream	name;
549 		name << frontFace[ndxFrontFace].name << "_" << cullMode[ndxCullMode].name;
550 
551 		testGroup->addChild(new NegativeViewportHeightTest(testGroup->getTestContext(), name.str(), "", params));
552 	}
553 }
554 
555 }	// anonymous
556 
createNegativeViewportHeightTests(tcu::TestContext & testCtx)557 tcu::TestCaseGroup*	createNegativeViewportHeightTests (tcu::TestContext& testCtx)
558 {
559 	return createTestGroup(testCtx, "negative_viewport_height", "Negative viewport height (VK_KHR_maintenance1)", populateTestGroup, false);
560 }
561 
createZeroViewportHeightTests(tcu::TestContext & testCtx)562 tcu::TestCaseGroup*	createZeroViewportHeightTests (tcu::TestContext& testCtx)
563 {
564 	return createTestGroup(testCtx, "zero_viewport_height", "Zero viewport height (VK_KHR_maintenance1)", populateTestGroup, true);
565 }
566 
567 }	// Draw
568 }	// vkt
569