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