1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2015 The Khronos Group Inc.
6 * Copyright (c) 2015 Intel Corporation
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 Dynamic State Viewport Tests
23 *//*--------------------------------------------------------------------*/
24
25#include "vktDynamicStateVPTests.hpp"
26
27#include "vktDynamicStateBaseClass.hpp"
28#include "vktDynamicStateTestCaseUtil.hpp"
29
30#include "vkImageUtil.hpp"
31
32#include "tcuTextureUtil.hpp"
33#include "tcuImageCompare.hpp"
34#include "tcuRGBA.hpp"
35
36namespace vkt
37{
38namespace DynamicState
39{
40
41using namespace Draw;
42
43namespace
44{
45
46class ViewportStateBaseCase : public DynamicStateBaseClass
47{
48public:
49	ViewportStateBaseCase (Context& context, const char* vertexShaderName, const char* fragmentShaderName)
50		: DynamicStateBaseClass	(context, vertexShaderName, fragmentShaderName)
51	{}
52
53	void initialize(void)
54	{
55		m_topology = vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
56
57		m_data.push_back(PositionColorVertex(tcu::Vec4(-0.5f, 0.5f, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
58		m_data.push_back(PositionColorVertex(tcu::Vec4(0.5f, 0.5f, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
59		m_data.push_back(PositionColorVertex(tcu::Vec4(-0.5f, -0.5f, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
60		m_data.push_back(PositionColorVertex(tcu::Vec4(0.5f, -0.5f, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
61
62		DynamicStateBaseClass::initialize();
63	}
64
65	virtual tcu::Texture2D buildReferenceFrame (void)
66	{
67		DE_ASSERT(false);
68		return tcu::Texture2D(tcu::TextureFormat(), 0, 0);
69	}
70
71	virtual void setDynamicStates (void)
72	{
73		DE_ASSERT(false);
74	}
75
76	virtual tcu::TestStatus iterate (void)
77	{
78		tcu::TestLog &log			= m_context.getTestContext().getLog();
79		const vk::VkQueue queue		= m_context.getUniversalQueue();
80
81		beginRenderPass();
82
83		// set states here
84		setDynamicStates();
85
86		m_vk.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
87
88		const vk::VkDeviceSize vertexBufferOffset = 0;
89		const vk::VkBuffer vertexBuffer = m_vertexBuffer->object();
90		m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
91
92		m_vk.cmdDraw(*m_cmdBuffer, static_cast<deUint32>(m_data.size()), 1, 0, 0);
93
94		m_vk.cmdEndRenderPass(*m_cmdBuffer);
95		m_vk.endCommandBuffer(*m_cmdBuffer);
96
97		vk::VkSubmitInfo submitInfo =
98		{
99			vk::VK_STRUCTURE_TYPE_SUBMIT_INFO,	// VkStructureType			sType;
100			DE_NULL,							// const void*				pNext;
101			0,									// deUint32					waitSemaphoreCount;
102			DE_NULL,							// const VkSemaphore*		pWaitSemaphores;
103			(const vk::VkPipelineStageFlags*)DE_NULL,
104			1,									// deUint32					commandBufferCount;
105			&m_cmdBuffer.get(),					// const VkCommandBuffer*	pCommandBuffers;
106			0,									// deUint32					signalSemaphoreCount;
107			DE_NULL								// const VkSemaphore*		pSignalSemaphores;
108		};
109		m_vk.queueSubmit(queue, 1, &submitInfo, DE_NULL);
110
111		// validation
112		{
113			VK_CHECK(m_vk.queueWaitIdle(queue));
114
115			tcu::Texture2D referenceFrame = buildReferenceFrame();
116
117			const vk::VkOffset3D zeroOffset = { 0, 0, 0 };
118			const tcu::ConstPixelBufferAccess renderedFrame = m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(),
119				vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT);
120
121			if (!tcu::fuzzyCompare(log, "Result", "Image comparison result",
122				referenceFrame.getLevel(0), renderedFrame, 0.05f,
123				tcu::COMPARE_LOG_RESULT))
124			{
125				return tcu::TestStatus(QP_TEST_RESULT_FAIL, "Image verification failed");
126			}
127
128			return tcu::TestStatus(QP_TEST_RESULT_PASS, "Image verification passed");
129		}
130	}
131};
132
133class ViewportParamTestInstane : public ViewportStateBaseCase
134{
135public:
136	ViewportParamTestInstane (Context& context, ShaderMap shaders)
137		: ViewportStateBaseCase (context, shaders[glu::SHADERTYPE_VERTEX], shaders[glu::SHADERTYPE_FRAGMENT])
138	{
139		ViewportStateBaseCase::initialize();
140	}
141
142	virtual void setDynamicStates(void)
143	{
144		const vk::VkViewport viewport	= { 0.0f, 0.0f, (float)WIDTH * 2, (float)HEIGHT * 2, 0.0f, 0.0f };
145		const vk::VkRect2D scissor		= { { 0, 0 }, { WIDTH, HEIGHT } };
146
147		setDynamicViewportState(1, &viewport, &scissor);
148		setDynamicRasterizationState();
149		setDynamicBlendState();
150		setDynamicDepthStencilState();
151	}
152
153	virtual tcu::Texture2D buildReferenceFrame (void)
154	{
155		tcu::Texture2D referenceFrame(vk::mapVkFormat(m_colorAttachmentFormat), (int)(0.5 + WIDTH), (int)(0.5 + HEIGHT));
156		referenceFrame.allocLevel(0);
157
158		const deInt32 frameWidth	= referenceFrame.getWidth();
159		const deInt32 frameHeight	= referenceFrame.getHeight();
160
161		tcu::clear(referenceFrame.getLevel(0), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
162
163		for (int y = 0; y < frameHeight; y++)
164		{
165			const float yCoord = (float)(y / (0.5*frameHeight)) - 1.0f;
166
167			for (int x = 0; x < frameWidth; x++)
168			{
169				const float xCoord = (float)(x / (0.5*frameWidth)) - 1.0f;
170
171				if (xCoord >= 0.0f && xCoord <= 1.0f && yCoord >= 0.0f && yCoord <= 1.0f)
172					referenceFrame.getLevel(0).setPixel(tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f), x, y);
173			}
174		}
175
176		return referenceFrame;
177	}
178};
179
180class ScissorParamTestInstance : public ViewportStateBaseCase
181{
182public:
183	ScissorParamTestInstance (Context& context, ShaderMap shaders)
184		: ViewportStateBaseCase (context, shaders[glu::SHADERTYPE_VERTEX], shaders[glu::SHADERTYPE_FRAGMENT])
185	{
186		ViewportStateBaseCase::initialize();
187	}
188
189	virtual void setDynamicStates (void)
190	{
191		const vk::VkViewport viewport	= { 0.0f, 0.0f, (float)WIDTH, (float)HEIGHT, 0.0f, 0.0f };
192		const vk::VkRect2D scissor		= { { 0, 0 }, { WIDTH / 2, HEIGHT / 2 } };
193
194		setDynamicViewportState(1, &viewport, &scissor);
195		setDynamicRasterizationState();
196		setDynamicBlendState();
197		setDynamicDepthStencilState();
198	}
199
200	virtual tcu::Texture2D buildReferenceFrame (void)
201	{
202		tcu::Texture2D referenceFrame(vk::mapVkFormat(m_colorAttachmentFormat), (int)(0.5 + WIDTH), (int)(0.5 + HEIGHT));
203		referenceFrame.allocLevel(0);
204
205		const deInt32 frameWidth	= referenceFrame.getWidth();
206		const deInt32 frameHeight	= referenceFrame.getHeight();
207
208		tcu::clear(referenceFrame.getLevel(0), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
209
210		for (int y = 0; y < frameHeight; y++)
211		{
212			const float yCoord = (float)(y / (0.5*frameHeight)) - 1.0f;
213
214			for (int x = 0; x < frameWidth; x++)
215			{
216				const float xCoord = (float)(x / (0.5*frameWidth)) - 1.0f;
217
218				if (xCoord >= -0.5f && xCoord <= 0.0f && yCoord >= -0.5f && yCoord <= 0.0f)
219					referenceFrame.getLevel(0).setPixel(tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f), x, y);
220			}
221		}
222
223		return referenceFrame;
224	}
225};
226
227class ViewportArrayTestInstance : public DynamicStateBaseClass
228{
229protected:
230	std::string m_geometryShaderName;
231
232public:
233
234	ViewportArrayTestInstance (Context& context, ShaderMap shaders)
235		: DynamicStateBaseClass	(context, shaders[glu::SHADERTYPE_VERTEX], shaders[glu::SHADERTYPE_FRAGMENT])
236		, m_geometryShaderName	(shaders[glu::SHADERTYPE_GEOMETRY])
237	{
238		// Check geometry shader support
239		{
240			const vk::VkPhysicalDeviceFeatures& deviceFeatures = m_context.getDeviceFeatures();
241
242			if (!deviceFeatures.multiViewport)
243				throw tcu::NotSupportedError("Multi-viewport is not supported");
244
245			if (!deviceFeatures.geometryShader)
246				throw tcu::NotSupportedError("Geometry shaders are not supported");
247		}
248
249		for (int i = 0; i < 4; i++)
250		{
251			m_data.push_back(PositionColorVertex(tcu::Vec4(-1.0f, 1.0f, (float)i / 3.0f, 1.0f), tcu::RGBA::green().toVec()));
252			m_data.push_back(PositionColorVertex(tcu::Vec4(1.0f, 1.0f, (float)i / 3.0f, 1.0f), tcu::RGBA::green().toVec()));
253			m_data.push_back(PositionColorVertex(tcu::Vec4(-1.0f, -1.0f, (float)i / 3.0f, 1.0f), tcu::RGBA::green().toVec()));
254			m_data.push_back(PositionColorVertex(tcu::Vec4(1.0f, -1.0f, (float)i / 3.0f, 1.0f), tcu::RGBA::green().toVec()));
255		}
256
257		DynamicStateBaseClass::initialize();
258	}
259
260	virtual void initPipeline (const vk::VkDevice device)
261	{
262		const vk::Unique<vk::VkShaderModule> vs(createShaderModule(m_vk, device, m_context.getBinaryCollection().get(m_vertexShaderName), 0));
263		const vk::Unique<vk::VkShaderModule> gs(createShaderModule(m_vk, device, m_context.getBinaryCollection().get(m_geometryShaderName), 0));
264		const vk::Unique<vk::VkShaderModule> fs(createShaderModule(m_vk, device, m_context.getBinaryCollection().get(m_fragmentShaderName), 0));
265
266		const PipelineCreateInfo::ColorBlendState::Attachment vkCbAttachmentState;
267
268		PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, 0);
269		pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vs, "main", vk::VK_SHADER_STAGE_VERTEX_BIT));
270		pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*gs, "main", vk::VK_SHADER_STAGE_GEOMETRY_BIT));
271		pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fs, "main", vk::VK_SHADER_STAGE_FRAGMENT_BIT));
272		pipelineCreateInfo.addState(PipelineCreateInfo::VertexInputState(m_vertexInputState));
273		pipelineCreateInfo.addState(PipelineCreateInfo::InputAssemblerState(m_topology));
274		pipelineCreateInfo.addState(PipelineCreateInfo::ColorBlendState(1, &vkCbAttachmentState));
275		pipelineCreateInfo.addState(PipelineCreateInfo::ViewportState(4));
276		pipelineCreateInfo.addState(PipelineCreateInfo::DepthStencilState());
277		pipelineCreateInfo.addState(PipelineCreateInfo::RasterizerState());
278		pipelineCreateInfo.addState(PipelineCreateInfo::MultiSampleState());
279		pipelineCreateInfo.addState(PipelineCreateInfo::DynamicState());
280
281		m_pipeline = vk::createGraphicsPipeline(m_vk, device, DE_NULL, &pipelineCreateInfo);
282	}
283
284	virtual tcu::TestStatus iterate (void)
285	{
286		tcu::TestLog &log = m_context.getTestContext().getLog();
287		const vk::VkQueue queue = m_context.getUniversalQueue();
288
289		beginRenderPass();
290
291		// set states here
292		const float halfWidth		= (float)WIDTH / 2;
293		const float halfHeight		= (float)HEIGHT / 2;
294		const deInt32 quarterWidth	= WIDTH / 4;
295		const deInt32 quarterHeight = HEIGHT / 4;
296
297		const vk::VkViewport viewports[4] =
298		{
299			{ 0.0f, 0.0f, (float)halfWidth, (float)halfHeight, 0.0f, 0.0f },
300			{ halfWidth, 0.0f, (float)halfWidth, (float)halfHeight, 0.0f, 0.0f },
301			{ halfWidth, halfHeight, (float)halfWidth, (float)halfHeight, 0.0f, 0.0f },
302			{ 0.0f, halfHeight, (float)halfWidth, (float)halfHeight, 0.0f, 0.0f }
303		};
304
305		const vk::VkRect2D scissors[4] =
306		{
307			{ { quarterWidth, quarterHeight }, { quarterWidth, quarterHeight } },
308			{ { (deInt32)halfWidth, quarterHeight }, { quarterWidth, quarterHeight } },
309			{ { (deInt32)halfWidth, (deInt32)halfHeight }, { quarterWidth, quarterHeight } },
310			{ { quarterWidth, (deInt32)halfHeight }, { quarterWidth, quarterHeight } },
311		};
312
313		setDynamicViewportState(4, viewports, scissors);
314		setDynamicRasterizationState();
315		setDynamicBlendState();
316		setDynamicDepthStencilState();
317
318		m_vk.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
319
320		const vk::VkDeviceSize vertexBufferOffset = 0;
321		const vk::VkBuffer vertexBuffer = m_vertexBuffer->object();
322		m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
323
324		m_vk.cmdDraw(*m_cmdBuffer, static_cast<deUint32>(m_data.size()), 1, 0, 0);
325
326		m_vk.cmdEndRenderPass(*m_cmdBuffer);
327		m_vk.endCommandBuffer(*m_cmdBuffer);
328
329		vk::VkSubmitInfo submitInfo =
330		{
331			vk::VK_STRUCTURE_TYPE_SUBMIT_INFO,	// VkStructureType			sType;
332			DE_NULL,							// const void*				pNext;
333			0,									// deUint32					waitSemaphoreCount;
334			DE_NULL,							// const VkSemaphore*		pWaitSemaphores;
335			(const vk::VkPipelineStageFlags*)DE_NULL,
336			1,									// deUint32					commandBufferCount;
337			&m_cmdBuffer.get(),					// const VkCommandBuffer*	pCommandBuffers;
338			0,									// deUint32					signalSemaphoreCount;
339			DE_NULL								// const VkSemaphore*		pSignalSemaphores;
340		};
341		m_vk.queueSubmit(queue, 1, &submitInfo, DE_NULL);
342
343		// validation
344		{
345			VK_CHECK(m_vk.queueWaitIdle(queue));
346
347			tcu::Texture2D referenceFrame(vk::mapVkFormat(m_colorAttachmentFormat), (int)(0.5 + WIDTH), (int)(0.5 + HEIGHT));
348			referenceFrame.allocLevel(0);
349
350			const deInt32 frameWidth = referenceFrame.getWidth();
351			const deInt32 frameHeight = referenceFrame.getHeight();
352
353			tcu::clear(referenceFrame.getLevel(0), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
354
355			for (int y = 0; y < frameHeight; y++)
356			{
357				const float yCoord = (float)(y / (0.5*frameHeight)) - 1.0f;
358
359				for (int x = 0; x < frameWidth; x++)
360				{
361					const float xCoord = (float)(x / (0.5*frameWidth)) - 1.0f;
362
363					if (xCoord >= -0.5f && xCoord <= 0.5f && yCoord >= -0.5f && yCoord <= 0.5f)
364						referenceFrame.getLevel(0).setPixel(tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f), x, y);
365				}
366			}
367
368			const vk::VkOffset3D zeroOffset = { 0, 0, 0 };
369			const tcu::ConstPixelBufferAccess renderedFrame = m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(),
370				vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT);
371
372			if (!tcu::fuzzyCompare(log, "Result", "Image comparison result",
373				referenceFrame.getLevel(0), renderedFrame, 0.05f,
374				tcu::COMPARE_LOG_RESULT))
375			{
376				return tcu::TestStatus(QP_TEST_RESULT_FAIL, "Image verification failed");
377			}
378
379			return tcu::TestStatus(QP_TEST_RESULT_PASS, "Image verification passed");
380		}
381	}
382};
383
384} //anonymous
385
386DynamicStateVPTests::DynamicStateVPTests (tcu::TestContext& testCtx)
387	: TestCaseGroup (testCtx, "vp_state", "Tests for viewport state")
388{
389	/* Left blank on purpose */
390}
391
392DynamicStateVPTests::~DynamicStateVPTests ()
393{
394}
395
396void DynamicStateVPTests::init (void)
397{
398	ShaderMap shaderPaths;
399	shaderPaths[glu::SHADERTYPE_VERTEX] = "vulkan/dynamic_state/VertexFetch.vert";
400	shaderPaths[glu::SHADERTYPE_FRAGMENT] = "vulkan/dynamic_state/VertexFetch.frag";
401
402	addChild(new InstanceFactory<ViewportParamTestInstane>(m_testCtx, "viewport", "Set viewport which is twice bigger than screen size", shaderPaths));
403	addChild(new InstanceFactory<ScissorParamTestInstance>(m_testCtx, "scissor", "Perform a scissor test on 1/4 bottom-left part of the surface", shaderPaths));
404
405	shaderPaths[glu::SHADERTYPE_GEOMETRY] = "vulkan/dynamic_state/ViewportArray.geom";
406	addChild(new InstanceFactory<ViewportArrayTestInstance>(m_testCtx, "viewport_array", "Multiple viewports and scissors", shaderPaths));
407}
408
409} // DynamicState
410} // vkt
411