• 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 #include "vkObjUtil.hpp"
37 #include "vkBarrierUtil.hpp"
38 
39 #include "tcuVector.hpp"
40 #include "tcuTextureUtil.hpp"
41 #include "tcuImageCompare.hpp"
42 #include "tcuTestLog.hpp"
43 
44 #include "deSharedPtr.hpp"
45 #include "deRandom.hpp"
46 
47 namespace vkt
48 {
49 namespace Draw
50 {
51 namespace
52 {
53 using namespace vk;
54 using tcu::Vec4;
55 using de::SharedPtr;
56 using de::MovePtr;
57 
58 class DynRenderHelper
59 {
60 public:
DynRenderHelper(const SharedGroupParams params)61 	DynRenderHelper (const SharedGroupParams params)
62 		: m_params(params)
63 		{}
64 
beginSecondaryCmdBuffer(const DeviceInterface & vkd,VkCommandBuffer cmdBuffer,const VkFormat & colorAttachmentFormat) const65 	void beginSecondaryCmdBuffer (const DeviceInterface& vkd, VkCommandBuffer cmdBuffer, const VkFormat& colorAttachmentFormat) const
66 	{
67 #ifndef CTS_USES_VULKANSC
68 		VkRenderingFlags renderingFlags = 0u;
69 		if (m_params->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
70 			renderingFlags |= VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT;
71 
72 		VkCommandBufferInheritanceRenderingInfoKHR inheritanceRenderingInfo
73 		{
74 			VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO_KHR,		// VkStructureType					sType;
75 			DE_NULL,																// const void*						pNext;
76 			renderingFlags,															// VkRenderingFlagsKHR				flags;
77 			0u,																		// uint32_t							viewMask;
78 			1u,																		// uint32_t							colorAttachmentCount;
79 			&colorAttachmentFormat,													// const VkFormat*					pColorAttachmentFormats;
80 			VK_FORMAT_UNDEFINED,													// VkFormat							depthAttachmentFormat;
81 			VK_FORMAT_UNDEFINED,													// VkFormat							stencilAttachmentFormat;
82 			VK_SAMPLE_COUNT_1_BIT,													// VkSampleCountFlagBits			rasterizationSamples;
83 		};
84 		const VkCommandBufferInheritanceInfo bufferInheritanceInfo = initVulkanStructure(&inheritanceRenderingInfo);
85 
86 		VkCommandBufferUsageFlags usageFlags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
87 		if (!m_params->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
88 			usageFlags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
89 
90 		const VkCommandBufferBeginInfo commandBufBeginParams
91 		{
92 			VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,							// VkStructureType					sType;
93 			DE_NULL,																// const void*						pNext;
94 			usageFlags,																// VkCommandBufferUsageFlags		flags;
95 			&bufferInheritanceInfo
96 		};
97 
98 		VK_CHECK(vkd.beginCommandBuffer(cmdBuffer, &commandBufBeginParams));
99 #else
100 		DE_UNREF(vkd);
101 		DE_UNREF(cmdBuffer);
102 		DE_UNREF(colorAttachmentFormat);
103 		DE_ASSERT(false);
104 #endif // CTS_USES_VULKANSC
105 	}
106 
beginRendering(const DeviceInterface & vkd,const VkCommandBuffer cmdBuffer,const bool isPrimaryCmdBuffer,const VkImageView colorImageView,const VkRect2D & renderArea,const VkClearValue & clearValue,const VkImageLayout imageLayout) const107 	void beginRendering(const DeviceInterface&		vkd,
108 						const VkCommandBuffer		cmdBuffer,
109 						const bool					isPrimaryCmdBuffer,
110 						const VkImageView			colorImageView,
111 						const VkRect2D&				renderArea,
112 						const VkClearValue&			clearValue,
113 						const VkImageLayout			imageLayout) const
114 	{
115 #ifndef CTS_USES_VULKANSC
116 		VkRenderingFlagsKHR renderingFlags = 0u;
117 		if (isPrimaryCmdBuffer && m_params->useSecondaryCmdBuffer && !m_params->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
118 			renderingFlags |= VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT;
119 
120 		vk::beginRendering(vkd, cmdBuffer, colorImageView, renderArea, clearValue, imageLayout, VK_ATTACHMENT_LOAD_OP_LOAD, renderingFlags);
121 #else
122 		DE_UNREF(vkd);
123 		DE_UNREF(cmdBuffer);
124 		DE_UNREF(isPrimaryCmdBuffer);
125 		DE_UNREF(colorImageView);
126 		DE_UNREF(renderArea);
127 		DE_UNREF(clearValue);
128 		DE_UNREF(imageLayout);
129 		DE_ASSERT(false);
130 #endif // CTS_USES_VULKANSC
131 	}
132 
133 protected:
134 	SharedGroupParams	m_params;
135 };
136 
137 enum Constants
138 {
139 	WIDTH	= 256,
140 	HEIGHT	= WIDTH/2,
141 };
142 
143 struct TestParams
144 {
145 	VkFrontFace					frontFace;
146 	VkCullModeFlagBits			cullMode;
147 	bool						zeroViewportHeight;
148 	const SharedGroupParams		groupParams;
149 };
150 
151 class NegativeViewportHeightTestInstance : public TestInstance
152 {
153 public:
154 									NegativeViewportHeightTestInstance	(Context& context, const TestParams& params);
155 	tcu::TestStatus					iterate								(void);
156 	void							preRenderCommands					(VkCommandBuffer cmdBuffer, const VkClearValue& clearColor);
157 	void							draw								(VkCommandBuffer cmdBuffer, const VkViewport& viewport);
158 
159 	MovePtr<tcu::TextureLevel>		generateReferenceImage				(void) const;
160 	bool							isCulled							(const VkFrontFace triangleFace) const;
161 
162 private:
163 	const TestParams				m_params;
164 	const DynRenderHelper			m_dynRenderHelper;
165 	const VkFormat					m_colorAttachmentFormat;
166 	SharedPtr<Image>				m_colorTargetImage;
167 	Move<VkImageView>				m_colorTargetView;
168 	SharedPtr<Buffer>				m_vertexBuffer;
169 	Move<VkRenderPass>				m_renderPass;
170 	Move<VkFramebuffer>				m_framebuffer;
171 	Move<VkPipelineLayout>			m_pipelineLayout;
172 	Move<VkPipeline>				m_pipeline;
173 };
174 
NegativeViewportHeightTestInstance(Context & context,const TestParams & params)175 NegativeViewportHeightTestInstance::NegativeViewportHeightTestInstance (Context& context, const TestParams& params)
176 	: TestInstance				(context)
177 	, m_params					(params)
178 	, m_dynRenderHelper			(params.groupParams)
179 	, m_colorAttachmentFormat	(VK_FORMAT_R8G8B8A8_UNORM)
180 {
181 	const DeviceInterface&	vk		= m_context.getDeviceInterface();
182 	const VkDevice			device	= m_context.getDevice();
183 
184 	// Vertex data
185 	{
186 		std::vector<Vec4> vertexData;
187 
188 		// CCW triangle
189 		vertexData.push_back(Vec4(-0.8f, -0.6f, 0.0f, 1.0f));	//  0-----2
190 		vertexData.push_back(Vec4(-0.8f,  0.6f, 0.0f, 1.0f));	//   |  /
191 		vertexData.push_back(Vec4(-0.2f, -0.6f, 0.0f, 1.0f));	//  1|/
192 
193 		// CW triangle
194 		vertexData.push_back(Vec4( 0.2f, -0.6f, 0.0f, 1.0f));	//  0-----1
195 		vertexData.push_back(Vec4( 0.8f, -0.6f, 0.0f, 1.0f));	//    \  |
196 		vertexData.push_back(Vec4( 0.8f,  0.6f, 0.0f, 1.0f));	//      \|2
197 
198 		const VkDeviceSize dataSize = vertexData.size() * sizeof(Vec4);
199 		m_vertexBuffer = Buffer::createAndAlloc(vk, device, BufferCreateInfo(dataSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT),
200 												m_context.getDefaultAllocator(), MemoryRequirement::HostVisible);
201 
202 		deMemcpy(m_vertexBuffer->getBoundMemory().getHostPtr(), &vertexData[0], static_cast<std::size_t>(dataSize));
203 		flushMappedMemoryRange(vk, device, m_vertexBuffer->getBoundMemory().getMemory(), m_vertexBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE);
204 	}
205 
206 	const VkExtent3D		targetImageExtent		= { WIDTH, HEIGHT, 1 };
207 	const VkImageUsageFlags	targetImageUsageFlags	= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
208 
209 	const ImageCreateInfo	targetImageCreateInfo(
210 		VK_IMAGE_TYPE_2D,						// imageType,
211 		m_colorAttachmentFormat,				// format,
212 		targetImageExtent,						// extent,
213 		1u,										// mipLevels,
214 		1u,										// arrayLayers,
215 		VK_SAMPLE_COUNT_1_BIT,					// samples,
216 		VK_IMAGE_TILING_OPTIMAL,				// tiling,
217 		targetImageUsageFlags);					// usage,
218 
219 	m_colorTargetImage = Image::createAndAlloc(vk, device, targetImageCreateInfo, m_context.getDefaultAllocator(), m_context.getUniversalQueueFamilyIndex());
220 
221 	const ImageViewCreateInfo colorTargetViewInfo(m_colorTargetImage->object(), VK_IMAGE_VIEW_TYPE_2D, m_colorAttachmentFormat);
222 	m_colorTargetView = createImageView(vk, device, &colorTargetViewInfo);
223 
224 	// Render pass and framebuffer
225 	if (!m_params.groupParams->useDynamicRendering)
226 	{
227 		RenderPassCreateInfo	renderPassCreateInfo;
228 		renderPassCreateInfo.addAttachment(AttachmentDescription(
229 			m_colorAttachmentFormat,				// format
230 			VK_SAMPLE_COUNT_1_BIT,					// samples
231 			VK_ATTACHMENT_LOAD_OP_LOAD,				// loadOp
232 			VK_ATTACHMENT_STORE_OP_STORE,			// storeOp
233 			VK_ATTACHMENT_LOAD_OP_DONT_CARE,		// stencilLoadOp
234 			VK_ATTACHMENT_STORE_OP_DONT_CARE,		// stencilStoreOp
235 			VK_IMAGE_LAYOUT_GENERAL,				// initialLayout
236 			VK_IMAGE_LAYOUT_GENERAL));				// finalLayout
237 
238 		const VkAttachmentReference colorAttachmentReference =
239 		{
240 			0u,
241 			VK_IMAGE_LAYOUT_GENERAL
242 		};
243 
244 		renderPassCreateInfo.addSubpass(SubpassDescription(
245 			VK_PIPELINE_BIND_POINT_GRAPHICS,		// pipelineBindPoint
246 			(VkSubpassDescriptionFlags)0,			// flags
247 			0u,										// inputAttachmentCount
248 			DE_NULL,								// inputAttachments
249 			1u,										// colorAttachmentCount
250 			&colorAttachmentReference,				// colorAttachments
251 			DE_NULL,								// resolveAttachments
252 			AttachmentReference(),					// depthStencilAttachment
253 			0u,										// preserveAttachmentCount
254 			DE_NULL));								// preserveAttachments
255 
256 		m_renderPass = createRenderPass(vk, device, &renderPassCreateInfo);
257 
258 		std::vector<VkImageView>		colorAttachments		{ *m_colorTargetView };
259 		const FramebufferCreateInfo		framebufferCreateInfo	(*m_renderPass, colorAttachments, WIDTH, HEIGHT, 1);
260 		m_framebuffer = createFramebuffer(vk, device, &framebufferCreateInfo);
261 	}
262 
263 	// Vertex input
264 
265 	const VkVertexInputBindingDescription		vertexInputBindingDescription =
266 	{
267 		0u,										// uint32_t             binding;
268 		sizeof(Vec4),							// uint32_t             stride;
269 		VK_VERTEX_INPUT_RATE_VERTEX,			// VkVertexInputRate    inputRate;
270 	};
271 
272 	const VkVertexInputAttributeDescription		vertexInputAttributeDescription =
273 	{
274 		0u,										// uint32_t    location;
275 		0u,										// uint32_t    binding;
276 		VK_FORMAT_R32G32B32A32_SFLOAT,			// VkFormat    format;
277 		0u										// uint32_t    offset;
278 	};
279 
280 	const PipelineCreateInfo::VertexInputState	vertexInputState = PipelineCreateInfo::VertexInputState(1, &vertexInputBindingDescription,
281 																										1, &vertexInputAttributeDescription);
282 
283 	// Graphics pipeline
284 
285 	const VkRect2D scissor = makeRect2D(WIDTH, HEIGHT);
286 
287 	std::vector<VkDynamicState>		dynamicStates;
288 	dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT);
289 
290 	const Unique<VkShaderModule>	vertexModule	(createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0));
291 	const Unique<VkShaderModule>	fragmentModule	(createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0));
292 
293 	const PipelineLayoutCreateInfo	pipelineLayoutCreateInfo;
294 	m_pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
295 
296 	const PipelineCreateInfo::ColorBlendState::Attachment colorBlendAttachmentState;
297 
298 	PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, (VkPipelineCreateFlags)0);
299 	pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vertexModule,   "main", VK_SHADER_STAGE_VERTEX_BIT));
300 	pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fragmentModule, "main", VK_SHADER_STAGE_FRAGMENT_BIT));
301 	pipelineCreateInfo.addState (PipelineCreateInfo::VertexInputState	(vertexInputState));
302 	pipelineCreateInfo.addState (PipelineCreateInfo::InputAssemblerState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST));
303 	pipelineCreateInfo.addState (PipelineCreateInfo::ColorBlendState	(1, &colorBlendAttachmentState));
304 	pipelineCreateInfo.addState (PipelineCreateInfo::ViewportState		(1, std::vector<VkViewport>(), std::vector<VkRect2D>(1, scissor)));
305 	pipelineCreateInfo.addState (PipelineCreateInfo::DepthStencilState	());
306 	pipelineCreateInfo.addState (PipelineCreateInfo::RasterizerState	(
307 		VK_FALSE,					// depthClampEnable
308 		VK_FALSE,					// rasterizerDiscardEnable
309 		VK_POLYGON_MODE_FILL,		// polygonMode
310 		m_params.cullMode,			// cullMode
311 		m_params.frontFace,			// frontFace
312 		VK_FALSE,					// depthBiasEnable
313 		0.0f,						// depthBiasConstantFactor
314 		0.0f,						// depthBiasClamp
315 		0.0f,						// depthBiasSlopeFactor
316 		1.0f));						// lineWidth
317 	pipelineCreateInfo.addState (PipelineCreateInfo::MultiSampleState	());
318 	pipelineCreateInfo.addState (PipelineCreateInfo::DynamicState		(dynamicStates));
319 
320 #ifndef CTS_USES_VULKANSC
321 	vk::VkPipelineRenderingCreateInfoKHR renderingCreateInfo
322 	{
323 		vk::VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR,
324 		DE_NULL,
325 		0u,
326 		1u,
327 		&m_colorAttachmentFormat,
328 		vk::VK_FORMAT_UNDEFINED,
329 		vk::VK_FORMAT_UNDEFINED
330 	};
331 
332 	if (m_params.groupParams->useDynamicRendering)
333 		pipelineCreateInfo.pNext = &renderingCreateInfo;
334 #endif // CTS_USES_VULKANSC
335 
336 	m_pipeline = createGraphicsPipeline(vk, device, DE_NULL, &pipelineCreateInfo);
337 }
338 
preRenderCommands(VkCommandBuffer cmdBuffer,const VkClearValue & clearColor)339 void NegativeViewportHeightTestInstance::preRenderCommands(VkCommandBuffer cmdBuffer, const VkClearValue& clearColor)
340 {
341 	const DeviceInterface&		vk					= m_context.getDeviceInterface();
342 	const ImageSubresourceRange	subresourceRange	(VK_IMAGE_ASPECT_COLOR_BIT);
343 
344 	initialTransitionColor2DImage(vk, cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL,
345 								  VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
346 	vk.cmdClearColorImage(cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL, &clearColor.color, 1, &subresourceRange);
347 
348 	const VkMemoryBarrier memBarrier
349 	{
350 		VK_STRUCTURE_TYPE_MEMORY_BARRIER,												// VkStructureType		sType;
351 		DE_NULL,																		// const void*			pNext;
352 		VK_ACCESS_TRANSFER_WRITE_BIT,													// VkAccessFlags		srcAccessMask;
353 		VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT		// VkAccessFlags		dstAccessMask;
354 	};
355 
356 	vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 1, &memBarrier, 0, DE_NULL, 0, DE_NULL);
357 }
358 
draw(VkCommandBuffer cmdBuffer,const VkViewport & viewport)359 void NegativeViewportHeightTestInstance::draw (VkCommandBuffer cmdBuffer, const VkViewport& viewport)
360 {
361 	const DeviceInterface&	vk		= m_context.getDeviceInterface();
362 	const VkBuffer			buffer	= m_vertexBuffer->object();
363 	const VkDeviceSize		offset	= 0;
364 
365 	if (m_params.zeroViewportHeight)
366 	{
367 		// Set zero viewport height
368 		const VkViewport zeroViewportHeight
369 		{
370 			viewport.x,			// float    x;
371 			viewport.y / 2.0f,	// float    y;
372 			viewport.width,		// float    width;
373 			0.0f,				// float    height;
374 			viewport.minDepth,	// float    minDepth;
375 			viewport.maxDepth	// float    maxDepth;
376 		};
377 
378 		vk.cmdSetViewport(cmdBuffer, 0u, 1u, &zeroViewportHeight);
379 	}
380 	else
381 		vk.cmdSetViewport(cmdBuffer, 0u, 1u, &viewport);
382 
383 	vk.cmdBindVertexBuffers(cmdBuffer, 0, 1, &buffer, &offset);
384 	vk.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
385 	vk.cmdDraw(cmdBuffer, 6, 1, 0, 0);
386 }
387 
388 //! Determine if a triangle with triangleFace orientation will be culled or not
isCulled(const VkFrontFace triangleFace) const389 bool NegativeViewportHeightTestInstance::isCulled (const VkFrontFace triangleFace) const
390 {
391 	const bool isFrontFacing = (triangleFace == m_params.frontFace);
392 
393 	if (m_params.cullMode == VK_CULL_MODE_FRONT_BIT && isFrontFacing)
394 		return true;
395 	if (m_params.cullMode == VK_CULL_MODE_BACK_BIT  && !isFrontFacing)
396 		return true;
397 
398 	return m_params.cullMode == VK_CULL_MODE_FRONT_AND_BACK;
399 }
400 
generateReferenceImage(void) const401 MovePtr<tcu::TextureLevel> NegativeViewportHeightTestInstance::generateReferenceImage (void) const
402 {
403 	DE_ASSERT(HEIGHT == WIDTH/2);
404 
405 	MovePtr<tcu::TextureLevel>		image	(new tcu::TextureLevel(mapVkFormat(m_colorAttachmentFormat), WIDTH, HEIGHT));
406 	const tcu::PixelBufferAccess	access	(image->getAccess());
407 	const Vec4						blue	(0.125f, 0.25f, 0.5f, 1.0f);
408 	const Vec4						white	(1.0f);
409 	const Vec4						gray	(0.5f, 0.5f, 0.5f, 1.0f);
410 
411 	tcu::clear(access, blue);
412 
413 	// Zero viewport height
414 	if (m_params.zeroViewportHeight)
415 	{
416 		return image;
417 	}
418 	// Negative viewport height
419 	else
420 	{
421 		const int p1 =      static_cast<int>(static_cast<float>(HEIGHT) * (1.0f - 0.6f) / 2.0f);
422 		const int p2 = p1 + static_cast<int>(static_cast<float>(HEIGHT) * (2.0f * 0.6f) / 2.0f);
423 
424 		// left triangle (CCW -> CW after y-flip)
425 		if (!isCulled(VK_FRONT_FACE_CLOCKWISE))
426 		{
427 			const Vec4& color = (m_params.frontFace == VK_FRONT_FACE_CLOCKWISE ? white : gray);
428 
429 			for (int y = p1; y <= p2; ++y)
430 			for (int x = p1; x <  y;  ++x)
431 				access.setPixel(color, x, y);
432 		}
433 
434 		// right triangle (CW -> CCW after y-flip)
435 		if (!isCulled(VK_FRONT_FACE_COUNTER_CLOCKWISE))
436 		{
437 			const Vec4& color = (m_params.frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE ? white : gray);
438 
439 			for (int y = p1;        y <= p2;          ++y)
440 			for (int x = WIDTH - y; x <  p2 + HEIGHT; ++x)
441 				access.setPixel(color, x, y);
442 		}
443 
444 		return image;
445 	}
446 }
447 
getCullModeStr(const VkCullModeFlagBits cullMode)448 std::string getCullModeStr (const VkCullModeFlagBits cullMode)
449 {
450 	// Cull mode flags are a bit special, because there's a meaning to 0 and or'ed flags.
451 	// The function getCullModeFlagsStr() doesn't work too well in this case.
452 
453 	switch (cullMode)
454 	{
455 		case VK_CULL_MODE_NONE:				return "VK_CULL_MODE_NONE";
456 		case VK_CULL_MODE_FRONT_BIT:		return "VK_CULL_MODE_FRONT_BIT";
457 		case VK_CULL_MODE_BACK_BIT:			return "VK_CULL_MODE_BACK_BIT";
458 		case VK_CULL_MODE_FRONT_AND_BACK:	return "VK_CULL_MODE_FRONT_AND_BACK";
459 
460 		default:
461 			DE_ASSERT(0);
462 			return std::string();
463 	}
464 }
465 
iterate(void)466 tcu::TestStatus NegativeViewportHeightTestInstance::iterate (void)
467 {
468 	// Set up the viewport and draw
469 
470 	const VkViewport viewport
471 	{
472 		0.0f,							// float    x;
473 		static_cast<float>(HEIGHT),		// float    y;
474 		static_cast<float>(WIDTH),		// float    width;
475 		-static_cast<float>(HEIGHT),	// float    height;
476 		0.0f,							// float    minDepth;
477 		1.0f,							// float    maxDepth;
478 	};
479 	VkRect2D rect = makeRect2D(0, 0, WIDTH, HEIGHT);
480 
481 	const DeviceInterface&			vk					= m_context.getDeviceInterface();
482 	const VkDevice					device				= m_context.getDevice();
483 	const VkQueue					queue				= m_context.getUniversalQueue();
484 	const deUint32					queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
485 	const VkClearValue				clearColor			= makeClearValueColorF32(0.125f, 0.25f, 0.5f, 1.0f);
486 	const CmdPoolCreateInfo			cmdPoolCreateInfo	(queueFamilyIndex);
487 	const Unique<VkCommandPool>		cmdPool				(createCommandPool(vk, device, &cmdPoolCreateInfo));
488 	const Unique<VkCommandBuffer>	cmdBuffer			(allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
489 	Move<VkCommandBuffer>			secCmdBuffer;
490 
491 #ifndef CTS_USES_VULKANSC
492 	if (m_params.groupParams->useSecondaryCmdBuffer)
493 	{
494 		secCmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
495 
496 		// record secondary command buffer
497 		m_dynRenderHelper.beginSecondaryCmdBuffer(vk, *secCmdBuffer, m_colorAttachmentFormat);
498 
499 		if (m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
500 			m_dynRenderHelper.beginRendering(vk, *secCmdBuffer, false/*isPrimary*/, *m_colorTargetView, rect, clearColor, VK_IMAGE_LAYOUT_GENERAL);
501 
502 		draw(*secCmdBuffer, viewport);
503 
504 		if (m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
505 			endRendering(vk, *secCmdBuffer);
506 
507 		endCommandBuffer(vk, *secCmdBuffer);
508 
509 		// record primary command buffer
510 		beginCommandBuffer(vk, *cmdBuffer, 0u);
511 
512 		preRenderCommands(*cmdBuffer, clearColor);
513 
514 		if (!m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
515 			m_dynRenderHelper.beginRendering(vk, *cmdBuffer, true/*isPrimary*/, *m_colorTargetView, rect, clearColor, VK_IMAGE_LAYOUT_GENERAL);
516 
517 		vk.cmdExecuteCommands(*cmdBuffer, 1u, &*secCmdBuffer);
518 
519 		if (!m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
520 			endRendering(vk, *cmdBuffer);
521 
522 		endCommandBuffer(vk, *cmdBuffer);
523 	}
524 	else if (m_params.groupParams->useDynamicRendering)
525 	{
526 		beginCommandBuffer(vk, *cmdBuffer);
527 
528 		preRenderCommands(*cmdBuffer, clearColor);
529 		m_dynRenderHelper.beginRendering(vk, *cmdBuffer, true/*isPrimary*/, *m_colorTargetView, rect, clearColor, VK_IMAGE_LAYOUT_GENERAL);
530 		draw(*cmdBuffer, viewport);
531 		endRendering(vk, *cmdBuffer);
532 
533 		endCommandBuffer(vk, *cmdBuffer);
534 	}
535 #endif // CTS_USES_VULKANSC
536 
537 	if (!m_params.groupParams->useDynamicRendering)
538 	{
539 		beginCommandBuffer(vk, *cmdBuffer);
540 
541 		preRenderCommands(*cmdBuffer, clearColor);
542 		beginRenderPass(vk, *cmdBuffer, *m_renderPass, *m_framebuffer, rect);
543 		draw(*cmdBuffer, viewport);
544 		endRenderPass(vk, *cmdBuffer);
545 
546 		endCommandBuffer(vk, *cmdBuffer);
547 	}
548 
549 	// Submit
550 	submitCommandsAndWait(vk, device, queue, cmdBuffer.get());
551 
552 	// Get result
553 	const VkOffset3D					zeroOffset	= { 0, 0, 0 };
554 	const tcu::ConstPixelBufferAccess	resultImage	= m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(), VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, VK_IMAGE_ASPECT_COLOR_BIT);
555 
556 	// Verify the results
557 
558 	tcu::TestLog&				log				= m_context.getTestContext().getLog();
559 	MovePtr<tcu::TextureLevel>	referenceImage	= generateReferenceImage();
560 
561 	// Zero viewport height
562 	if (m_params.zeroViewportHeight)
563 	{
564 		log << tcu::TestLog::Message
565 			<< "Drawing two triangles with zero viewport height."
566 			<< tcu::TestLog::EndMessage;
567 		log << tcu::TestLog::Message
568 			<< "Result image should be empty."
569 			<< tcu::TestLog::EndMessage;
570 	}
571 	// Negative viewport height
572 	else
573 	{
574 		log << tcu::TestLog::Message
575 			<< "Drawing two triangles with negative viewport height, which will cause a y-flip. This changes the sign of the triangle's area."
576 			<< tcu::TestLog::EndMessage;
577 		log << tcu::TestLog::Message
578 			<< "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."
579 			<< " Front face is white, back face is gray."
580 			<< tcu::TestLog::EndMessage;
581 	}
582 
583 	log << tcu::TestLog::Message
584 		<< "Front face: " << getFrontFaceName(m_params.frontFace) << "\n"
585 		<< "Cull mode: "  << getCullModeStr  (m_params.cullMode)  << "\n"
586 		<< tcu::TestLog::EndMessage;
587 
588 	if (!tcu::fuzzyCompare(log, "Image compare", "Image compare", referenceImage->getAccess(), resultImage, 0.02f, tcu::COMPARE_LOG_RESULT))
589 		return tcu::TestStatus::fail("Rendered image is incorrect");
590 	else
591 		return tcu::TestStatus::pass("Pass");
592 }
593 
594 class NegativeViewportHeightTest : public TestCase
595 {
596 public:
NegativeViewportHeightTest(tcu::TestContext & testCtx,const std::string & name,const TestParams & params)597 	NegativeViewportHeightTest (tcu::TestContext& testCtx, const std::string& name, const TestParams& params)
598 		: TestCase	(testCtx, name)
599 		, m_params	(params)
600 	{
601 	}
602 
initPrograms(SourceCollections & programCollection) const603 	void initPrograms (SourceCollections& programCollection) const
604 	{
605 		// Vertex shader
606 		{
607 			std::ostringstream src;
608 			src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
609 				<< "\n"
610 				<< "layout(location = 0) in vec4 in_position;\n"
611 				<< "\n"
612 				<< "out gl_PerVertex {\n"
613 				<< "    vec4  gl_Position;\n"
614 				<< "};\n"
615 				<< "\n"
616 				<< "void main(void)\n"
617 				<< "{\n"
618 				<< "    gl_Position = in_position;\n"
619 				<< "}\n";
620 
621 			programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
622 		}
623 
624 		// Fragment shader
625 		{
626 			std::ostringstream src;
627 			src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
628 				<< "\n"
629 				<< "layout(location = 0) out vec4 out_color;\n"
630 				<< "\n"
631 				<< "void main(void)\n"
632 				<< "{\n"
633 				<< "    if (gl_FrontFacing)\n"
634 				<< "        out_color = vec4(1.0);\n"
635 				<< "    else\n"
636 				<< "        out_color = vec4(vec3(0.5), 1.0);\n"
637 				<< "}\n";
638 
639 			programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
640 		}
641 	}
642 
checkSupport(Context & context) const643 	virtual void checkSupport (Context& context) const
644 	{
645 		if (m_params.groupParams->useDynamicRendering)
646 			context.requireDeviceFunctionality("VK_KHR_dynamic_rendering");
647 
648 		context.requireDeviceFunctionality("VK_KHR_maintenance1");
649 	}
650 
createInstance(Context & context) const651 	virtual TestInstance* createInstance (Context& context) const
652 	{
653 		return new NegativeViewportHeightTestInstance(context, m_params);
654 	}
655 
656 private:
657 	const TestParams	m_params;
658 };
659 
660 struct SubGroupParams
661 {
662 	bool					zeroViewportHeight;
663 	const SharedGroupParams	groupParams;
664 };
665 
populateTestGroup(tcu::TestCaseGroup * testGroup,SubGroupParams subGroupParams)666 void populateTestGroup (tcu::TestCaseGroup* testGroup, SubGroupParams subGroupParams)
667 {
668 	const struct
669 	{
670 		const char* const	name;
671 		VkFrontFace			frontFace;
672 	} frontFace[] =
673 	{
674 		{ "front_ccw",	VK_FRONT_FACE_COUNTER_CLOCKWISE	},
675 		{ "front_cw",	VK_FRONT_FACE_CLOCKWISE			},
676 	};
677 
678 	const struct
679 	{
680 		const char* const	name;
681 		VkCullModeFlagBits	cullMode;
682 	} cullMode[] =
683 	{
684 		{ "cull_none",	VK_CULL_MODE_NONE			},
685 		{ "cull_front",	VK_CULL_MODE_FRONT_BIT		},
686 		{ "cull_back",	VK_CULL_MODE_BACK_BIT		},
687 		{ "cull_both",	VK_CULL_MODE_FRONT_AND_BACK	},
688 	};
689 
690 	for (int ndxFrontFace = 0; ndxFrontFace < DE_LENGTH_OF_ARRAY(frontFace); ++ndxFrontFace)
691 	for (int ndxCullMode  = 0; ndxCullMode  < DE_LENGTH_OF_ARRAY(cullMode);  ++ndxCullMode)
692 	{
693 		const TestParams params =
694 		{
695 			frontFace[ndxFrontFace].frontFace,
696 			cullMode[ndxCullMode].cullMode,
697 			subGroupParams.zeroViewportHeight,
698 			subGroupParams.groupParams
699 		};
700 		std::ostringstream	name;
701 		name << frontFace[ndxFrontFace].name << "_" << cullMode[ndxCullMode].name;
702 
703 		testGroup->addChild(new NegativeViewportHeightTest(testGroup->getTestContext(), name.str(), params));
704 	}
705 }
706 
707 enum class OffScreenAxisCase
708 {
709 	ONSCREEN		= 0,
710 	NEGATIVE_SIDE	= 1,
711 	POSITIVE_SIDE	= 2,
712 };
713 
714 struct OffScreenParams
715 {
716 	const uint32_t			randomSeed;
717 	const OffScreenAxisCase	xAxis;
718 	const OffScreenAxisCase	yAxis;
719 	const bool				negativeHeight;
720 	const SharedGroupParams	groupParams;
721 
OffScreenParamsvkt::Draw::__anon89b8d6ff0111::OffScreenParams722 	OffScreenParams (uint32_t seed, OffScreenAxisCase x, OffScreenAxisCase y, bool negH, const SharedGroupParams gp)
723 		: randomSeed		(seed)
724 		, xAxis				(x)
725 		, yAxis				(y)
726 		, negativeHeight	(negH)
727 		, groupParams		(gp)
728 	{
729 		// At least one of them must be offscreen.
730 		DE_ASSERT(xAxis != OffScreenAxisCase::ONSCREEN || yAxis != OffScreenAxisCase::ONSCREEN);
731 	}
732 };
733 
734 class OffScreenViewportCase : public vkt::TestCase
735 {
736 public:
737 	static constexpr int32_t	kFramebufferSize	= 32;	// Width and Height of framebuffer.
738 	static constexpr int32_t	kViewportMaxDim		= 1024;	// When generating offscreen coords, use this limit as the negative or positive max coord for X/Y.
739 	static constexpr uint32_t	kVertexCount		= 4u;
740 
741 	// Choose a couple of values for the Axis range (X or Y) according to the chosen Axis case.
genAxis(de::Random & rnd,OffScreenAxisCase axisCase)742 	static tcu::IVec2 genAxis (de::Random& rnd, OffScreenAxisCase axisCase)
743 	{
744 		int32_t minVal = 0;
745 		int32_t maxVal = 0;
746 
747 		if (axisCase == OffScreenAxisCase::ONSCREEN)
748 			maxVal = kFramebufferSize - 1;
749 		else if (axisCase == OffScreenAxisCase::NEGATIVE_SIDE)
750 		{
751 			minVal = -kViewportMaxDim;
752 			maxVal = -1;
753 		}
754 		else if (axisCase == OffScreenAxisCase::POSITIVE_SIDE)
755 		{
756 			minVal = kFramebufferSize + 1;
757 			maxVal = kViewportMaxDim;
758 		}
759 
760 		const auto a = rnd.getInt(minVal, maxVal);
761 		const auto b = rnd.getInt(minVal, maxVal);
762 
763 		const tcu::IVec2 axisRange (de::min(a, b), de::max(a, b));
764 		return axisRange;
765 	}
766 
OffScreenViewportCase(tcu::TestContext & testCtx,const std::string & name,const OffScreenParams & params)767 					OffScreenViewportCase	(tcu::TestContext& testCtx, const std::string& name, const OffScreenParams& params)
768 						: vkt::TestCase	(testCtx, name)
769 						, m_params		(params)
770 						{}
771 
~OffScreenViewportCase(void)772 	virtual			~OffScreenViewportCase	(void) {}
773 
774 	void			initPrograms	(vk::SourceCollections& programCollection) const override;
775 	TestInstance*	createInstance	(Context& context) const override;
776 	void			checkSupport	(Context& context) const override;
777 
778 protected:
779 	const OffScreenParams m_params;
780 };
781 
782 class OffScreenViewportInstance : public vkt::TestInstance
783 {
784 public:
OffScreenViewportInstance(Context & context,const OffScreenParams & params)785 						OffScreenViewportInstance	(Context& context, const OffScreenParams& params)
786 							: vkt::TestInstance	(context)
787 							, m_params			(params)
788 							, m_dynRenderHelper	(params.groupParams)
789 							{}
790 
~OffScreenViewportInstance(void)791 	virtual				~OffScreenViewportInstance	(void) {}
792 
793 	tcu::TestStatus		iterate						(void) override;
794 
795 protected:
796 	const OffScreenParams	m_params;
797 	const DynRenderHelper	m_dynRenderHelper;
798 };
799 
createInstance(Context & context) const800 TestInstance* OffScreenViewportCase::createInstance (Context &context) const
801 {
802 	return new OffScreenViewportInstance(context, m_params);
803 }
804 
checkSupport(Context & context) const805 void OffScreenViewportCase::checkSupport (Context &context) const
806 {
807 	if (m_params.groupParams->useDynamicRendering)
808 		context.requireDeviceFunctionality("VK_KHR_dynamic_rendering");
809 
810 	if (m_params.negativeHeight)
811 		context.requireDeviceFunctionality("VK_KHR_maintenance1");
812 }
813 
initPrograms(vk::SourceCollections & programCollection) const814 void OffScreenViewportCase::initPrograms (vk::SourceCollections &programCollection) const
815 {
816 	std::ostringstream vert;
817 	vert
818 		<< "#version 460\n"
819 		<< "const int vertexCount = " << kVertexCount << ";\n"
820 		<< "vec2 positions[vertexCount] = vec2[](\n"
821 		<< "    vec2(-1.0, -1.0),\n"
822 		<< "    vec2(-1.0,  1.0),\n"
823 		<< "    vec2( 1.0, -1.0),\n"
824 		<< "    vec2( 1.0,  1.0)\n"
825 		<< ");\n"
826 		<< "void main (void) { gl_Position = vec4(positions[gl_VertexIndex % vertexCount], 0.0, 1.0); }\n"
827 		;
828 	programCollection.glslSources.add("vert") << glu::VertexSource(vert.str());
829 
830 	std::ostringstream frag;
831 	frag
832 		<< "#version 460\n"
833 		<< "layout (location=0) out vec4 outColor;\n"
834 		<< "void main (void) { outColor = vec4(0.0, 0.0, 1.0, 1.0); }\n"
835 		;
836 	programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
837 }
838 
iterate(void)839 tcu::TestStatus OffScreenViewportInstance::iterate (void)
840 {
841 	de::Random rnd(m_params.randomSeed);
842 
843 	// Pseudorandomly generate viewport data.
844 	const auto	xAxis	= OffScreenViewportCase::genAxis(rnd, m_params.xAxis);
845 	auto		yAxis	= OffScreenViewportCase::genAxis(rnd, m_params.yAxis);
846 	const auto	width	= xAxis.y() - xAxis.x() + 1;
847 	auto		height	= yAxis.y() - yAxis.x() + 1;
848 
849 	if (m_params.negativeHeight)
850 	{
851 		height = -height;
852 		std::swap(yAxis[0], yAxis[1]);
853 	}
854 
855 	const VkViewport testViewport =
856 	{
857 		static_cast<float>(xAxis.x()),	//	float	x;
858 		static_cast<float>(yAxis.x()),	//	float	y;
859 		static_cast<float>(width),		//	float	width;
860 		static_cast<float>(height),		//	float	height;
861 		0.0f,							//	float	minDepth;
862 		1.0f,							//	float	maxDepth;
863 	};
864 
865 	// Framebuffer parameters.
866 	const auto kIFbSize	= OffScreenViewportCase::kFramebufferSize;
867 	const auto fbSize	= static_cast<uint32_t>(kIFbSize);
868 	const auto fbExtent	= makeExtent3D(fbSize, fbSize, 1u);
869 	const auto fbFormat	= VK_FORMAT_R8G8B8A8_UNORM;
870 	const auto fbUsage	= (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
871 
872 	const auto&				ctx			= m_context.getContextCommonData();
873 	CommandPoolWithBuffer	cmd			(ctx.vkd, ctx.device, ctx.qfIndex);
874 	ImageWithBuffer			colorRes	(ctx.vkd, ctx.device, ctx.allocator, fbExtent, fbFormat, fbUsage, VK_IMAGE_TYPE_2D);
875 
876 	const auto&		binaries	= m_context.getBinaryCollection();
877 	const auto		vertModule	= createShaderModule(ctx.vkd, ctx.device, binaries.get("vert"));
878 	const auto		fragModule	= createShaderModule(ctx.vkd, ctx.device, binaries.get("frag"));
879 
880 	// Render pass and framebuffer.
881 	const auto		renderPass	= makeRenderPass(ctx.vkd, ctx.device, fbFormat, VK_FORMAT_UNDEFINED /* DS format */, VK_ATTACHMENT_LOAD_OP_LOAD);
882 	const auto		framebuffer	= makeFramebuffer(ctx.vkd, ctx.device, renderPass.get(), colorRes.getImageView(), fbExtent.width, fbExtent.height);
883 
884 	// Pipeline.
885 	const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = initVulkanStructure();
886 
887 	const std::vector<VkViewport>	viewports(1u, testViewport);
888 	const std::vector<VkRect2D>		scissors (1u, makeRect2D(fbExtent));
889 
890 	const auto pipelineLayout	= makePipelineLayout(ctx.vkd, ctx.device);
891 	const auto pipelineRP		= (m_params.groupParams->useDynamicRendering ? VK_NULL_HANDLE : renderPass.get());
892 	const auto pipeline			= makeGraphicsPipeline(ctx.vkd, ctx.device, pipelineLayout.get(),
893 		vertModule.get(), VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, fragModule.get(),
894 		pipelineRP, viewports, scissors, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 0u, 0u, &vertexInputStateCreateInfo);
895 
896 	const auto cmdBuffer		= cmd.cmdBuffer.get();
897 	const auto secCmdBufferPtr	= (m_params.groupParams->useSecondaryCmdBuffer
898 								? allocateCommandBuffer(ctx.vkd, ctx.device, cmd.cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_SECONDARY)
899 								: Move<VkCommandBuffer>());
900 	const auto secCmdBuffer		= secCmdBufferPtr.get();
901 	const auto clearColor		= tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
902 	const auto clearColorVal	= makeClearValueColorVec4(clearColor);
903 	const auto colorSRR			= makeDefaultImageSubresourceRange();
904 
905 	// Draw (offscreen due to the viewport).
906 	beginCommandBuffer(ctx.vkd, cmdBuffer);
907 
908 	// Clear color image outside render pass.
909 	const auto preClearBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_TRANSFER_WRITE_BIT,
910 		VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
911 		colorRes.getImage(), colorSRR);
912 	cmdPipelineImageMemoryBarrier(ctx.vkd, cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, &preClearBarrier);
913 
914 	ctx.vkd.cmdClearColorImage(cmdBuffer, colorRes.getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColorVal.color, 1u, &colorSRR);
915 
916 	const auto postClearBarrier = makeImageMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT),
917 		VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
918 		colorRes.getImage(), colorSRR);
919 	cmdPipelineImageMemoryBarrier(ctx.vkd, cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, &postClearBarrier);
920 
921 	// Render pass.
922 	if (!m_params.groupParams->useDynamicRendering)
923 	{
924 		beginRenderPass(ctx.vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0u));
925 		ctx.vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
926 		ctx.vkd.cmdDraw(cmdBuffer, OffScreenViewportCase::kVertexCount, 1u, 0u, 0u);
927 		endRenderPass(ctx.vkd, cmdBuffer);
928 	}
929 	else
930 	{
931 #ifndef CTS_USES_VULKANSC
932 		const bool secondary				= m_params.groupParams->useSecondaryCmdBuffer;
933 		const bool allInSecondary			= m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass;
934 		const auto beginEndCmdBuffer		= (allInSecondary ? secCmdBuffer : cmdBuffer);
935 		const auto rpContentsCmdBuffer		= (secondary ? secCmdBuffer : cmdBuffer);
936 		const auto endAndExecuteSecondary	= [&cmdBuffer, &secCmdBuffer, &ctx](void)
937 			{
938 				endCommandBuffer(ctx.vkd, secCmdBuffer);
939 				ctx.vkd.cmdExecuteCommands(cmdBuffer, 1u, &secCmdBuffer);
940 			};
941 
942 		if (secondary)
943 			m_dynRenderHelper.beginSecondaryCmdBuffer(ctx.vkd, secCmdBuffer, fbFormat);
944 
945 		m_dynRenderHelper.beginRendering(ctx.vkd, beginEndCmdBuffer, !allInSecondary/*isPrimary*/, colorRes.getImageView(), scissors.at(0), clearColorVal, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
946 		ctx.vkd.cmdBindPipeline(rpContentsCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
947 		ctx.vkd.cmdDraw(rpContentsCmdBuffer, OffScreenViewportCase::kVertexCount, 1u, 0u, 0u);
948 		if (secondary && !allInSecondary)
949 			endAndExecuteSecondary();
950 		endRendering(ctx.vkd, beginEndCmdBuffer);
951 
952 		if (secondary && allInSecondary)
953 			endAndExecuteSecondary();
954 #else
955 		DE_UNREF(secCmdBuffer);
956 		DE_ASSERT(false);
957 #endif // CTS_USES_VULKANSC
958 	}
959 
960 	// Copy to results buffer.
961 	copyImageToBuffer(ctx.vkd, cmdBuffer, colorRes.getImage(), colorRes.getBuffer(), tcu::IVec2(kIFbSize, kIFbSize),
962 		VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
963 		1u, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_ASPECT_COLOR_BIT,
964 		VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
965 
966 	endCommandBuffer(ctx.vkd, cmdBuffer);
967 	submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, cmdBuffer);
968 
969 	// Verify color buffer.
970 	invalidateAlloc(ctx.vkd, ctx.device, colorRes.getBufferAllocation());
971 
972 	const tcu::ConstPixelBufferAccess	resultAccess	(mapVkFormat(fbFormat), tcu::IVec3(kIFbSize, kIFbSize, 1), colorRes.getBufferAllocation().getHostPtr());
973 	auto&								log				= m_context.getTestContext().getLog();
974 	const tcu::Vec4						threshold		(0.0f, 0.0f, 0.0f, 0.0f);
975 
976 	if (!tcu::floatThresholdCompare(log, "Result", "", clearColor, resultAccess, threshold, tcu::COMPARE_LOG_ON_ERROR))
977 		return tcu::TestStatus::fail("Unexpected color result; check log for details");
978 
979 	return tcu::TestStatus::pass("Pass");
980 }
981 
982 }	// anonymous
983 
createNegativeViewportHeightTests(tcu::TestContext & testCtx,const SharedGroupParams groupParams)984 tcu::TestCaseGroup*	createNegativeViewportHeightTests (tcu::TestContext& testCtx, const SharedGroupParams groupParams)
985 {
986 	SubGroupParams subGroupParams { false, groupParams };
987 	// Negative viewport height (VK_KHR_maintenance1)
988 	return createTestGroup(testCtx, "negative_viewport_height", populateTestGroup, subGroupParams);
989 }
990 
createZeroViewportHeightTests(tcu::TestContext & testCtx,const SharedGroupParams groupParams)991 tcu::TestCaseGroup*	createZeroViewportHeightTests (tcu::TestContext& testCtx, const SharedGroupParams groupParams)
992 {
993 	SubGroupParams subGroupParams{ false, groupParams };
994 	// Zero viewport height (VK_KHR_maintenance1)
995 	return createTestGroup(testCtx, "zero_viewport_height", populateTestGroup, subGroupParams);
996 }
997 
createOffScreenViewportTests(tcu::TestContext & testCtx,const SharedGroupParams groupParams)998 tcu::TestCaseGroup* createOffScreenViewportTests (tcu::TestContext& testCtx, const SharedGroupParams groupParams)
999 {
1000 	using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
1001 
1002 	const struct
1003 	{
1004 		const OffScreenAxisCase	axisCase;
1005 		const char*				suffix;
1006 	} axisCases[] =
1007 	{
1008 		{ OffScreenAxisCase::ONSCREEN,		"_on_screen"			},
1009 		{ OffScreenAxisCase::NEGATIVE_SIDE,	"_off_screen_negative"	},
1010 		{ OffScreenAxisCase::POSITIVE_SIDE,	"_off_screen_positive"	},
1011 	};
1012 
1013 	const struct
1014 	{
1015 		const bool			negativeHeight;
1016 		const char*			suffix;
1017 	} negativeHeightCases[] =
1018 	{
1019 		{ false,		""					},
1020 		{ true,			"_negative_height"	},
1021 	};
1022 
1023 	uint32_t seed	= 1674229780;
1024 	// Test using off-screen viewports
1025 	GroupPtr group	(new tcu::TestCaseGroup(testCtx, "offscreen_viewport"));
1026 
1027 	for (const auto& xCase : axisCases)
1028 		for (const auto& yCase : axisCases)
1029 		{
1030 			// At least one of the axis has to be offscreen for the framebuffer to remain clear.
1031 			if (xCase.axisCase == OffScreenAxisCase::ONSCREEN && yCase.axisCase == OffScreenAxisCase::ONSCREEN)
1032 				continue;
1033 
1034 			for (const auto& negHeightCase : negativeHeightCases)
1035 			{
1036 				OffScreenParams params(seed, xCase.axisCase, yCase.axisCase, negHeightCase.negativeHeight, groupParams);
1037 				++seed;
1038 
1039 				const auto testName = std::string("x") + xCase.suffix + "_y" + yCase.suffix + negHeightCase.suffix;
1040 				group->addChild(new OffScreenViewportCase(testCtx, testName, params));
1041 			}
1042 		}
1043 	return group.release();
1044 }
1045 
1046 }	// Draw
1047 }	// vkt
1048